summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Misc/NEWS.d/next/C API/2021-02-21-15-30-38.bpo-43278.DMPaWH.rst1
-rw-r--r--Python/getcompiler.c4
2 files changed, 3 insertions, 2 deletions
diff --git a/Misc/NEWS.d/next/C API/2021-02-21-15-30-38.bpo-43278.DMPaWH.rst b/Misc/NEWS.d/next/C API/2021-02-21-15-30-38.bpo-43278.DMPaWH.rst
new file mode 100644
index 0000000..7df9295
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2021-02-21-15-30-38.bpo-43278.DMPaWH.rst
@@ -0,0 +1 @@
+Always put compiler and system information on the first line of the REPL welcome message.
diff --git a/Python/getcompiler.c b/Python/getcompiler.c
index 59c0dbf..a5d2623 100644
--- a/Python/getcompiler.c
+++ b/Python/getcompiler.c
@@ -8,9 +8,9 @@
// Note the __clang__ conditional has to come before the __GNUC__ one because
// clang pretends to be GCC.
#if defined(__clang__)
-#define COMPILER "\n[Clang " __clang_version__ "]"
+#define COMPILER "[Clang " __clang_version__ "]"
#elif defined(__GNUC__)
-#define COMPILER "\n[GCC " __VERSION__ "]"
+#define COMPILER "[GCC " __VERSION__ "]"
// Generic fallbacks.
#elif defined(__cplusplus)
#define COMPILER "[C++]"
hicsSceneDragDropEvent class provides events for - drag and drop in the graphics view framework. + drag and drop in the graphics view framework. \since 4.2 \ingroup multimedia \ingroup graphicsview-api @@ -257,6 +257,37 @@ QGraphicsItem::ItemPositionHasChanged */ +/*! + \class QGraphicsSceneTouchEvent + \brief The QGraphicsSceneTouchEvent class provides touch events in the graphics view framework. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + + When a QGraphicsView receives a QTouchEvent, it translates it to a + QGraphicsSceneTouchEvent. The event is then forwarded to the + QGraphicsScene associated with the view. + + The touchPoints() function returns a list of touch points for the + event. In addition to containing the item, scene, and screen + coordinates, each touch point also contains its starting and + previous coordinates. + + \sa QTouchEvent +*/ + +/*! + \class QGraphicsSceneTouchEvent::TouchPoint + \brief The QGraphicsSceneTouchEvent::TouchPoint class represents a single touch point in a QGraphicsSceneTouchEvent. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + + Each touch point in a QGraphicsSceneTouchEvent has an id() and + state() in addition to current, starting, and previous coordinates + for the touch point in item, scene, and screen coordinates. +*/ + #include "qgraphicssceneevent.h" #ifndef QT_NO_GRAPHICSVIEW @@ -522,7 +553,7 @@ void QGraphicsSceneMouseEvent::setLastPos(const QPointF &pos) } /*! - Returns the last recorded mouse cursor position in scene + Returns the last recorded mouse cursor position in scene coordinates. The last recorded position is the position of the previous mouse event received by the view that created the event. @@ -545,7 +576,7 @@ void QGraphicsSceneMouseEvent::setLastScenePos(const QPointF &pos) } /*! - Returns the last recorded mouse cursor position in screen + Returns the last recorded mouse cursor position in screen coordinates. The last recorded position is the position of the previous mouse event received by the view that created the event. @@ -1275,7 +1306,7 @@ QGraphicsSceneDragDropEvent::~QGraphicsSceneDragDropEvent() /*! Returns the mouse position of the event relative to the view that sent the event. - + \sa QGraphicsView, screenPos(), scenePos() */ QPointF QGraphicsSceneDragDropEvent::pos() const @@ -1373,7 +1404,7 @@ void QGraphicsSceneDragDropEvent::setButtons(Qt::MouseButtons buttons) /*! Returns the keyboard modifiers that were pressed when the drag - and drop event was created. + and drop event was created. \sa Qt::KeyboardModifiers */ @@ -1428,7 +1459,7 @@ void QGraphicsSceneDragDropEvent::setPossibleActions(Qt::DropActions actions) The action must be one of the possible actions as defined by \c possibleActions(). - \sa Qt::DropAction, possibleActions() + \sa Qt::DropAction, possibleActions() */ Qt::DropAction QGraphicsSceneDragDropEvent::proposedAction() const @@ -1673,6 +1704,291 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) d->newPos = pos; } +class QGraphicsSceneTouchEventPrivate : public QGraphicsSceneEventPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneTouchEvent) +public: + inline QGraphicsSceneTouchEventPrivate() + : modifiers(Qt::NoModifier) + { } + + QList touchPoints; + Qt::KeyboardModifiers modifiers; +}; + +/*! + \internal + + Constructs a generic QGraphicsSceneTouchEvent of type \a type. +*/ +QGraphicsSceneTouchEvent::QGraphicsSceneTouchEvent(Type type) + : QGraphicsSceneEvent(*new QGraphicsSceneTouchEventPrivate, type) +{ } + +/*! + Destroys the QGraphicsSceneTouchEvent. +*/ +QGraphicsSceneTouchEvent::~QGraphicsSceneTouchEvent() +{ + Q_D(QGraphicsSceneTouchEvent); + qDeleteAll(d->touchPoints); +} + +/*! + Returns the list of touch points for this event. + + \sa QGraphicsSceneTouchEvent::TouchPoint +*/ +const QList &QGraphicsSceneTouchEvent::touchPoints() const +{ + Q_D(const QGraphicsSceneTouchEvent); + return d->touchPoints; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::setTouchPoints(const QList &touchPoints) +{ + Q_D(QGraphicsSceneTouchEvent); + d->touchPoints = touchPoints; +} + +/*! + Returns the keyboard modifiers in use at the time the event was + sent. +*/ +Qt::KeyboardModifiers QGraphicsSceneTouchEvent::modifiers() const +{ + Q_D(const QGraphicsSceneTouchEvent); + return d->modifiers; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QGraphicsSceneTouchEvent); + d->modifiers = modifiers; +} + +class QGraphicsSceneTouchEventTouchPointPrivate +{ +public: + inline QGraphicsSceneTouchEventTouchPointPrivate() + : id(-1), state(Qt::TouchPointReleased), pressure(qreal(0.)) + { } + + int id; + Qt::TouchPointState state; + QPointF pos, startPos, lastPos; + QPointF scenePos, startScenePos, lastScenePos; + QPointF screenPos, startScreenPos, lastScreenPos; + qreal pressure; +}; + +/*! \internal + + Constructs a new touch point for use in a QGraphicsSceneTouchEvent. +*/ +QGraphicsSceneTouchEvent::TouchPoint::TouchPoint() + : d(new QGraphicsSceneTouchEventTouchPointPrivate) +{ +} + +/*! \internal + + Destroys the QGraphicsSceneTouchEvent::TouchPoint. +*/ +QGraphicsSceneTouchEvent::TouchPoint::~TouchPoint() +{ + delete d; +} + +/*! + Returns the identifier for this touch point. +*/ +int QGraphicsSceneTouchEvent::TouchPoint::id() const +{ + return d->id; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setId(int id) +{ + d->id = id; +} + +/*! + Returns the state of this touch point at the time the + QGraphicsSceneTouchEvent occurred. +*/ +Qt::TouchPointState QGraphicsSceneTouchEvent::TouchPoint::state() const +{ + return d->state; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setState(Qt::TouchPointState state) +{ + d->state = state; +} + +/*! + Returns the current position of this touch point in item coordinates. + + \sa scenePos(), screenPos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::pos() const +{ + return d->pos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setPos(const QPointF &pos) +{ + d->pos = pos; +} + +/*! + Returns the starting position of this touch point in item coordinates. + + \sa startScenePos(), startScreenPos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::startPos() const +{ + return d->startPos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setStartPos(const QPointF &startPos) +{ + d->startPos = startPos; +} + +/*! + Returns the previous position of this touch point in item coordinates. + + \sa lastScenePos(), lastScreenPos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::lastPos() const +{ + return d->lastPos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos) +{ + d->lastPos = lastPos; +} + +/*! + Returns the current position of this touch point in scene coordinates. + + \sa pos(), screenPos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::scenePos() const +{ + return d->scenePos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos) +{ + d->scenePos = scenePos; +} + +/*! + Returns the starting position of this touch point in scene coordinates. + + \sa startPos(), startScreenPos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::startScenePos() const +{ + return d->startScenePos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setStartScenePos(const QPointF &startScenePos) +{ + d->startScenePos = startScenePos; +} + +/*! + Returns the previous position of this touch point in scene coordinates. + + \sa lastPos(), lastScreenPos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::lastScenePos() const +{ + return d->lastScenePos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastScenePos) +{ + d->lastScenePos = lastScenePos; +} + +/*! + Returns the current position of this touch point in screen coordinates. + + \sa pos(), scenePos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::screenPos() const +{ + return d->screenPos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) +{ + d->screenPos = screenPos; +} + +/*! + Returns the starting position of this touch point in screen coordinates. + + \sa startPos(), startScenePos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::startScreenPos() const +{ + return d->startScreenPos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos) +{ + d->startScreenPos = startScreenPos; +} + +/*! + Returns the previous position of this touch point in screen coordinates. + + \sa lastPos(), lastScenePos() +*/ +QPointF QGraphicsSceneTouchEvent::TouchPoint::lastScreenPos() const +{ + return d->lastScreenPos; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos) +{ + d->lastScreenPos = lastScreenPos; +} + +/*! + Returns the pressure of this touch point. +*/ +qreal QGraphicsSceneTouchEvent::TouchPoint::pressure() const +{ + return d->pressure; +} + +/*! \internal */ +void QGraphicsSceneTouchEvent::TouchPoint::setPressure(qreal pressure) +{ + d->pressure = pressure; +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index be50e96..8ae3a99 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -302,6 +302,70 @@ public: void setNewPos(const QPointF &pos); }; +class QGraphicsSceneTouchEventPrivate; +class QGraphicsSceneTouchEventTouchPointPrivate; +class Q_GUI_EXPORT QGraphicsSceneTouchEvent : public QGraphicsSceneEvent +{ +public: + QGraphicsSceneTouchEvent(Type type = None); + ~QGraphicsSceneTouchEvent(); + + class Q_GUI_EXPORT TouchPoint + { + public: + TouchPoint(); + ~TouchPoint(); + + int id() const; + void setId(int id); + + Qt::TouchPointState state() const; + void setState(Qt::TouchPointState state); + + QPointF pos() const; + void setPos(const QPointF &pos); + + QPointF startPos() const; + void setStartPos(const QPointF &startPos); + + QPointF lastPos() const; + void setLastPos(const QPointF &lastPos); + + QPointF scenePos() const; + void setScenePos(const QPointF &scenePos); + + QPointF startScenePos() const; + void setStartScenePos(const QPointF &startScenePos); + + QPointF lastScenePos() const; + void setLastScenePos(const QPointF &lastScenePos); + + QPointF screenPos() const; + void setScreenPos(const QPointF &screenPos); + + QPointF startScreenPos() const; + void setStartScreenPos(const QPointF &startScreenPos); + + QPointF lastScreenPos() const; + void setLastScreenPos(const QPointF &lastScreenPos); + + qreal pressure() const; // 0.0 -> 1.0 + void setPressure(qreal pressure); + + private: + QGraphicsSceneTouchEventTouchPointPrivate *d; + }; + + const QList &touchPoints() const; + void setTouchPoints(const QList &touchPoints); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + +private: + Q_DECLARE_PRIVATE(QGraphicsSceneTouchEvent); +}; + #endif // QT_NO_GRAPHICSVIEW QT_END_NAMESPACE diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 3f1df5e..ff69c24 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3506,4 +3506,168 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) #endif +/*! \class QTouchEvent + \brief The QTouchEvent class contains parameters that describe a touch event +. + \since 4.6 + \ingroup events + + Touch events occur when pressing, releasing, or moving one or more + touch points on a touch device (such as a touch-screen or + track-pad), and if the widget has the Qt::WA_AcceptTouchEvents + attribute. + + Like QMouseEvent, Qt automatically grabs the touch device on the + first press inside a widget; the widget will receive all touch + events until the last touch point is released. + + A touch event contains a special accept flag that indicates + whether the receiver wants the event. By default, the event is + ignored. You should call accept() if the touch event is handled by + your widget. A touch event is propagated up the parent widget + chain until a widget accepts it with accept(), or an event filter + consumes it. If the touch event is neither accepted nor consumed, + then mouse events are simulated from the state of the first touch + point. + + All touch events are of type QEvent::Touch. The touchPoints() + function returns a list of all touch points contained in the + event. Information about each touch point can be retreived using + the QTouchEvent::TouchPoint class. + + The Qt::TouchPointState enum describes the different states that a + touch point may have. + + QTouchEvent::TouchPoint Qt::TouchPointState Qt::WA_AcceptTouchEvents +*/ + +/*! \enum Qt::TouchPointState + \since 4.6 + + This enum represents the state of a touch point at the time the + QTouchEvent occurred. + + \value TouchPointPressed The touch point is now pressed. + \value TouchPointMoved The touch point moved. + \value TouchPointStationary The touch point did not move. + \value TouchPointReleased The touch point was released. +*/ + +/*! \class QTouchEvent::TouchPoint + \brief The QTouchEvent::TouchPoint class provide information about a touch point in a QTouchEvent. + \since 4.6 +*/ + +/*! + Constructs a QTouchEvent with the given \a type and \a + touchPoints. The \a modifiers are the current keyboard modifiers + at the time of the event. +*/ +QTouchEvent::QTouchEvent(QEvent::Type type, + Qt::KeyboardModifiers modifiers, + const QList &touchPoints) + : QInputEvent(type, modifiers), _touchPoints(touchPoints) +{ } + +/*! \fn const QList &QTouchEvent::touchPoints() const + + Returns the list of touch points contained in the touch event. +*/ + +/*! \internal + + Constructs a QTouchEvent::TouchPoint for use in a QTouchEvent. +*/ +QTouchEvent::TouchPoint::TouchPoint(int id) + : d(new QTouchEventTouchPointPrivate(id)) +{ } + +/*! \internal + + Destroys the QTouchEvent::TouchPoint. +*/ +QTouchEvent::TouchPoint::~TouchPoint() +{ + delete d; +} + +/*! + Returns the id number of this touch point. Id numbers are + sequential, starting at zero, meaning the first touch point has id + 0, the second has id 1, and so on... +*/ +int QTouchEvent::TouchPoint::id() const +{ + return d->id; +} + +/*! + Returns the current state of this touch point. +*/ +Qt::TouchPointState QTouchEvent::TouchPoint::state() const +{ + return d->state; +} + +/*! + Returns the position of this touch point, relative to the widget + that received the event. +*/ +const QPointF &QTouchEvent::TouchPoint::pos() const +{ + return d->pos; +} + +/*! + Returns the starting position of this touch point, relative to the + widget that received the event. +*/ +const QPointF &QTouchEvent::TouchPoint::startPos() const +{ + return d->startPos; +} + +/*! + Returns the position of this touch point from the previous touch + event, relative to the widget that received the event. +*/ +const QPointF &QTouchEvent::TouchPoint::lastPos() const +{ + return d->lastPos; +} + +/*! + Returns the global position of this touch point. +*/ +const QPointF &QTouchEvent::TouchPoint::globalPos() const +{ + return d->globalPos; +} + +/*! + Returns the global starting position of this touch point. +*/ +const QPointF &QTouchEvent::TouchPoint::startGlobalPos() const +{ + return d->startGlobalPos; +} + +/*! + Returns the global position of this touch point from the previous + touch event. +*/ +const QPointF &QTouchEvent::TouchPoint::lastGlobalPos() const +{ + return d->lastGlobalPos; +} + +/*! + Returns the pressure of this touch point. The return value is in + the range 0.0 to 1.0. +*/ +qreal QTouchEvent::TouchPoint::pressure() const +{ + return d->pressure; +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 449730d..3cb712d 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -719,6 +719,47 @@ inline bool operator==(QKeyEvent *e, QKeySequence::StandardKey key){return (e ? inline bool operator==(QKeySequence::StandardKey key, QKeyEvent *e){return (e ? e->matches(key) : false);} #endif // QT_NO_SHORTCUT +class QTouchEventTouchPointPrivate; +class Q_GUI_EXPORT QTouchEvent : public QInputEvent +{ +public: + class Q_GUI_EXPORT TouchPoint + { + public: + TouchPoint(int id = -1); + ~TouchPoint(); + + int id() const; + + Qt::TouchPointState state() const; + + const QPointF &pos() const; + const QPointF &startPos() const; + const QPointF &lastPos() const; + + const QPointF &globalPos() const; + const QPointF &startGlobalPos() const; + const QPointF &lastGlobalPos() const; + + qreal pressure() const; // 0.0 -> 1.0 + + private: + QTouchEventTouchPointPrivate *d; + + friend class QApplicationPrivate; + }; + + QTouchEvent(QEvent::Type type, + Qt::KeyboardModifiers modifiers, + const QList &touchPoints); + + inline const QList &touchPoints() const { return _touchPoints; + } + +protected: + QList _touchPoints; +}; + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index cc94aad..5e24eeb 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -89,6 +89,22 @@ protected: friend class QMouseEvent; }; +class QTouchEventTouchPointPrivate +{ +public: + inline QTouchEventTouchPointPrivate(int id) + : id(id), + state(Qt::TouchPointReleased), + pressure(qreal(-1.)) + { } + + int id; + Qt::TouchPointState state; + QPointF pos, startPos, lastPos; + QPointF globalPos, startGlobalPos, lastGlobalPos; + qreal pressure; +}; + QT_END_NAMESPACE #endif // QEVENT_P_H -- cgit v0.12 From 075a8a30560e20fee94d47aa447daa15f4cb138f Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 24 Mar 2009 12:01:44 +0100 Subject: add QGraphicsItem::acceptTouchEvents() and setAcceptTouchEvents() like Qt::WA_AcceptTouchEvents, QGraphicsItems don't receive touch events unless touch events are explicitly enabled. --- src/gui/graphicsview/qgraphicsitem.cpp | 25 +++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsitem.h | 2 ++ src/gui/graphicsview/qgraphicsitem_p.h | 2 ++ 3 files changed, 29 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index fc81f39..0b51c8c 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -2063,6 +2063,31 @@ void QGraphicsItem::setAcceptsHoverEvents(bool enabled) d_ptr->acceptsHover = quint32(enabled); } +/*! \since 4.6 + + Returns true if an item accepts touch events + (QGraphicsSceneTouchEvent); otherwise, returns false. By default, + items do not accept touch events. + + \sa setAcceptTouchEvents() +*/ +bool QGraphicsItem::acceptTouchEvents() const +{ + return d_ptr->acceptTouchEvents; +} + +/*! + \since 4.6 + + If \a enabled is true, this item will accept touch events; + otherwise, it will ignore them. By default, items do not accept + touch events. +*/ +void QGraphicsItem::setAcceptTouchEvents(bool enabled) +{ + d_ptr->acceptTouchEvents = quint32(enabled); +} + /*! Returns true if this item handles child events (i.e., all events intended for any of its children are instead sent to this item); diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index b98882d..ec3373a 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -203,6 +203,8 @@ public: void setAcceptsHoverEvents(bool enabled); // obsolete bool acceptHoverEvents() const; void setAcceptHoverEvents(bool enabled); + bool acceptTouchEvents() const; + void setAcceptTouchEvents(bool enabled); bool handlesChildEvents() const; void setHandlesChildEvents(bool enabled); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index af0b077..228368c 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -138,6 +138,7 @@ public: dirty(0), dirtyChildren(0), localCollisionHack(0), + acceptTouchEvents(0), globalStackingOrder(-1), sceneTransformIndex(-1), q_ptr(0) @@ -272,6 +273,7 @@ public: quint32 dirty : 1; quint32 dirtyChildren : 1; quint32 localCollisionHack : 1; + quint32 acceptTouchEvents : 1; // Optional stacking order int globalStackingOrder; -- cgit v0.12 From b9b9fc18d8ea5e9ddc2508ac6e9ce24960a8c24a Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 24 Mar 2009 12:03:21 +0100 Subject: implement support for touch events in QGraphicsScene QGraphicsView will convert QTouchEvents to QGraphicsSceneTouchEvents and send them to the scene. The scene will then send the touch events to the appropriate item. Like mouse event support, the item that accepts the touch begin is the item that will get all subsequent touch events. if no item accepts the touch begin event, then no touch events are sent to any item (and mouse events are sent instead). --- src/gui/graphicsview/qgraphicsscene.cpp | 166 ++++++++++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsscene_p.h | 6 ++ src/gui/graphicsview/qgraphicsview.cpp | 106 ++++++++++++++++++++ src/gui/graphicsview/qgraphicsview_p.h | 5 + 4 files changed, 283 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 553967c..b47ed5e 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3609,6 +3609,9 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: + case QEvent::GraphicsSceneTouchBegin: + case QEvent::GraphicsSceneTouchUpdate: + case QEvent::GraphicsSceneTouchEnd: // Reset the under-mouse list to ensure that this event gets fresh // item-under-mouse data. Be careful about this list; if people delete // items from inside event handlers, this list can quickly end up @@ -3760,6 +3763,15 @@ bool QGraphicsScene::event(QEvent *event) // geometries that do not have an explicit style set. update(); break; + case QEvent::GraphicsSceneTouchBegin: + d->touchBeginEvent(static_cast(event)); + break; + case QEvent::GraphicsSceneTouchUpdate: + d->touchUpdateEvent(static_cast(event)); + break; + case QEvent::GraphicsSceneTouchEnd: + d->touchEndEvent(static_cast(event)); + break; case QEvent::Timer: if (d->indexTimerId && static_cast(event)->timerId() == d->indexTimerId) { if (d->restartIndexTimer) { @@ -5370,6 +5382,160 @@ void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) } } +// ### FIXME: the code for touch event support is mosly copied from +// ### QGraphicsScenePrivate::mousePressEventHandler() and friends, need to +// ### refactor to reduce code duplication + +void QGraphicsScenePrivate::sendTouchEvent(QGraphicsSceneTouchEvent *touchEvent) +{ + if (touchEvent->type() == QEvent::GraphicsSceneTouchEnd && lastMouseGrabberItemHasImplicitMouseGrab) { + clearMouseGrabber(); + return; + } + + QGraphicsItem *item = mouseGrabberItems.last(); + QList touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = touchPoints.at(i); + touchPoint->setPos(item->d_ptr->genericMapFromScene(touchPoint->scenePos(), touchEvent->widget())); + touchPoint->setStartPos(item->d_ptr->genericMapFromScene(touchPoint->startScenePos(), touchEvent->widget())); + touchPoint->setLastPos(item->d_ptr->genericMapFromScene(touchPoint->lastScenePos(), touchEvent->widget())); + } + sendEvent(item, touchEvent); +} + +void QGraphicsScenePrivate::touchBeginEvent(QGraphicsSceneTouchEvent *touchEvent) +{ + Q_Q(QGraphicsScene); + + // Ignore by default, unless we find a mouse grabber that accepts it. + touchEvent->ignore(); + + // Deliver to any existing mouse grabber. + if (!mouseGrabberItems.isEmpty()) { + // The event is ignored by default, but we disregard the event's + // accepted state after delivery; the mouse is grabbed, after all. + sendTouchEvent(touchEvent); + return; + } + + // Start by determining the number of items at the current position. + // Reuse value from earlier calculations if possible. + if (cachedItemsUnderMouse.isEmpty()) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = touchEvent->touchPoints().first(); + // ### FIXME: should the itemsAtPosition() function support sub-pixel screenPos? + cachedItemsUnderMouse = itemsAtPosition(touchPoint->screenPos().toPoint(), + touchPoint->scenePos(), + touchEvent->widget()); + } + + // Update window activation. + QGraphicsWidget *newActiveWindow = windowForItem(cachedItemsUnderMouse.value(0)); + if (newActiveWindow != activeWindow) + q->setActiveWindow(newActiveWindow); + + // Set focus on the topmost enabled item that can take focus. + bool setFocus = false; + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { + setFocus = true; + if (item != q->focusItem()) + q->setFocusItem(item, Qt::MouseFocusReason); + break; + } + } + } + + // If nobody could take focus, clear it. + if (!stickyFocus && !setFocus) + q->setFocusItem(0, Qt::MouseFocusReason); + + // Find a mouse grabber by sending touch events to all mouse grabber + // candidates one at a time, until the event is accepted. It's accepted by + // default, so the receiver has to explicitly ignore it for it to pass + // through. + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (!item->acceptTouchEvents()) { + // Skip items that don't accept the touch events + continue; + } + + grabMouse(item, /* implicit = */ true); + touchEvent->accept(); + + // check if the item we are sending to are disabled (before we send the event) + bool disabled = !item->isEnabled(); + bool isWindow = item->isWindow(); + sendTouchEvent(touchEvent); + + bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.last() != item; + if (disabled) { + ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); + break; + } + if (touchEvent->isAccepted()) { + lastMouseGrabberItem = item; + return; + } + ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); + + // Don't propagate through windows. + if (isWindow) + break; + } + + // Is the event still ignored? Then the mouse press goes to the scene. + // Reset the mouse grabber, clear the selection, clear focus, and leave + // the event ignored so that it can propagate through the originating + // view. + if (!touchEvent->isAccepted()) { + clearMouseGrabber(); + + QGraphicsView *view = touchEvent->widget() ? qobject_cast(touchEvent->widget()->parentWidget()) : 0; + bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag; + if (!dontClearSelection) { + // Clear the selection if the originating view isn't in scroll + // hand drag mode. The view will clear the selection if no drag + // happened. + q->clearSelection(); + } + } +} + +void QGraphicsScenePrivate::touchUpdateEvent(QGraphicsSceneTouchEvent *touchEvent) +{ + if (mouseGrabberItems.isEmpty()) { + touchEvent->ignore(); + return; + } + + // Forward the event to the mouse grabber + sendTouchEvent(touchEvent); + touchEvent->accept(); +} + +void QGraphicsScenePrivate::touchEndEvent(QGraphicsSceneTouchEvent *touchEvent) +{ + if (mouseGrabberItems.isEmpty()) { + touchEvent->ignore(); + return; + } + + // Forward the event to the mouse grabber + sendTouchEvent(touchEvent); + touchEvent->accept(); + + // Reset the mouse grabber + if (!mouseGrabberItems.isEmpty()) { + lastMouseGrabberItem = mouseGrabberItems.last(); + if (lastMouseGrabberItemHasImplicitMouseGrab) + mouseGrabberItems.last()->ungrabMouse(); + } else { + lastMouseGrabberItem = 0; + } +} + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 7f441da..d5e539b 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -73,6 +73,7 @@ QT_BEGIN_NAMESPACE class QGraphicsView; class QGraphicsWidget; +class QGraphicsSceneTouchEvent; class QGraphicsScenePrivate : public QObjectPrivate { @@ -259,6 +260,11 @@ public: mutable QVector sceneTransformCache; mutable QBitArray validTransforms; mutable QVector freeSceneTransformSlots; + + void sendTouchEvent(QGraphicsSceneTouchEvent *touchEvent); + void touchBeginEvent(QGraphicsSceneTouchEvent *touchEvent); + void touchUpdateEvent(QGraphicsSceneTouchEvent *touchEvent); + void touchEndEvent(QGraphicsSceneTouchEvent *touchEvent); }; QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index a661441..f95d328 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -2926,6 +2926,28 @@ bool QGraphicsView::viewportEvent(QEvent *event) d->scene->d_func()->updateAll = false; } break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + if (!isEnabled()) + return false; + QTouchEvent *touchEvent = static_cast(event); + switch (touchEvent->type()) { + case QEvent::TouchBegin: + d->touchBeginEvent(touchEvent); + break; + case QEvent::TouchUpdate: + d->touchUpdateEvent(touchEvent); + break; + case QEvent::TouchEnd: + d->touchEndEvent(touchEvent); + break; + default: + break; + } + return true; + } default: break; } @@ -3864,6 +3886,90 @@ void QGraphicsView::resetTransform() setTransform(QTransform()); } +static void qt_convertTouchEventToGraphicsSceneTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *originalEvent, QGraphicsSceneTouchEvent *touchEvent) +{ + QList originalTouchPoints = originalEvent->touchPoints(); + QList touchPoints; + for (int i = 0; i < originalTouchPoints.count(); ++i) { + QTouchEvent::TouchPoint *originalTouchPoint = originalTouchPoints.at(i); + + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = new QGraphicsSceneTouchEvent::TouchPoint(); + touchPoint->setId(originalTouchPoint->id()); + touchPoint->setState(originalTouchPoint->state()); + // the scene will set the pos before delivering to an item + touchPoint->setScenePos(d->mapToScene(originalTouchPoint->pos())); + touchPoint->setScreenPos(originalTouchPoint->globalPos()); + // the scene will set the startPos before delivering to an item + touchPoint->setStartScenePos(d->mapToScene(originalTouchPoint->startPos())); + touchPoint->setStartScreenPos(originalTouchPoint->startGlobalPos()); + // the scene will set the lastPos before delivering to an item + touchPoint->setLastScenePos(d->mapToScene(originalTouchPoint->lastPos())); + touchPoint->setLastScreenPos(originalTouchPoint->lastGlobalPos()); + touchPoint->setPressure(originalTouchPoint->pressure()); + + touchPoints.append(touchPoint); + } + + touchEvent->setTouchPoints(touchPoints); + touchEvent->setModifiers(originalEvent->modifiers()); +} + +QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const +{ + QPointF p = point; + p.rx() += horizontalScroll(); + p.ry() += verticalScroll(); + return identityMatrix ? p : matrix.inverted().map(p); +} + +void QGraphicsViewPrivate::touchBeginEvent(QTouchEvent *event) +{ + Q_Q(QGraphicsView); + + if (!scene || !sceneInteractionAllowed) + return; + + // Convert and deliver the touch event to the scene. + QGraphicsSceneTouchEvent touchEvent(QEvent::GraphicsSceneTouchBegin); + touchEvent.setWidget(q->viewport()); + qt_convertTouchEventToGraphicsSceneTouchEvent(this, event, &touchEvent); + touchEvent.setAccepted(false); + QApplication::sendEvent(scene, &touchEvent); + event->setAccepted(touchEvent.isAccepted()); +} + +void QGraphicsViewPrivate::touchUpdateEvent(QTouchEvent *event) +{ + Q_Q(QGraphicsView); + + if (!scene || !sceneInteractionAllowed) + return; + + // Convert and deliver the touch event to the scene. + QGraphicsSceneTouchEvent touchEvent(QEvent::GraphicsSceneTouchUpdate); + touchEvent.setWidget(q->viewport()); + qt_convertTouchEventToGraphicsSceneTouchEvent(this, event, &touchEvent); + touchEvent.setAccepted(false); + QApplication::sendEvent(scene, &touchEvent); + event->setAccepted(touchEvent.isAccepted()); +} + +void QGraphicsViewPrivate::touchEndEvent(QTouchEvent *event) +{ + Q_Q(QGraphicsView); + + if (!scene || !sceneInteractionAllowed) + return; + + // Convert and deliver the touch event to the scene. + QGraphicsSceneTouchEvent touchEvent(QEvent::GraphicsSceneTouchEnd); + touchEvent.setWidget(q->viewport()); + qt_convertTouchEventToGraphicsSceneTouchEvent(this, event, &touchEvent); + touchEvent.setAccepted(false); + QApplication::sendEvent(scene, &touchEvent); + event->setAccepted(touchEvent.isAccepted()); +} + QT_END_NAMESPACE #include "moc_qgraphicsview.cpp" diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index 2109673..e9479f3 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -182,6 +182,11 @@ public: const QTransform &worldTransform, bool allItems, const QRegion &exposedRegion) const; + + QPointF mapToScene(const QPointF &point) const; + void touchBeginEvent(QTouchEvent *event); + void touchUpdateEvent(QTouchEvent *event); + void touchEndEvent(QTouchEvent *event); }; QT_END_NAMESPACE -- cgit v0.12 From 96741cb1aba9feaf0099910884bee5241e52f794 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 24 Mar 2009 14:59:07 +0100 Subject: add a multitouch pinchzoom example, based on the collidingmice example --- examples/multitouch/multitouch.pro | 2 + examples/multitouch/pinchzoom/graphicsview.cpp | 78 +++++++++ examples/multitouch/pinchzoom/graphicsview.h | 53 +++++++ examples/multitouch/pinchzoom/images/cheese.jpg | Bin 0 -> 3029 bytes examples/multitouch/pinchzoom/main.cpp | 88 +++++++++++ examples/multitouch/pinchzoom/mice.qrc | 5 + examples/multitouch/pinchzoom/mouse.cpp | 200 ++++++++++++++++++++++++ examples/multitouch/pinchzoom/mouse.h | 72 +++++++++ examples/multitouch/pinchzoom/pinchzoom.pro | 16 ++ 9 files changed, 514 insertions(+) create mode 100644 examples/multitouch/multitouch.pro create mode 100644 examples/multitouch/pinchzoom/graphicsview.cpp create mode 100644 examples/multitouch/pinchzoom/graphicsview.h create mode 100644 examples/multitouch/pinchzoom/images/cheese.jpg create mode 100644 examples/multitouch/pinchzoom/main.cpp create mode 100644 examples/multitouch/pinchzoom/mice.qrc create mode 100644 examples/multitouch/pinchzoom/mouse.cpp create mode 100644 examples/multitouch/pinchzoom/mouse.h create mode 100644 examples/multitouch/pinchzoom/pinchzoom.pro diff --git a/examples/multitouch/multitouch.pro b/examples/multitouch/multitouch.pro new file mode 100644 index 0000000..7efa412 --- /dev/null +++ b/examples/multitouch/multitouch.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = pinchzoom \ No newline at end of file diff --git a/examples/multitouch/pinchzoom/graphicsview.cpp b/examples/multitouch/pinchzoom/graphicsview.cpp new file mode 100644 index 0000000..b77c926 --- /dev/null +++ b/examples/multitouch/pinchzoom/graphicsview.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicsview.h" + +#include + + +GraphicsView::GraphicsView(QGraphicsScene *scene, QWidget *parent) + : QGraphicsView(scene, parent) +{ + setAttribute(Qt::WA_AcceptTouchEvents); +} + +bool GraphicsView::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + qDebug("touch events"); + + QList touchPoints = static_cast(event)->touchPoints(); + if (touchPoints.count() == 2) { + // determine scale factor + const QTouchEvent::TouchPoint *touchPoint0 = touchPoints.first(); + const QTouchEvent::TouchPoint *touchPoint1 = touchPoints.last(); + const qreal scaleFactor = QLineF(touchPoint0->pos(), touchPoint1->pos()).length() + / QLineF(touchPoint0->startPos(), touchPoint1->startPos()).length(); + setTransform(QTransform().scale(scaleFactor, scaleFactor)); + } + event->accept(); + return true; + } + default: + break; + } + return QGraphicsView::event(event); +} diff --git a/examples/multitouch/pinchzoom/graphicsview.h b/examples/multitouch/pinchzoom/graphicsview.h new file mode 100644 index 0000000..626f23c --- /dev/null +++ b/examples/multitouch/pinchzoom/graphicsview.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once +#include + +class GraphicsView : public QGraphicsView +{ + Q_OBJECT + +public: + GraphicsView(QGraphicsScene *scene = 0, QWidget *parent = 0); + + bool event(QEvent *event); +}; diff --git a/examples/multitouch/pinchzoom/images/cheese.jpg b/examples/multitouch/pinchzoom/images/cheese.jpg new file mode 100644 index 0000000..dea5795 Binary files /dev/null and b/examples/multitouch/pinchzoom/images/cheese.jpg differ diff --git a/examples/multitouch/pinchzoom/main.cpp b/examples/multitouch/pinchzoom/main.cpp new file mode 100644 index 0000000..6b25060 --- /dev/null +++ b/examples/multitouch/pinchzoom/main.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "graphicsview.h" +#include "mouse.h" + +#include + +#include + +static const int MouseCount = 7; + +//! [0] +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); +//! [0] + +//! [1] + QGraphicsScene scene; + scene.setSceneRect(-300, -300, 600, 600); +//! [1] //! [2] + scene.setItemIndexMethod(QGraphicsScene::NoIndex); +//! [2] + +//! [3] + for (int i = 0; i < MouseCount; ++i) { + Mouse *mouse = new Mouse; + mouse->setPos(::sin((i * 6.28) / MouseCount) * 200, + ::cos((i * 6.28) / MouseCount) * 200); + scene.addItem(mouse); + } +//! [3] + +//! [4] + GraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing); + view.setBackgroundBrush(QPixmap(":/images/cheese.jpg")); +//! [4] //! [5] + view.setCacheMode(QGraphicsView::CacheBackground); + view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + view.setDragMode(QGraphicsView::ScrollHandDrag); +//! [5] //! [6] + view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Colliding Mice")); + view.showMaximized(); + + return app.exec(); +} +//! [6] diff --git a/examples/multitouch/pinchzoom/mice.qrc b/examples/multitouch/pinchzoom/mice.qrc new file mode 100644 index 0000000..accdb4d --- /dev/null +++ b/examples/multitouch/pinchzoom/mice.qrc @@ -0,0 +1,5 @@ + + + images/cheese.jpg + + diff --git a/examples/multitouch/pinchzoom/mouse.cpp b/examples/multitouch/pinchzoom/mouse.cpp new file mode 100644 index 0000000..1d9fa89 --- /dev/null +++ b/examples/multitouch/pinchzoom/mouse.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mouse.h" + +#include +#include +#include + +#include + +static const double Pi = 3.14159265358979323846264338327950288419717; +static double TwoPi = 2.0 * Pi; + +static qreal normalizeAngle(qreal angle) +{ + while (angle < 0) + angle += TwoPi; + while (angle > TwoPi) + angle -= TwoPi; + return angle; +} + +//! [0] +Mouse::Mouse() + : angle(0), speed(0), mouseEyeDirection(0), + color(qrand() % 256, qrand() % 256, qrand() % 256) +{ + rotate(qrand() % (360 * 16)); + startTimer(1000 / 33); +} +//! [0] + +//! [1] +QRectF Mouse::boundingRect() const +{ + qreal adjust = 0.5; + return QRectF(-18 - adjust, -22 - adjust, + 36 + adjust, 60 + adjust); +} +//! [1] + +//! [2] +QPainterPath Mouse::shape() const +{ + QPainterPath path; + path.addRect(-10, -20, 20, 40); + return path; +} +//! [2] + +//! [3] +void Mouse::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + // Body + painter->setBrush(color); + painter->drawEllipse(-10, -20, 20, 40); + + // Eyes + painter->setBrush(Qt::white); + painter->drawEllipse(-10, -17, 8, 8); + painter->drawEllipse(2, -17, 8, 8); + + // Nose + painter->setBrush(Qt::black); + painter->drawEllipse(QRectF(-2, -22, 4, 4)); + + // Pupils + painter->drawEllipse(QRectF(-8.0 + mouseEyeDirection, -17, 4, 4)); + painter->drawEllipse(QRectF(4.0 + mouseEyeDirection, -17, 4, 4)); + + // Ears + painter->setBrush(scene()->collidingItems(this).isEmpty() ? Qt::darkYellow : Qt::red); + painter->drawEllipse(-17, -12, 16, 16); + painter->drawEllipse(1, -12, 16, 16); + + // Tail + QPainterPath path(QPointF(0, 20)); + path.cubicTo(-5, 22, -5, 22, 0, 25); + path.cubicTo(5, 27, 5, 32, 0, 30); + path.cubicTo(-5, 32, -5, 42, 0, 35); + painter->setBrush(Qt::NoBrush); + painter->drawPath(path); +} +//! [3] + +//! [4] +void Mouse::timerEvent(QTimerEvent *) +{ +//! [4] + // Don't move too far away +//! [5] + QLineF lineToCenter(QPointF(0, 0), mapFromScene(0, 0)); + if (lineToCenter.length() > 150) { + qreal angleToCenter = ::acos(lineToCenter.dx() / lineToCenter.length()); + if (lineToCenter.dy() < 0) + angleToCenter = TwoPi - angleToCenter; + angleToCenter = normalizeAngle((Pi - angleToCenter) + Pi / 2); + + if (angleToCenter < Pi && angleToCenter > Pi / 4) { + // Rotate left + angle += (angle < -Pi / 2) ? 0.25 : -0.25; + } else if (angleToCenter >= Pi && angleToCenter < (Pi + Pi / 2 + Pi / 4)) { + // Rotate right + angle += (angle < Pi / 2) ? 0.25 : -0.25; + } + } else if (::sin(angle) < 0) { + angle += 0.25; + } else if (::sin(angle) > 0) { + angle -= 0.25; +//! [5] //! [6] + } +//! [6] + + // Try not to crash with any other mice +//! [7] + QList dangerMice = scene()->items(QPolygonF() + << mapToScene(0, 0) + << mapToScene(-30, -50) + << mapToScene(30, -50)); + foreach (QGraphicsItem *item, dangerMice) { + if (item == this) + continue; + + QLineF lineToMouse(QPointF(0, 0), mapFromItem(item, 0, 0)); + qreal angleToMouse = ::acos(lineToMouse.dx() / lineToMouse.length()); + if (lineToMouse.dy() < 0) + angleToMouse = TwoPi - angleToMouse; + angleToMouse = normalizeAngle((Pi - angleToMouse) + Pi / 2); + + if (angleToMouse >= 0 && angleToMouse < Pi / 2) { + // Rotate right + angle += 0.5; + } else if (angleToMouse <= TwoPi && angleToMouse > (TwoPi - Pi / 2)) { + // Rotate left + angle -= 0.5; +//! [7] //! [8] + } +//! [8] //! [9] + } +//! [9] + + // Add some random movement +//! [10] + if (dangerMice.size() > 1 && (qrand() % 10) == 0) { + if (qrand() % 1) + angle += (qrand() % 100) / 500.0; + else + angle -= (qrand() % 100) / 500.0; + } +//! [10] + +//! [11] + speed += (-50 + qrand() % 100) / 100.0; + + qreal dx = ::sin(angle) * 10; + mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5; + + rotate(dx); + setPos(mapToParent(0, -(3 + sin(speed) * 3))); +} +//! [11] diff --git a/examples/multitouch/pinchzoom/mouse.h b/examples/multitouch/pinchzoom/mouse.h new file mode 100644 index 0000000..7545fe6 --- /dev/null +++ b/examples/multitouch/pinchzoom/mouse.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOUSE_H +#define MOUSE_H + +#include +#include + +//! [0] +class Mouse : public QObject, public QGraphicsItem +{ + Q_OBJECT + +public: + Mouse(); + + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget); + +protected: + void timerEvent(QTimerEvent *event); + +private: + qreal angle; + qreal speed; + qreal mouseEyeDirection; + QColor color; +}; +//! [0] + +#endif diff --git a/examples/multitouch/pinchzoom/pinchzoom.pro b/examples/multitouch/pinchzoom/pinchzoom.pro new file mode 100644 index 0000000..bd87cf7 --- /dev/null +++ b/examples/multitouch/pinchzoom/pinchzoom.pro @@ -0,0 +1,16 @@ +HEADERS += \ + mouse.h \ + graphicsview.h +SOURCES += \ + main.cpp \ + mouse.cpp \ + graphicsview.cpp + +RESOURCES += \ + mice.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multitouch/pinchzoom +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS pinchzoom.pro images +sources.path = $$[QT_INSTALL_EXAMPLES]/multitouch/pinchzoom +INSTALLS += target sources -- cgit v0.12 From 98619f427322ac7889b3db6f8991ca079209cec7 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 11:31:33 +0100 Subject: add the multitouch fingerpaint example, based on the scribble example --- examples/multitouch/fingerpaint/fingerpaint.pro | 11 ++ examples/multitouch/fingerpaint/main.cpp | 52 ++++++ examples/multitouch/fingerpaint/mainwindow.cpp | 218 +++++++++++++++++++++++ examples/multitouch/fingerpaint/mainwindow.h | 89 +++++++++ examples/multitouch/fingerpaint/scribblearea.cpp | 213 ++++++++++++++++++++++ examples/multitouch/fingerpaint/scribblearea.h | 81 +++++++++ 6 files changed, 664 insertions(+) create mode 100644 examples/multitouch/fingerpaint/fingerpaint.pro create mode 100644 examples/multitouch/fingerpaint/main.cpp create mode 100644 examples/multitouch/fingerpaint/mainwindow.cpp create mode 100644 examples/multitouch/fingerpaint/mainwindow.h create mode 100644 examples/multitouch/fingerpaint/scribblearea.cpp create mode 100644 examples/multitouch/fingerpaint/scribblearea.h diff --git a/examples/multitouch/fingerpaint/fingerpaint.pro b/examples/multitouch/fingerpaint/fingerpaint.pro new file mode 100644 index 0000000..1d45635 --- /dev/null +++ b/examples/multitouch/fingerpaint/fingerpaint.pro @@ -0,0 +1,11 @@ +HEADERS = mainwindow.h \ + scribblearea.h +SOURCES = main.cpp \ + mainwindow.cpp \ + scribblearea.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multitouch/fingerpaint +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS fingerpaint.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/multitouch/fingerpaint +INSTALLS += target sources diff --git a/examples/multitouch/fingerpaint/main.cpp b/examples/multitouch/fingerpaint/main.cpp new file mode 100644 index 0000000..49bf25b --- /dev/null +++ b/examples/multitouch/fingerpaint/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow window; + window.showMaximized(); + return app.exec(); +} diff --git a/examples/multitouch/fingerpaint/mainwindow.cpp b/examples/multitouch/fingerpaint/mainwindow.cpp new file mode 100644 index 0000000..546c685 --- /dev/null +++ b/examples/multitouch/fingerpaint/mainwindow.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "mainwindow.h" +#include "scribblearea.h" + +//! [0] +MainWindow::MainWindow() +{ + scribbleArea = new ScribbleArea; + setCentralWidget(scribbleArea); + + createActions(); + createMenus(); + + setWindowTitle(tr("Scribble")); + resize(500, 500); +} +//! [0] + +//! [1] +void MainWindow::closeEvent(QCloseEvent *event) +//! [1] //! [2] +{ + if (maybeSave()) { + event->accept(); + } else { + event->ignore(); + } +} +//! [2] + +//! [3] +void MainWindow::open() +//! [3] //! [4] +{ + if (maybeSave()) { + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open File"), QDir::currentPath()); + if (!fileName.isEmpty()) + scribbleArea->openImage(fileName); + } +} +//! [4] + +//! [5] +void MainWindow::save() +//! [5] //! [6] +{ + QAction *action = qobject_cast(sender()); + QByteArray fileFormat = action->data().toByteArray(); + saveFile(fileFormat); +} +//! [6] + +//! [11] +void MainWindow::about() +//! [11] //! [12] +{ + QMessageBox::about(this, tr("About Scribble"), + tr("

The Scribble example shows how to use QMainWindow as the " + "base widget for an application, and how to reimplement some of " + "QWidget's event handlers to receive the events generated for " + "the application's widgets:

We reimplement the mouse event " + "handlers to facilitate drawing, the paint event handler to " + "update the application and the resize event handler to optimize " + "the application's appearance. In addition we reimplement the " + "close event handler to intercept the close events before " + "terminating the application.

The example also demonstrates " + "how to use QPainter to draw an image in real time, as well as " + "to repaint widgets.

")); +} +//! [12] + +//! [13] +void MainWindow::createActions() +//! [13] //! [14] +{ + openAct = new QAction(tr("&Open..."), this); + openAct->setShortcut(tr("Ctrl+O")); + connect(openAct, SIGNAL(triggered()), this, SLOT(open())); + + foreach (QByteArray format, QImageWriter::supportedImageFormats()) { + QString text = tr("%1...").arg(QString(format).toUpper()); + + QAction *action = new QAction(text, this); + action->setData(format); + connect(action, SIGNAL(triggered()), this, SLOT(save())); + saveAsActs.append(action); + } + + printAct = new QAction(tr("&Print..."), this); + connect(printAct, SIGNAL(triggered()), scribbleArea, SLOT(print())); + + exitAct = new QAction(tr("E&xit"), this); + exitAct->setShortcut(tr("Ctrl+Q")); + connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); + + clearScreenAct = new QAction(tr("&Clear Screen"), this); + clearScreenAct->setShortcut(tr("Ctrl+L")); + connect(clearScreenAct, SIGNAL(triggered()), + scribbleArea, SLOT(clearImage())); + + aboutAct = new QAction(tr("&About"), this); + connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); + + aboutQtAct = new QAction(tr("About &Qt"), this); + connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); +} +//! [14] + +//! [15] +void MainWindow::createMenus() +//! [15] //! [16] +{ + saveAsMenu = new QMenu(tr("&Save As"), this); + foreach (QAction *action, saveAsActs) + saveAsMenu->addAction(action); + + fileMenu = new QMenu(tr("&File"), this); + fileMenu->addAction(openAct); + fileMenu->addMenu(saveAsMenu); + fileMenu->addAction(printAct); + fileMenu->addSeparator(); + fileMenu->addAction(exitAct); + + optionMenu = new QMenu(tr("&Options"), this); + optionMenu->addAction(clearScreenAct); + + helpMenu = new QMenu(tr("&Help"), this); + helpMenu->addAction(aboutAct); + helpMenu->addAction(aboutQtAct); + + menuBar()->addMenu(fileMenu); + menuBar()->addMenu(optionMenu); + menuBar()->addMenu(helpMenu); +} +//! [16] + +//! [17] +bool MainWindow::maybeSave() +//! [17] //! [18] +{ + if (scribbleArea->isModified()) { + QMessageBox::StandardButton ret; + ret = QMessageBox::warning(this, tr("Scribble"), + tr("The image has been modified.\n" + "Do you want to save your changes?"), + QMessageBox::Save | QMessageBox::Discard + | QMessageBox::Cancel); + if (ret == QMessageBox::Save) { + return saveFile("png"); + } else if (ret == QMessageBox::Cancel) { + return false; + } + } + return true; +} +//! [18] + +//! [19] +bool MainWindow::saveFile(const QByteArray &fileFormat) +//! [19] //! [20] +{ + QString initialPath = QDir::currentPath() + "/untitled." + fileFormat; + + QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), + initialPath, + tr("%1 Files (*.%2);;All Files (*)") + .arg(QString(fileFormat.toUpper())) + .arg(QString(fileFormat))); + if (fileName.isEmpty()) { + return false; + } else { + return scribbleArea->saveImage(fileName, fileFormat); + } +} +//! [20] diff --git a/examples/multitouch/fingerpaint/mainwindow.h b/examples/multitouch/fingerpaint/mainwindow.h new file mode 100644 index 0000000..fdb790d --- /dev/null +++ b/examples/multitouch/fingerpaint/mainwindow.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +class ScribbleArea; + +//! [0] +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + +protected: + void closeEvent(QCloseEvent *event); + +private slots: + void open(); + void save(); + void about(); + +private: + void createActions(); + void createMenus(); + bool maybeSave(); + bool saveFile(const QByteArray &fileFormat); + + ScribbleArea *scribbleArea; + + QMenu *saveAsMenu; + QMenu *fileMenu; + QMenu *optionMenu; + QMenu *helpMenu; + + QAction *openAct; + QList saveAsActs; + QAction *exitAct; + QAction *printAct; + QAction *clearScreenAct; + QAction *aboutAct; + QAction *aboutQtAct; +}; +//! [0] + +#endif diff --git a/examples/multitouch/fingerpaint/scribblearea.cpp b/examples/multitouch/fingerpaint/scribblearea.cpp new file mode 100644 index 0000000..a042fe7 --- /dev/null +++ b/examples/multitouch/fingerpaint/scribblearea.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "scribblearea.h" + +//! [0] +ScribbleArea::ScribbleArea(QWidget *parent) + : QWidget(parent) +{ + setAttribute(Qt::WA_AcceptTouchEvents); + setAttribute(Qt::WA_StaticContents); + modified = false; + + myPenColors + << QColor("green") + << QColor("purple") + << QColor("red") + << QColor("blue") + << QColor("yellow") + + << QColor("pink") + << QColor("orange") + << QColor("brown") + << QColor("grey") + << QColor("black"); +} +//! [0] + +//! [1] +bool ScribbleArea::openImage(const QString &fileName) +//! [1] //! [2] +{ + QImage loadedImage; + if (!loadedImage.load(fileName)) + return false; + + QSize newSize = loadedImage.size().expandedTo(size()); + resizeImage(&loadedImage, newSize); + image = loadedImage; + modified = false; + update(); + return true; +} +//! [2] + +//! [3] +bool ScribbleArea::saveImage(const QString &fileName, const char *fileFormat) +//! [3] //! [4] +{ + QImage visibleImage = image; + resizeImage(&visibleImage, size()); + + if (visibleImage.save(fileName, fileFormat)) { + modified = false; + return true; + } else { + return false; + } +} +//! [4] + +//! [9] +void ScribbleArea::clearImage() +//! [9] //! [10] +{ + image.fill(qRgb(255, 255, 255)); + modified = true; + update(); +} +//! [10] + +//! [12] //! [13] +void ScribbleArea::paintEvent(QPaintEvent *event) +//! [13] //! [14] +{ + QPainter painter(this); + const QRect rect = event->rect(); + painter.drawImage(rect.topLeft(), image, rect); +} +//! [14] + +//! [15] +void ScribbleArea::resizeEvent(QResizeEvent *event) +//! [15] //! [16] +{ + if (width() > image.width() || height() > image.height()) { + int newWidth = qMax(width() + 128, image.width()); + int newHeight = qMax(height() + 128, image.height()); + resizeImage(&image, QSize(newWidth, newHeight)); + update(); + } + QWidget::resizeEvent(event); +} +//! [16] + +//! [19] +void ScribbleArea::resizeImage(QImage *image, const QSize &newSize) +//! [19] //! [20] +{ + if (image->size() == newSize) + return; + + QImage newImage(newSize, QImage::Format_RGB32); + newImage.fill(qRgb(255, 255, 255)); + QPainter painter(&newImage); + painter.drawImage(QPoint(0, 0), *image); + *image = newImage; +} +//! [20] + +//! [21] +void ScribbleArea::print() +{ +#ifndef QT_NO_PRINTER + QPrinter printer(QPrinter::HighResolution); + + QPrintDialog *printDialog = new QPrintDialog(&printer, this); +//! [21] //! [22] + if (printDialog->exec() == QDialog::Accepted) { + QPainter painter(&printer); + QRect rect = painter.viewport(); + QSize size = image.size(); + size.scale(rect.size(), Qt::KeepAspectRatio); + painter.setViewport(rect.x(), rect.y(), size.width(), size.height()); + painter.setWindow(image.rect()); + painter.drawImage(0, 0, image); + } +#endif // QT_NO_PRINTER +} +//! [22] + +bool ScribbleArea::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + QList touchPoints = static_cast(event)->touchPoints(); + foreach (QTouchEvent::TouchPoint *touchPoint, touchPoints) { + switch (touchPoint->state()) { + case Qt::TouchPointStationary: + // don't do anything if this touch point hasn't moved + continue; + default: + { + int diameter = int(50 * touchPoint->pressure()); + QRectF rectF(0, 0, diameter, diameter); + rectF.moveCenter(touchPoint->pos()); + QRect rect = rectF.toRect(); + + QPainter painter(&image); + painter.setPen(Qt::NoPen); + painter.setBrush(myPenColors.at(touchPoint->id())); + painter.drawEllipse(rectF); + painter.end(); + + modified = true; + int rad = 2; + update(rect.adjusted(-rad,-rad, +rad, +rad)); + } + break; + } + } + + event->accept(); + return true; + } + default: + break; + } + return QWidget::event(event); +} diff --git a/examples/multitouch/fingerpaint/scribblearea.h b/examples/multitouch/fingerpaint/scribblearea.h new file mode 100644 index 0000000..8928ce1 --- /dev/null +++ b/examples/multitouch/fingerpaint/scribblearea.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SCRIBBLEAREA_H +#define SCRIBBLEAREA_H + +#include +#include +#include +#include + +//! [0] +class ScribbleArea : public QWidget +{ + Q_OBJECT + +public: + ScribbleArea(QWidget *parent = 0); + + bool openImage(const QString &fileName); + bool saveImage(const QString &fileName, const char *fileFormat); + + bool isModified() const { return modified; } + +public slots: + void clearImage(); + void print(); + +protected: + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + bool event(QEvent *event); + +private: + void resizeImage(QImage *image, const QSize &newSize); + + bool modified; + QList myPenColors; + QImage image; +}; +//! [0] + +#endif -- cgit v0.12 From b5236dedd9ab72d6e0bd09151a6f55bb40a7034c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 11:32:52 +0100 Subject: have the window title in the fingerpaint example say Finger Paint, not Scribble --- examples/multitouch/fingerpaint/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/multitouch/fingerpaint/mainwindow.cpp b/examples/multitouch/fingerpaint/mainwindow.cpp index 546c685..e510f89 100644 --- a/examples/multitouch/fingerpaint/mainwindow.cpp +++ b/examples/multitouch/fingerpaint/mainwindow.cpp @@ -53,7 +53,7 @@ MainWindow::MainWindow() createActions(); createMenus(); - setWindowTitle(tr("Scribble")); + setWindowTitle(tr("Finger Paint")); resize(500, 500); } //! [0] -- cgit v0.12 From 81b5a795a47f6d03c81756d7b5b4501f60528c41 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 11:34:20 +0100 Subject: add fingerpaint to examples\multitouch.pro --- examples/multitouch/multitouch.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/multitouch/multitouch.pro b/examples/multitouch/multitouch.pro index 7efa412..b2b2b76 100644 --- a/examples/multitouch/multitouch.pro +++ b/examples/multitouch/multitouch.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS = pinchzoom \ No newline at end of file +SUBDIRS = pinchzoom fingerpaint -- cgit v0.12 From e1dd9242895e27afa63b255198ad541f738c06ef Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 11:52:33 +0100 Subject: remove code duplication for setting the focus widget based on t he focus policy refactor duplicated code in the mouse and wheel event handlers that sets the focus widget based on the widget's (or its ancestors) focus policy --- src/gui/kernel/qapplication.cpp | 46 ++++++++++++++++++++--------------------- src/gui/kernel/qapplication_p.h | 4 ++++ 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 1bbb019..34c2d74 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -3735,19 +3735,10 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QPoint relpos = mouse->pos(); if (e->spontaneous()) { - if (e->type() == QEvent::MouseButtonPress) { - QWidget *fw = w; - while (fw) { - if (fw->isEnabled() - && QApplicationPrivate::shouldSetFocus(fw, Qt::ClickFocus)) { - fw->setFocus(Qt::MouseFocusReason); - break; - } - if (fw->isWindow()) - break; - fw = fw->parentWidget(); - } + QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, + Qt::ClickFocus, + Qt::MouseFocusReason); } if (e->type() == QEvent::MouseMove && mouse->buttons() == 0) { @@ -3829,17 +3820,9 @@ bool QApplication::notify(QObject *receiver, QEvent *e) bool eventAccepted = wheel->isAccepted(); if (e->spontaneous()) { - QWidget *fw = w; - while (fw) { - if (fw->isEnabled() - && QApplicationPrivate::shouldSetFocus(fw, Qt::WheelFocus)) { - fw->setFocus(Qt::MouseFocusReason); - break; - } - if (fw->isWindow()) - break; - fw = fw->parentWidget(); - } + QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, + Qt::WheelFocus, + Qt::MouseFocusReason); } while (w) { @@ -5033,6 +5016,23 @@ Qt::LayoutDirection QApplication::keyboardInputDirection() return qt_keymapper_private()->keyboardInputDirection; } +void QApplicationPrivate::giveFocusAccordingToFocusPolicy(QWidget *widget, + Qt::FocusPolicy focusPolicy, + Qt::FocusReason focusReason) +{ + QWidget *focusWidget = widget; + while (focusWidget) { + if (focusWidget->isEnabled() + && QApplicationPrivate::shouldSetFocus(focusWidget, focusPolicy)) { + focusWidget->setFocus(focusReason); + break; + } + if (focusWidget->isWindow()) + break; + focusWidget = focusWidget->parentWidget(); + } +} + bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) { QWidget *f = w; diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 7487f0a..6d75f37 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -430,6 +430,10 @@ private: #endif static QApplicationPrivate *self; + + static void giveFocusAccordingToFocusPolicy(QWidget *w, + Qt::FocusPolicy focusPolicy, + Qt::FocusReason focusReason); static bool shouldSetFocus(QWidget *w, Qt::FocusPolicy policy); }; -- cgit v0.12 From 480b0fe494cc7dec7d8082860c6216e970ce6a57 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 11:57:14 +0100 Subject: implement event propagation for touch events behavior is similar to that of QGraphicsScene mouse events: the first touch event (the TouchBegin) is propagated to all parents. if a widget accepts the event, it will receive all other touch events (the TouchUpdate and TouchEnd events). If no widget accepts the TouchBegin, then we will fallback to normal mouse events (TBD). --- src/gui/kernel/qapplication.cpp | 48 +++++++++++++++++++++++++++++++++++++++++ src/gui/kernel/qapplication_p.h | 3 +++ src/gui/kernel/qevent.h | 2 ++ 3 files changed, 53 insertions(+) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 34c2d74..9c70208 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -85,6 +85,7 @@ #include #include "qapplication_p.h" +#include "qevent_p.h" #include "qwidget_p.h" #include "qapplication.h" @@ -4027,7 +4028,41 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } break; #endif + case QEvent::TouchBegin: + // Note: TouchUpdate and TouchEnd events are sent to d->currentMultitouchWidget and never propagated + { + QWidget *widget = static_cast(receiver); + QTouchEvent *touchEvent = static_cast(e); + bool eventAccepted = touchEvent->isAccepted(); + + if (widget->testAttribute(Qt::WA_AcceptTouchEvents) && e->spontaneous()) { + // give the widget focus if the focus policy allows it + QApplicationPrivate::giveFocusAccordingToFocusPolicy(widget, + Qt::ClickFocus, + Qt::MouseFocusReason); + } + + while (widget) { + // first, try to deliver the touch event + touchEvent->ignore(); + res = widget->testAttribute(Qt::WA_AcceptTouchEvents) + && d->notify_helper(widget, touchEvent); + eventAccepted = touchEvent->isAccepted(); + touchEvent->spont = false; + if (res && eventAccepted) { + // the first widget to accept the TouchBegin gets an implicit grab. + d->currentMultitouchWidget = widget; + break; + } else if (widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) { + break; + } + widget = widget->parentWidget(); + d->updateTouchPointsForWidget(widget, touchEvent); + } + touchEvent->setAccepted(eventAccepted); + break; + } default: res = d->notify_helper(receiver, e); break; @@ -5046,6 +5081,19 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) return true; } +void QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent) +{ + for (int i = 0; i < touchEvent->_touchPoints.count(); ++i) { + QTouchEvent::TouchPoint *touchPoint = touchEvent->_touchPoints.at(i); + + // preserve the sub-pixel resolution + const QPointF delta = touchPoint->d->globalPos - touchPoint->d->globalPos.toPoint(); + touchPoint->d->pos = widget->mapFromGlobal(touchPoint->d->globalPos.toPoint()) + delta; + touchPoint->d->startPos = widget->mapFromGlobal(touchPoint->d->startGlobalPos.toPoint()) + delta; + touchPoint->d->lastPos = widget->mapFromGlobal(touchPoint->d->lastGlobalPos.toPoint()) + delta; + } +} + QT_END_NAMESPACE #include "moc_qapplication.cpp" diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 6d75f37..252d0cb 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -424,6 +424,9 @@ public: void sendSyntheticEnterLeave(QWidget *widget); #endif + QPointer currentMultitouchWidget; + static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); + private: #ifdef Q_WS_QWS QMap maxWindowRects; diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 3cb712d..506e0c1 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -758,6 +758,8 @@ public: protected: QList _touchPoints; + + friend class QApplicationPrivate; }; QT_END_NAMESPACE -- cgit v0.12 From aa50245865b5bf8655ee3d476ee961009f0c314e Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 13:38:02 +0100 Subject: add a multitouch example that shows how to handle multitouch events in QGraphicsItems --- examples/multitouch/knobs/knob.cpp | 93 +++++++++++++++++++++++++++++++++++++ examples/multitouch/knobs/knob.h | 55 ++++++++++++++++++++++ examples/multitouch/knobs/knobs.pro | 2 + examples/multitouch/knobs/main.cpp | 66 ++++++++++++++++++++++++++ examples/multitouch/multitouch.pro | 2 +- 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 examples/multitouch/knobs/knob.cpp create mode 100644 examples/multitouch/knobs/knob.h create mode 100644 examples/multitouch/knobs/knobs.pro create mode 100644 examples/multitouch/knobs/main.cpp diff --git a/examples/multitouch/knobs/knob.cpp b/examples/multitouch/knobs/knob.cpp new file mode 100644 index 0000000..4622f69 --- /dev/null +++ b/examples/multitouch/knobs/knob.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "knob.h" + +#include +#include + +Knob::Knob() + : QGraphicsEllipseItem(-50, -50, 100, 100) +{ + setAcceptTouchEvents(true); + setBrush(Qt::lightGray); + + QGraphicsEllipseItem *leftItem = new QGraphicsEllipseItem(0, 0, 10, 10, this); + leftItem->setPos(-45, -5); + leftItem->setBrush(Qt::darkGreen); + + QGraphicsEllipseItem *rightItem = new QGraphicsEllipseItem(0, 0, 10, 10, this); + rightItem->setPos(35, -5); + rightItem->setBrush(Qt::darkRed); +} + +bool Knob::sceneEvent(QEvent *event) +{ + switch (event->type()) { + case QEvent::GraphicsSceneTouchBegin: + event->accept(); + break; + + case QEvent::GraphicsSceneTouchUpdate: + case QEvent::GraphicsSceneTouchEnd: + { + QGraphicsSceneTouchEvent *touchEvent = static_cast(event); + + if (touchEvent->touchPoints().count() == 2) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint1 = touchEvent->touchPoints().first(); + QGraphicsSceneTouchEvent::TouchPoint *touchPoint2 = touchEvent->touchPoints().last(); + + QLineF line1(touchPoint1->lastScenePos(), touchPoint2->lastScenePos()); + QLineF line2(touchPoint1->scenePos(), touchPoint2->scenePos()); + + rotate(line2.angleTo(line1)); + } + + touchEvent->accept(); + break; + } + + default: + return QGraphicsItem::sceneEvent(event); + } + + return true; +} diff --git a/examples/multitouch/knobs/knob.h b/examples/multitouch/knobs/knob.h new file mode 100644 index 0000000..1079483 --- /dev/null +++ b/examples/multitouch/knobs/knob.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef KNOB_H +#define KNOB_H + +#include + +class Knob : public QGraphicsEllipseItem +{ +public: + Knob(); + + bool sceneEvent(QEvent *event); +}; + +#endif // KNOB_H diff --git a/examples/multitouch/knobs/knobs.pro b/examples/multitouch/knobs/knobs.pro new file mode 100644 index 0000000..90f0ba5 --- /dev/null +++ b/examples/multitouch/knobs/knobs.pro @@ -0,0 +1,2 @@ +HEADERS = knob.h +SOURCES = main.cpp knob.cpp diff --git a/examples/multitouch/knobs/main.cpp b/examples/multitouch/knobs/main.cpp new file mode 100644 index 0000000..c5a7ea7 --- /dev/null +++ b/examples/multitouch/knobs/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "knob.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.viewport()->setAttribute(Qt::WA_AcceptTouchEvents); + + Knob *knob1 = new Knob; + knob1->setPos(-110, 0); + Knob *knob2 = new Knob; + + scene.addItem(knob1); + scene.addItem(knob2); + + view.showFullScreen(); + view.fitInView(scene.sceneRect().adjusted(-20, -20, 20, 20), Qt::KeepAspectRatio); + + return app.exec(); +} diff --git a/examples/multitouch/multitouch.pro b/examples/multitouch/multitouch.pro index b2b2b76..b3d1d55 100644 --- a/examples/multitouch/multitouch.pro +++ b/examples/multitouch/multitouch.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS = pinchzoom fingerpaint +SUBDIRS = pinchzoom fingerpaint knobs -- cgit v0.12 From 0103e7871000f224a1048649b4eca18a7840fe3b Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 13:30:38 +0100 Subject: initial implementation of multitouch support on Windows 7 WM_TOUCH* messages are translated to QTouchEvents and sent to the appropriate widget. we set the qt_tabletChokeMouse variable according to the result of the event processing to make sure that mouse events are sent when touch events are not handled. --- src/gui/kernel/qapplication_p.h | 27 ++++- src/gui/kernel/qapplication_win.cpp | 192 ++++++++++++++++++++++++++++++++++++ src/gui/kernel/qwidget_win.cpp | 4 + 3 files changed, 220 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 252d0cb..ccd9f41 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -55,6 +55,7 @@ // #include "QtGui/qapplication.h" +#include "QtGui/qevent.h" #include "QtGui/qfont.h" #include "QtGui/qcursor.h" #include "QtGui/qregion.h" @@ -77,10 +78,7 @@ class QClipboard; class QGraphicsScene; class QGraphicsSystem; class QInputContext; -class QKeyEvent; -class QMouseEvent; class QObject; -class QWheelEvent; class QWidget; extern bool qt_is_gui_used; @@ -189,6 +187,12 @@ extern "C" { } #endif +#if defined(Q_WS_WIN) +typedef BOOL (WINAPI *qt_RegisterTouchWindowPtr)(HWND, ULONG); +typedef BOOL (WINAPI *qt_GetTouchInputInfoPtr)(HANDLE, UINT, PVOID, int); +typedef BOOL (WINAPI *qt_CloseTouchInputHandlePtr)(HANDLE); +#endif + class QScopedLoopLevelCounter { QThreadData *threadData; @@ -427,6 +431,23 @@ public: QPointer currentMultitouchWidget; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); +#if defined(Q_WS_WIN) + static qt_RegisterTouchWindowPtr RegisterTouchWindow; + static qt_GetTouchInputInfoPtr GetTouchInputInfo; + static qt_CloseTouchInputHandlePtr CloseTouchInputHandle; + + QMap touchInputIDToTouchPointID; + QVector allTouchPoints; + QList currentTouchPoints, activeTouchPoints; + + static bool sendTouchEvent(QWidget *widget, QTouchEvent *touchEvent); + + void initializeMultitouch(); + void insertActiveTouch(QTouchEvent::TouchPoint *touchPoint); + void removeActiveTouch(QTouchEvent::TouchPoint *touchPoint); + bool translateTouchEvent(const MSG &msg); +#endif + private: #ifdef Q_WS_QWS QMap maxWindowRects; diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index fae0335..7c7f7f6 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -88,6 +88,7 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c #include "qdebug.h" #include #include +#include "qevent_p.h" //#define ALIEN_DEBUG @@ -112,6 +113,39 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c # include #endif +#ifndef WM_TOUCHMOVE + +# define WM_TOUCHMOVE 0x0240 +# define WM_TOUCHDOWN 0x0241 +# define WM_TOUCHUP 0x0242 + +# define TOUCHEVENTF_MOVE 0x0001 +# define TOUCHEVENTF_DOWN 0x0002 +# define TOUCHEVENTF_UP 0x0004 +# define TOUCHEVENTF_INRANGE 0x0008 +# define TOUCHEVENTF_PRIMARY 0x0010 +# define TOUCHEVENTF_NOCOALESCE 0x0020 +# define TOUCHEVENTF_PEN 0x0040 + +# define TOUCHINPUTMASKF_TIMEFROMSYSTEM 0x0001 +# define TOUCHINPUTMASKF_EXTRAINFO 0x0002 +# define TOUCHINPUTMASKF_CONTACTAREA 0x0004 + +typedef struct tagTOUCHINPUT +{ + LONG x; + LONG y; + HANDLE hSource; + DWORD dwID; + DWORD dwFlags; + DWORD dwMask; + DWORD dwTime; + ULONG_PTR dwExtraInfo; + DWORD cxContact; + DWORD cyContact; +} TOUCHINPUT, *PTOUCHINPUT; + +#endif #ifndef FLASHW_STOP typedef struct { @@ -856,6 +890,8 @@ void qt_init(QApplicationPrivate *priv, int) if (ptrUpdateLayeredWindow && !ptrUpdateLayeredWindowIndirect) ptrUpdateLayeredWindowIndirect = qt_updateLayeredWindowIndirect; #endif + + priv->initializeMultitouch(); } /***************************************************************************** @@ -1726,6 +1762,11 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam result = widget->translateWheelEvent(msg); } else { switch (message) { + case WM_TOUCHMOVE: + case WM_TOUCHDOWN: + case WM_TOUCHUP: + result = getQApplicationPrivateInternal()->translateTouchEvent(msg); + break; case WM_KEYDOWN: // keyboard event case WM_SYSKEYDOWN: qt_keymapper_private()->updateKeyMap(msg); @@ -3953,4 +3994,155 @@ void QSessionManager::cancel() #endif //QT_NO_SESSIONMANAGER +qt_RegisterTouchWindowPtr QApplicationPrivate::RegisterTouchWindow = 0; +qt_GetTouchInputInfoPtr QApplicationPrivate::GetTouchInputInfo = 0; +qt_CloseTouchInputHandlePtr QApplicationPrivate::CloseTouchInputHandle = 0; + +void QApplicationPrivate::initializeMultitouch() +{ + QLibrary library(QLatin1String("user32")); + RegisterTouchWindow = static_cast(library.resolve("RegisterTouchWindow")); + GetTouchInputInfo = static_cast(library.resolve("GetTouchInputInfo")); + CloseTouchInputHandle = static_cast(library.resolve("CloseTouchInputHandle")); + + currentMultitouchWidget = 0; + touchInputIDToTouchPointID.clear(); + allTouchPoints.clear(); + currentTouchPoints.clear(); + activeTouchPoints.clear(); +} + +void QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint) +{ + // insort deviceNumber + int at = 0; + for (; at < currentTouchPoints.count(); ++at) { + if (currentTouchPoints.at(at)->id() > touchPoint->id()) + break; + } + currentTouchPoints.insert(at, touchPoint); + activeTouchPoints = currentTouchPoints; + + if (currentTouchPoints.count() > allTouchPoints.count()) { + qFatal("Qt: INTERNAL ERROR: currentTouchPoints.count() (%d) > allTouchPoints.count() (%d)", + currentTouchPoints.count(), + allTouchPoints.count()); + } +} + +void QApplicationPrivate::removeActiveTouch(QTouchEvent::TouchPoint *touchPoint) +{ + for (int i = qMin(currentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { + if (currentTouchPoints.at(i) == touchPoint) { + currentTouchPoints.removeAt(i); + break; + } + } + + // leave activeTouchPoints unchanged, since we need to make sure + // that the Release for deviceNumber is sent +} + +bool QApplicationPrivate::translateTouchEvent(const MSG &msg) +{ + Q_Q(QApplication); + + bool sendTouchBegin = currentTouchPoints.isEmpty(); + activeTouchPoints = currentTouchPoints; + + QVector winTouchInputs(msg.wParam); + memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); + QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); + for (int i = 0; i < winTouchInputs.count(); ++i) { + const TOUCHINPUT &touchInput = winTouchInputs.at(i); + + int touchPointID = touchInputIDToTouchPointID.value(touchInput.dwID, -1); + if (touchPointID == -1) { + touchPointID = touchInputIDToTouchPointID.count(); + touchInputIDToTouchPointID.insert(touchInput.dwID, touchPointID); + } + + if (allTouchPoints.count() <= touchPointID) + allTouchPoints.resize(touchPointID + 1); + + QTouchEvent::TouchPoint *touchPoint = allTouchPoints.at(touchPointID); + if (!touchPoint) + touchPoint = allTouchPoints[touchPointID] = new QTouchEvent::TouchPoint(touchPointID); + + // update state + bool down = touchPoint->d->state != Qt::TouchPointReleased; + QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); + if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { + insertActiveTouch(touchPoint); + touchPoint->d->state = Qt::TouchPointPressed; + touchPoint->d->globalPos = touchPoint->d->startGlobalPos = touchPoint->d->lastGlobalPos = globalPos; + touchPoint->d->pressure = qreal(1.); + } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { + removeActiveTouch(touchPoint); + touchPoint->d->state = Qt::TouchPointReleased; + touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; + touchPoint->d->globalPos = globalPos; + touchPoint->d->pressure = qreal(0.); + } else if (down) { + touchPoint->d->state = globalPos == touchPoint->d->globalPos + ? Qt::TouchPointStationary + : Qt::TouchPointMoved; + touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; + touchPoint->d->globalPos = globalPos; + // pressure should still be 1. + } + } + QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); + + bool sendTouchEnd = currentTouchPoints.isEmpty(); + + if (activeTouchPoints.isEmpty()) + return false; + + if (sendTouchBegin) { + // the window under the first touch point gets the touch event + int firstTouchId = activeTouchPoints.first()->id(); + const QPoint &globalPos = allTouchPoints.at(firstTouchId)->d->globalPos.toPoint(); + QWidget *window = q->topLevelAt(globalPos); + if (window) { + QWidget *child = window->childAt(window->mapFromGlobal(globalPos)); + if (!child) + child = window; + currentMultitouchWidget = child; + } + } + + QWidget *widget = q->activePopupWidget(); + if (!widget) + widget = currentMultitouchWidget; + + if (sendTouchEnd) { + // reset currentMultitouchWindow when the last touch is released + currentMultitouchWidget = 0; + if (!currentTouchPoints.isEmpty()) { + qFatal("Qt: INTERNAL ERROR, currentTouchPoints should be empty!"); + } + } + + // deliver the event + if (widget && QApplicationPrivate::tryModalHelper(widget, 0)) { + QTouchEvent touchEvent((sendTouchBegin + ? QEvent::TouchBegin + : sendTouchEnd + ? QEvent::TouchEnd + : QEvent::TouchUpdate), + q->keyboardModifiers(), activeTouchPoints); + updateTouchPointsForWidget(widget, &touchEvent); + return (qt_tabletChokeMouse = sendTouchEvent(widget, &touchEvent)); + } + + return false; +} + +bool QApplicationPrivate::sendTouchEvent(QWidget *widget, QTouchEvent *touchEvent) +{ + bool res = QApplication::sendSpontaneousEvent(widget, touchEvent); + return res && touchEvent->isAccepted(); +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index ffbb341..b55f1c8 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -503,6 +503,10 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO } } + // ### don't always register for touch events + if (QApplicationPrivate::RegisterTouchWindow && !desktop) + QApplicationPrivate::RegisterTouchWindow(id, 0); + q->setAttribute(Qt::WA_WState_Created); // accept move/resize events hd = 0; // no display context -- cgit v0.12 From 761cac16eb0a6d32f9e7d558867c5c1bbfb244f9 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 25 Mar 2009 13:49:34 +0100 Subject: show the example maximized, not fullscreen --- examples/multitouch/knobs/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/multitouch/knobs/main.cpp b/examples/multitouch/knobs/main.cpp index c5a7ea7..8f78cf9 100644 --- a/examples/multitouch/knobs/main.cpp +++ b/examples/multitouch/knobs/main.cpp @@ -59,7 +59,7 @@ int main(int argc, char **argv) scene.addItem(knob1); scene.addItem(knob2); - view.showFullScreen(); + view.showMaximized(); view.fitInView(scene.sceneRect().adjusted(-20, -20, 20, 20), Qt::KeepAspectRatio); return app.exec(); -- cgit v0.12 From e14f2ddd25a72f07a3215c1bc7f4755c48dd6e3a Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 12 Mar 2009 13:45:18 +0100 Subject: add a test for touch event propagation --- tests/auto/qapplication/tst_qapplication.cpp | 143 +++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/tests/auto/qapplication/tst_qapplication.cpp b/tests/auto/qapplication/tst_qapplication.cpp index cee3125..356768e 100644 --- a/tests/auto/qapplication/tst_qapplication.cpp +++ b/tests/auto/qapplication/tst_qapplication.cpp @@ -48,6 +48,7 @@ #include "qabstracteventdispatcher.h" #include +#include "private/qapplication_p.h" #include "private/qstylesheetstyle_p.h" #ifdef Q_OS_WINCE #include @@ -117,6 +118,8 @@ private slots: void windowsCommandLine_data(); void windowsCommandLine(); + + void touchEventPropagation(); }; class MyInputContext : public QInputContext @@ -1773,6 +1776,146 @@ void tst_QApplication::windowsCommandLine() #endif } +class TouchEventPropagationTestWidget : public QWidget +{ + Q_OBJECT + +public: + bool seenTouchEvent, acceptTouchEvent, seenMouseEvent, acceptMouseEvent; + + TouchEventPropagationTestWidget(QWidget *parent = 0) + : QWidget(parent), seenTouchEvent(false), acceptTouchEvent(false), seenMouseEvent(false), acceptMouseEvent(false) + { } + + void reset() + { + seenTouchEvent = acceptTouchEvent = seenMouseEvent = acceptMouseEvent = false; + } + + bool event(QEvent *event) + { + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + // qDebug() << objectName() << "seenMouseEvent = true"; + seenMouseEvent = true; + event->setAccepted(acceptMouseEvent); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + // qDebug() << objectName() << "seenTouchEvent = true"; + seenTouchEvent = true; + event->setAccepted(acceptTouchEvent); + break; + default: + return QWidget::event(event); + } + return true; + } +}; + +void tst_QApplication::touchEventPropagation() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QTouchEvent::TouchPoint touchPoint(0); + QTouchEvent touchEvent(QEvent::TouchBegin, Qt::NoModifier, QList() << (&touchPoint)); + + { + // touch event behavior on a window + TouchEventPropagationTestWidget window; + window.setObjectName("1. window"); + + QApplicationPrivate::sendTouchEvent(&window, &touchEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(window.seenMouseEvent); + + window.reset(); + window.setAttribute(Qt::WA_AcceptsTouchEvents); + QApplicationPrivate::sendTouchEvent(&window, &touchEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(window.seenMouseEvent); + + window.reset(); + window.acceptTouchEvent = true; + QApplicationPrivate::sendTouchEvent(&window, &touchEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + } + + { + // touch event behavior on a window with a child widget + TouchEventPropagationTestWidget window; + window.setObjectName("2. window"); + TouchEventPropagationTestWidget widget(&window); + widget.setObjectName("2. widget"); + + QApplicationPrivate::sendTouchEvent(&widget, &touchEvent); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.setAttribute(Qt::WA_AcceptsTouchEvents); + QApplicationPrivate::sendTouchEvent(&widget, &touchEvent); + QVERIFY(widget.seenTouchEvent); + QVERIFY(widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.acceptMouseEvent = true; + QApplicationPrivate::sendTouchEvent(&widget, &touchEvent); + QVERIFY(widget.seenTouchEvent); + QVERIFY(widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.acceptTouchEvent = true; + QApplicationPrivate::sendTouchEvent(&widget, &touchEvent); + QVERIFY(widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.setAttribute(Qt::WA_AcceptsTouchEvents, false); + window.setAttribute(Qt::WA_AcceptsTouchEvents); + QApplicationPrivate::sendTouchEvent(&widget, &touchEvent); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(widget.seenMouseEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(window.seenMouseEvent); + + window.reset(); + widget.reset(); + window.acceptTouchEvent = true; + QApplicationPrivate::sendTouchEvent(&widget, &touchEvent); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.acceptMouseEvent = true; // doesn't matter, touch events are propagated first + window.acceptTouchEvent = true; + QApplicationPrivate::sendTouchEvent(&widget, &touchEvent); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + } +} + //QTEST_APPLESS_MAIN(tst_QApplication) int main(int argc, char *argv[]) { -- cgit v0.12 From b2824b384485ddba95091e49f81733485f1a73be Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 26 Mar 2009 08:24:39 +0100 Subject: remove QApplicationPrivate::sendTouchEvent --- src/gui/kernel/qapplication_p.h | 2 -- src/gui/kernel/qapplication_win.cpp | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index ccd9f41..ce7110d 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -440,8 +440,6 @@ public: QVector allTouchPoints; QList currentTouchPoints, activeTouchPoints; - static bool sendTouchEvent(QWidget *widget, QTouchEvent *touchEvent); - void initializeMultitouch(); void insertActiveTouch(QTouchEvent::TouchPoint *touchPoint); void removeActiveTouch(QTouchEvent::TouchPoint *touchPoint); diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 7c7f7f6..cef8cf2 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4133,16 +4133,12 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) : QEvent::TouchUpdate), q->keyboardModifiers(), activeTouchPoints); updateTouchPointsForWidget(widget, &touchEvent); - return (qt_tabletChokeMouse = sendTouchEvent(widget, &touchEvent)); + + bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent); + return (qt_tabletChokeMouse = res && touchEvent.isAccepted()); } return false; } -bool QApplicationPrivate::sendTouchEvent(QWidget *widget, QTouchEvent *touchEvent) -{ - bool res = QApplication::sendSpontaneousEvent(widget, touchEvent); - return res && touchEvent->isAccepted(); -} - QT_END_NAMESPACE -- cgit v0.12 From b3a2c210973a77d1517d017637a3d6ef849f4089 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 26 Mar 2009 08:54:36 +0100 Subject: implement panning using touch events since we don't get mouse events to send to QGraphicsView's ScrollHandDrag handlers, we need to do it ourselves --- examples/multitouch/pinchzoom/graphicsview.cpp | 13 ++++++++----- examples/multitouch/pinchzoom/main.cpp | 1 - 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/multitouch/pinchzoom/graphicsview.cpp b/examples/multitouch/pinchzoom/graphicsview.cpp index b77c926..f763e6f 100644 --- a/examples/multitouch/pinchzoom/graphicsview.cpp +++ b/examples/multitouch/pinchzoom/graphicsview.cpp @@ -41,9 +41,9 @@ #include "graphicsview.h" +#include #include - GraphicsView::GraphicsView(QGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent) { @@ -57,10 +57,13 @@ bool GraphicsView::event(QEvent *event) case QEvent::TouchUpdate: case QEvent::TouchEnd: { - qDebug("touch events"); - - QList touchPoints = static_cast(event)->touchPoints(); - if (touchPoints.count() == 2) { + QList touchPoints = static_cast(event)->touchPoints(); + if (touchPoints.count() == 1) { + const QTouchEvent::TouchPoint *touchPoint = touchPoints.first(); + QPointF delta = touchPoint->pos() - touchPoint->lastPos(); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() - delta.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() - delta.y()); + } else if (touchPoints.count() == 2) { // determine scale factor const QTouchEvent::TouchPoint *touchPoint0 = touchPoints.first(); const QTouchEvent::TouchPoint *touchPoint1 = touchPoints.last(); diff --git a/examples/multitouch/pinchzoom/main.cpp b/examples/multitouch/pinchzoom/main.cpp index 6b25060..bb86870 100644 --- a/examples/multitouch/pinchzoom/main.cpp +++ b/examples/multitouch/pinchzoom/main.cpp @@ -78,7 +78,6 @@ int main(int argc, char **argv) //! [4] //! [5] view.setCacheMode(QGraphicsView::CacheBackground); view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); - view.setDragMode(QGraphicsView::ScrollHandDrag); //! [5] //! [6] view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Colliding Mice")); view.showMaximized(); -- cgit v0.12 From 46caddc9fa82df4fba88ea4ef87dba692b18c1c9 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 30 Mar 2009 11:08:12 +0200 Subject: compile --- src/network/kernel/qauthenticator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 4f477bd..19243aa 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE -#include <../3rdparty/des/des.cpp> +#include <../../3rdparty/des/des.cpp> static QByteArray qNtlmPhase1(); static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data); -- cgit v0.12 From be8b6047ccd43df1127beb4c602c58408ff9b612 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 31 Mar 2009 13:19:23 +0200 Subject: Add QSysInfo::WV_WINDOWS7 --- src/corelib/global/qglobal.cpp | 9 ++++++++- src/corelib/global/qglobal.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 459167e..bcb17a2 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -1054,6 +1054,7 @@ bool qSharedBuild() \value WV_XP Windows XP (operating system version 5.1) \value WV_2003 Windows Server 2003, Windows Server 2003 R2, Windows Home Server, Windows XP Professional x64 Edition (operating system version 5.2) \value WV_VISTA Windows Vista, Windows Server 2008 (operating system version 6.0) + \value WV_WINDOWS7 Windows 7 (operating system version 6.1) Alternatively, you may use the following macros which correspond directly to the Windows operating system version number: @@ -1062,6 +1063,7 @@ bool qSharedBuild() \value WV_5_1 Operating system version 5.1, corresponds to Windows XP \value WV_5_2 Operating system version 5.2, corresponds to Windows Server 2003, Windows Server 2003 R2, Windows Home Server, and Windows XP Professional x64 Edition \value WV_6_0 Operating system version 6.0, corresponds to Windows Vista and Windows Server 2008 + \value WV_6_1 Operation system version 6.1, corresponds to Windows 7 CE-based versions: @@ -1632,7 +1634,10 @@ QSysInfo::WinVersion QSysInfo::windowsVersion() if (osver.dwMajorVersion < 5) { winver = QSysInfo::WV_NT; } else if (osver.dwMajorVersion == 6) { - winver = QSysInfo::WV_VISTA; + if (osver.dwMinorVersion == 0) + winver = QSysInfo::WV_VISTA; + else + winver = QSysInfo::WV_WINDOWS7; } else if (osver.dwMinorVersion == 0) { winver = QSysInfo::WV_2000; } else if (osver.dwMinorVersion == 1) { @@ -1667,6 +1672,8 @@ QSysInfo::WinVersion QSysInfo::windowsVersion() winver = QSysInfo::WV_XP; else if (override == "VISTA") winver = QSysInfo::WV_VISTA; + else if (override == "WINDOWS7") + winver = QSysInfo::WV_WINDOWS7; } #endif diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 9522e39..872dcf5 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -1309,6 +1309,7 @@ public: WV_XP = 0x0030, WV_2003 = 0x0040, WV_VISTA = 0x0080, + WV_WINDOWS7 = 0x0090, WV_NT_based = 0x00f0, /* version numbers */ @@ -1317,6 +1318,7 @@ public: WV_5_1 = WV_XP, WV_5_2 = WV_2003, WV_6_0 = WV_VISTA, + WV_6_1 = WV_WINDOWS7, WV_CE = 0x0100, WV_CENET = 0x0200, -- cgit v0.12 From 726694c873104ac484a3d09c1a9f64f06a88f864 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 23 Apr 2009 14:57:12 +0200 Subject: the start of a manual test for touch events --- tests/manual/qtouchevent/form.ui | 1843 ++++++++++++++++++++++++++++++ tests/manual/qtouchevent/main.cpp | 58 + tests/manual/qtouchevent/multitouch.pro | 5 + tests/manual/qtouchevent/touchwidget.cpp | 56 + tests/manual/qtouchevent/touchwidget.h | 42 + 5 files changed, 2004 insertions(+) create mode 100644 tests/manual/qtouchevent/form.ui create mode 100644 tests/manual/qtouchevent/main.cpp create mode 100644 tests/manual/qtouchevent/multitouch.pro create mode 100644 tests/manual/qtouchevent/touchwidget.cpp create mode 100644 tests/manual/qtouchevent/touchwidget.h diff --git a/tests/manual/qtouchevent/form.ui b/tests/manual/qtouchevent/form.ui new file mode 100644 index 0000000..00e15ae --- /dev/null +++ b/tests/manual/qtouchevent/form.ui @@ -0,0 +1,1843 @@ + + + Form + + + + 0 + 0 + 791 + 499 + + + + Form + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + Qt::Vertical + + + + 20 + 290 + + + + + + + + Qt::Horizontal + + + + 84 + 20 + + + + + + + + + 180 + 120 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 255 + + + + + + + 0 + 0 + 212 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 113 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 127 + 212 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 255 + + + + + + + 0 + 0 + 212 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 113 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 127 + 212 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 255 + + + + + + + 0 + 0 + 212 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 113 + + + + + + + 0 + 0 + 85 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 170 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + + Qt::Horizontal + + + + 84 + 20 + + + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 212 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 113 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 212 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 212 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 113 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 212 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 85 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 212 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 113 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 85 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + Qt::Vertical + + + + 20 + 290 + + + + + + + + Qt::Horizontal + + + + 84 + 20 + + + + + + + + + 180 + 120 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + + Qt::Horizontal + + + + 83 + 20 + + + + + + + + + + + + 75 + true + + + + Test Name + + + + + + + Test description + + + + + + + + TouchWidget + QWidget +
touchwidget.h
+ 1 +
+
+ + +
diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp new file mode 100644 index 0000000..c406543 --- /dev/null +++ b/tests/manual/qtouchevent/main.cpp @@ -0,0 +1,58 @@ +#include +#include + +#include "ui_form.h" +#include "touchwidget.h" + +class MultitouchTestWidget : public QWidget, public Ui::Form +{ + Q_OBJECT + +public: + MultitouchTestWidget(QWidget *parent = 0) + : QWidget(parent) + { + setupUi(this); + } +}; + +class tst_ManualMultitouch : public QObject +{ + Q_OBJECT + +public: + tst_ManualMultitouch(); + ~tst_ManualMultitouch(); + +private slots: + void touchBeginPropagation(); +}; + +tst_ManualMultitouch::tst_ManualMultitouch() +{ } + +tst_ManualMultitouch::~tst_ManualMultitouch() +{ } + +void tst_ManualMultitouch::touchBeginPropagation() +{ + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Touch event propagation"); + testWidget.testDescriptionLabel->setText("Touch, move, and release your finger over the green widget, the close this window."); + testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greenWidget->acceptTouchBegin = true; + testWidget.show(); + + (void) qApp->exec(); + + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); +} + +QTEST_MAIN(tst_ManualMultitouch) + +#include "main.moc" diff --git a/tests/manual/qtouchevent/multitouch.pro b/tests/manual/qtouchevent/multitouch.pro new file mode 100644 index 0000000..de1ee06 --- /dev/null +++ b/tests/manual/qtouchevent/multitouch.pro @@ -0,0 +1,5 @@ +QT += testlib +SOURCES = main.cpp \ + touchwidget.cpp +FORMS += form.ui +HEADERS += touchwidget.h diff --git a/tests/manual/qtouchevent/touchwidget.cpp b/tests/manual/qtouchevent/touchwidget.cpp new file mode 100644 index 0000000..5205504 --- /dev/null +++ b/tests/manual/qtouchevent/touchwidget.cpp @@ -0,0 +1,56 @@ +#include "touchwidget.h" + +#include +#include + +bool TouchWidget::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + seenTouchBegin = true; + if (acceptTouchBegin) { + event->accept(); + return true; + } + break; + case QEvent::TouchUpdate: + seenTouchUpdate = true; + if (acceptTouchUpdate) { + event->accept(); + return true; + } + break; + case QEvent::TouchEnd: + seenTouchEnd = true; + if (acceptTouchEnd) { + event->accept(); + return true; + } + break; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + seenMousePress = true; + if (acceptMousePress) { + event->accept(); + return true; + } + break; + case QEvent::MouseMove: + seenMouseMove = true; + if (acceptMouseMove) { + event->accept(); + return true; + } + break; + case QEvent::MouseButtonRelease: + seenMouseRelease = true; + if (acceptMouseRelease) { + event->accept(); + return true; + } + break; + default: + break; + } + return QWidget::event(event); +} diff --git a/tests/manual/qtouchevent/touchwidget.h b/tests/manual/qtouchevent/touchwidget.h new file mode 100644 index 0000000..f438a87 --- /dev/null +++ b/tests/manual/qtouchevent/touchwidget.h @@ -0,0 +1,42 @@ +#ifndef TOUCHWIDGET_H +#define TOUCHWIDGET_H + +#include + +class TouchWidget : public QWidget +{ + Q_OBJECT + +public: + bool acceptTouchBegin, acceptTouchUpdate, acceptTouchEnd; + bool seenTouchBegin, seenTouchUpdate, seenTouchEnd; + bool acceptMousePress, acceptMouseMove, acceptMouseRelease; + bool seenMousePress, seenMouseMove, seenMouseRelease; + + inline TouchWidget(QWidget *parent = 0) + : QWidget(parent) + { + reset(); + } + + void reset() + { + acceptTouchBegin + = acceptTouchUpdate + = acceptTouchEnd + = seenTouchBegin + = seenTouchUpdate + = seenTouchEnd + = acceptMousePress + = acceptMouseMove + = acceptMouseRelease + = seenMousePress + = seenMouseMove + = seenMouseRelease + = false; + } + + bool event(QEvent *event); +}; + +#endif // TOUCHWIDGET_H -- cgit v0.12 From 704261859297dae22f90be32ed0e4b675fd02ed3 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 23 Apr 2009 15:54:06 +0200 Subject: Initial test for TouchBegin propagation and TouchUpdate/TouchEnd handling --- tests/manual/qtouchevent/main.cpp | 54 ++++++++++++++++++++++++++++++-- tests/manual/qtouchevent/touchwidget.cpp | 26 +++++++++++++++ tests/manual/qtouchevent/touchwidget.h | 20 +++--------- 3 files changed, 82 insertions(+), 18 deletions(-) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index c406543..2aab4ca 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -38,13 +38,63 @@ void tst_ManualMultitouch::touchBeginPropagation() { MultitouchTestWidget testWidget; testWidget.testNameLabel->setText("Touch event propagation"); - testWidget.testDescriptionLabel->setText("Touch, move, and release your finger over the green widget, the close this window."); + testWidget.testDescriptionLabel->setText("Touch, move, and release your finger over the green widget."); testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greenWidget->acceptTouchBegin = true; - testWidget.show(); + testWidget.greenWidget->acceptTouchUpdate = true; + testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // again, ignoring the TouchEnd + testWidget.greenWidget->reset(); + testWidget.greenWidget->acceptTouchBegin = true; + testWidget.greenWidget->acceptTouchUpdate = true; + // testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // again, ignoring TouchUpdates + testWidget.greenWidget->reset(); + testWidget.greenWidget->acceptTouchBegin = true; + // testWidget.greenWidget->acceptTouchUpdate = true; + testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // last time, ignoring TouchUpdates and TouchEnd + testWidget.greenWidget->reset(); + testWidget.greenWidget->acceptTouchBegin = true; + // testWidget.greenWidget->acceptTouchUpdate = true; + // testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); diff --git a/tests/manual/qtouchevent/touchwidget.cpp b/tests/manual/qtouchevent/touchwidget.cpp index 5205504..54ca685 100644 --- a/tests/manual/qtouchevent/touchwidget.cpp +++ b/tests/manual/qtouchevent/touchwidget.cpp @@ -3,6 +3,28 @@ #include #include + +void TouchWidget::reset() +{ + acceptTouchBegin + = acceptTouchUpdate + = acceptTouchEnd + = seenTouchBegin + = seenTouchUpdate + = seenTouchEnd + = closeWindowOnTouchEnd + + = acceptMousePress + = acceptMouseMove + = acceptMouseRelease + = seenMousePress + = seenMouseMove + = seenMouseRelease + = closeWindowOnMouseRelease + + = false; +} + bool TouchWidget::event(QEvent *event) { switch (event->type()) { @@ -22,6 +44,8 @@ bool TouchWidget::event(QEvent *event) break; case QEvent::TouchEnd: seenTouchEnd = true; + if (closeWindowOnTouchEnd) + window()->close(); if (acceptTouchEnd) { event->accept(); return true; @@ -44,6 +68,8 @@ bool TouchWidget::event(QEvent *event) break; case QEvent::MouseButtonRelease: seenMouseRelease = true; + if (closeWindowOnMouseRelease) + window()->close(); if (acceptMouseRelease) { event->accept(); return true; diff --git a/tests/manual/qtouchevent/touchwidget.h b/tests/manual/qtouchevent/touchwidget.h index f438a87..3e95610 100644 --- a/tests/manual/qtouchevent/touchwidget.h +++ b/tests/manual/qtouchevent/touchwidget.h @@ -10,8 +10,11 @@ class TouchWidget : public QWidget public: bool acceptTouchBegin, acceptTouchUpdate, acceptTouchEnd; bool seenTouchBegin, seenTouchUpdate, seenTouchEnd; + bool closeWindowOnTouchEnd; + bool acceptMousePress, acceptMouseMove, acceptMouseRelease; bool seenMousePress, seenMouseMove, seenMouseRelease; + bool closeWindowOnMouseRelease; inline TouchWidget(QWidget *parent = 0) : QWidget(parent) @@ -19,22 +22,7 @@ public: reset(); } - void reset() - { - acceptTouchBegin - = acceptTouchUpdate - = acceptTouchEnd - = seenTouchBegin - = seenTouchUpdate - = seenTouchEnd - = acceptMousePress - = acceptMouseMove - = acceptMouseRelease - = seenMousePress - = seenMouseMove - = seenMouseRelease - = false; - } + void reset(); bool event(QEvent *event); }; -- cgit v0.12 From 66ed5fc90fe44554e852a6f8dca4548154da781c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 23 Apr 2009 17:54:17 +0200 Subject: update test to test basic event handling and touch begin propagation --- tests/manual/qtouchevent/form.ui | 572 +++++++++++++++++-------------- tests/manual/qtouchevent/main.cpp | 152 +++++++- tests/manual/qtouchevent/touchwidget.cpp | 7 +- 3 files changed, 460 insertions(+), 271 deletions(-) diff --git a/tests/manual/qtouchevent/form.ui b/tests/manual/qtouchevent/form.ui index 00e15ae..6cf4cba 100644 --- a/tests/manual/qtouchevent/form.ui +++ b/tests/manual/qtouchevent/form.ui @@ -6,16 +6,16 @@ 0 0 - 791 - 499 + 770 + 424 Form - - + + 0 @@ -37,8 +37,8 @@ - 0 - 170 + 170 + 0 0 @@ -46,8 +46,8 @@ - 0 - 255 + 255 + 0 0 @@ -55,8 +55,8 @@ - 0 - 212 + 212 + 0 0 @@ -64,8 +64,8 @@ - 0 - 85 + 85 + 0 0 @@ -73,8 +73,8 @@ - 0 - 113 + 113 + 0 0 @@ -118,8 +118,8 @@ - 0 - 170 + 170 + 0 0 @@ -136,8 +136,8 @@ - 127 - 212 + 212 + 127 127 @@ -174,8 +174,8 @@ - 0 - 170 + 170 + 0 0 @@ -183,8 +183,8 @@ - 0 - 255 + 255 + 0 0 @@ -192,8 +192,8 @@ - 0 - 212 + 212 + 0 0 @@ -201,8 +201,8 @@ - 0 - 85 + 85 + 0 0 @@ -210,8 +210,8 @@ - 0 - 113 + 113 + 0 0 @@ -255,8 +255,8 @@ - 0 - 170 + 170 + 0 0 @@ -273,8 +273,8 @@ - 127 - 212 + 212 + 127 127 @@ -302,8 +302,8 @@ - 0 - 85 + 85 + 0 0 @@ -311,8 +311,8 @@ - 0 - 170 + 170 + 0 0 @@ -320,8 +320,8 @@ - 0 - 255 + 255 + 0 0 @@ -329,8 +329,8 @@ - 0 - 212 + 212 + 0 0 @@ -338,8 +338,8 @@ - 0 - 85 + 85 + 0 0 @@ -347,8 +347,8 @@ - 0 - 113 + 113 + 0 0 @@ -356,8 +356,8 @@ - 0 - 85 + 85 + 0 0 @@ -374,8 +374,8 @@ - 0 - 85 + 85 + 0 0 @@ -383,8 +383,8 @@ - 0 - 170 + 170 + 0 0 @@ -392,8 +392,8 @@ - 0 - 170 + 170 + 0 0 @@ -410,8 +410,8 @@ - 0 - 170 + 170 + 0 0 @@ -440,35 +440,41 @@ true - + - + Qt::Vertical - 20 - 290 + 0 + 0 - + Qt::Horizontal - 84 - 20 + 0 + 0 - + + + + 0 + 0 + + 180 @@ -481,9 +487,9 @@ - 0 - 0 - 0 + 255 + 255 + 255 @@ -492,7 +498,7 @@ 0 0 - 170 + 0 @@ -501,7 +507,7 @@ 0 0 - 255 + 0 @@ -510,7 +516,7 @@ 0 0 - 212 + 0 @@ -519,7 +525,7 @@ 0 0 - 85 + 0 @@ -528,16 +534,16 @@ 0 0 - 113 + 0 - 0 - 0 - 0 + 255 + 255 + 255 @@ -553,18 +559,18 @@ - 0 - 0 - 0 + 255 + 255 + 255 - 255 - 255 - 255 + 0 + 0 + 0 @@ -573,7 +579,7 @@ 0 0 - 170 + 0 @@ -589,9 +595,9 @@ - 127 - 127 - 212 + 0 + 0 + 0 @@ -618,9 +624,9 @@ - 0 - 0 - 0 + 255 + 255 + 255 @@ -629,7 +635,7 @@ 0 0 - 170 + 0 @@ -638,7 +644,7 @@ 0 0 - 255 + 0 @@ -647,7 +653,7 @@ 0 0 - 212 + 0 @@ -656,7 +662,7 @@ 0 0 - 85 + 0 @@ -665,16 +671,16 @@ 0 0 - 113 + 0 - 0 - 0 - 0 + 255 + 255 + 255 @@ -690,18 +696,18 @@ - 0 - 0 - 0 + 255 + 255 + 255 - 255 - 255 - 255 + 0 + 0 + 0 @@ -710,7 +716,7 @@ 0 0 - 170 + 0 @@ -726,9 +732,9 @@ - 127 - 127 - 212 + 0 + 0 + 0 @@ -757,7 +763,7 @@ 0 0 - 85 + 0 @@ -766,7 +772,7 @@ 0 0 - 170 + 0 @@ -775,7 +781,7 @@ 0 0 - 255 + 0 @@ -784,7 +790,7 @@ 0 0 - 212 + 0 @@ -793,7 +799,7 @@ 0 0 - 85 + 0 @@ -802,7 +808,7 @@ 0 0 - 113 + 0 @@ -811,7 +817,7 @@ 0 0 - 85 + 0 @@ -829,7 +835,7 @@ 0 0 - 85 + 0 @@ -838,7 +844,7 @@ 0 0 - 170 + 0 @@ -847,7 +853,7 @@ 0 0 - 170 + 0 @@ -865,7 +871,7 @@ 0 0 - 170 + 0 @@ -896,14 +902,27 @@ - + Qt::Horizontal - 84 - 20 + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 @@ -911,8 +930,34 @@ - - + + + + + 16 + 75 + true + + + + Test Name + + + + + + + + 16 + + + + Test description + + + + + 0 @@ -934,8 +979,8 @@ - 170 - 0 + 0 + 170 0 @@ -943,8 +988,8 @@ - 255 - 0 + 0 + 255 0 @@ -952,8 +997,8 @@ - 212 - 0 + 0 + 212 0 @@ -961,8 +1006,8 @@ - 85 - 0 + 0 + 85 0 @@ -970,8 +1015,8 @@ - 113 - 0 + 0 + 113 0 @@ -1015,8 +1060,8 @@ - 170 - 0 + 0 + 170 0 @@ -1033,8 +1078,8 @@ - 212 - 127 + 127 + 212 127 @@ -1071,8 +1116,8 @@ - 170 - 0 + 0 + 170 0 @@ -1080,8 +1125,8 @@ - 255 - 0 + 0 + 255 0 @@ -1089,8 +1134,8 @@ - 212 - 0 + 0 + 212 0 @@ -1098,8 +1143,8 @@ - 85 - 0 + 0 + 85 0 @@ -1107,8 +1152,8 @@ - 113 - 0 + 0 + 113 0 @@ -1152,8 +1197,8 @@ - 170 - 0 + 0 + 170 0 @@ -1170,8 +1215,8 @@ - 212 - 127 + 127 + 212 127 @@ -1199,8 +1244,8 @@ - 85 - 0 + 0 + 85 0 @@ -1208,8 +1253,8 @@ - 170 - 0 + 0 + 170 0 @@ -1217,8 +1262,8 @@ - 255 - 0 + 0 + 255 0 @@ -1226,8 +1271,8 @@ - 212 - 0 + 0 + 212 0 @@ -1235,8 +1280,8 @@ - 85 - 0 + 0 + 85 0 @@ -1244,8 +1289,8 @@ - 113 - 0 + 0 + 113 0 @@ -1253,8 +1298,8 @@ - 85 - 0 + 0 + 85 0 @@ -1271,8 +1316,8 @@ - 85 - 0 + 0 + 85 0 @@ -1280,8 +1325,8 @@ - 170 - 0 + 0 + 170 0 @@ -1289,8 +1334,8 @@ - 170 - 0 + 0 + 170 0 @@ -1307,8 +1352,8 @@ - 170 - 0 + 0 + 170 0 @@ -1337,35 +1382,28 @@ true - - - - - Qt::Vertical - - - - 20 - 290 - - - - + - + Qt::Horizontal - 84 - 20 + 0 + 0 - + + + + 0 + 0 + + 180 @@ -1378,9 +1416,9 @@ - 255 - 255 - 255 + 0 + 0 + 0 @@ -1389,7 +1427,7 @@ 0 0 - 0 + 170 @@ -1398,7 +1436,7 @@ 0 0 - 0 + 255 @@ -1407,7 +1445,7 @@ 0 0 - 0 + 212 @@ -1416,7 +1454,7 @@ 0 0 - 0 + 85 @@ -1425,16 +1463,16 @@ 0 0 - 0 + 113 - 255 - 255 - 255 + 0 + 0 + 0 @@ -1450,18 +1488,18 @@ - 255 - 255 - 255 + 0 + 0 + 0 - 0 - 0 - 0 + 255 + 255 + 255 @@ -1470,7 +1508,7 @@ 0 0 - 0 + 170 @@ -1486,9 +1524,9 @@ - 0 - 0 - 0 + 127 + 127 + 212 @@ -1515,9 +1553,9 @@ - 255 - 255 - 255 + 0 + 0 + 0 @@ -1526,7 +1564,7 @@ 0 0 - 0 + 170 @@ -1535,7 +1573,7 @@ 0 0 - 0 + 255 @@ -1544,7 +1582,7 @@ 0 0 - 0 + 212 @@ -1553,7 +1591,7 @@ 0 0 - 0 + 85 @@ -1562,16 +1600,16 @@ 0 0 - 0 + 113 - 255 - 255 - 255 + 0 + 0 + 0 @@ -1587,18 +1625,18 @@ - 255 - 255 - 255 + 0 + 0 + 0 - 0 - 0 - 0 + 255 + 255 + 255 @@ -1607,7 +1645,7 @@ 0 0 - 0 + 170 @@ -1623,9 +1661,9 @@ - 0 - 0 - 0 + 127 + 127 + 212 @@ -1654,7 +1692,7 @@ 0 0 - 0 + 85 @@ -1663,7 +1701,7 @@ 0 0 - 0 + 170 @@ -1672,7 +1710,7 @@ 0 0 - 0 + 255 @@ -1681,7 +1719,7 @@ 0 0 - 0 + 212 @@ -1690,7 +1728,7 @@ 0 0 - 0 + 85 @@ -1699,7 +1737,7 @@ 0 0 - 0 + 113 @@ -1708,7 +1746,7 @@ 0 0 - 0 + 85 @@ -1726,7 +1764,7 @@ 0 0 - 0 + 85 @@ -1735,7 +1773,7 @@ 0 0 - 0 + 170 @@ -1744,7 +1782,7 @@ 0 0 - 0 + 170 @@ -1762,7 +1800,7 @@ 0 0 - 0 + 170 @@ -1793,14 +1831,40 @@ - + Qt::Horizontal - 83 - 20 + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 @@ -1808,26 +1872,6 @@ - - - - - 75 - true - - - - Test Name - - - - - - - Test description - - - diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index 2aab4ca..7ed9b15 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -12,8 +12,15 @@ public: MultitouchTestWidget(QWidget *parent = 0) : QWidget(parent) { + setAttribute(Qt::WA_QuitOnClose, false);\ setupUi(this); } + + void closeEvent(QCloseEvent *event) + { + event->accept(); + QTimer::singleShot(1000, qApp, SLOT(quit())); + } }; class tst_ManualMultitouch : public QObject @@ -25,7 +32,8 @@ public: ~tst_ManualMultitouch(); private slots: - void touchBeginPropagation(); + void basicEventHandling(); + void touchEventPropagation(); }; tst_ManualMultitouch::tst_ManualMultitouch() @@ -34,11 +42,99 @@ tst_ManualMultitouch::tst_ManualMultitouch() tst_ManualMultitouch::~tst_ManualMultitouch() { } -void tst_ManualMultitouch::touchBeginPropagation() +void tst_ManualMultitouch::basicEventHandling() +{ + // first, make sure that we get mouse events when not enabling touch events + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Basic QTouchEvent handling test"); + testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the green widget."); + testWidget.redWidget->hide(); + testWidget.blueWidget->hide(); + testWidget.greenWidget->closeWindowOnMouseRelease = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(!testWidget.greenWidget->seenTouchBegin); + QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + QVERIFY(!testWidget.greenWidget->seenTouchEnd); + QVERIFY(testWidget.greenWidget->seenMousePress); + // QVERIFY(testWidget.greenWidget->seenMouseMove); + QVERIFY(testWidget.greenWidget->seenMouseRelease); + + // now enable touch and make sure we get the touch events + testWidget.greenWidget->reset(); + testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greenWidget->acceptTouchBegin = true; + testWidget.greenWidget->acceptTouchUpdate = true; + testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin + && testWidget.greenWidget->seenTouchUpdate + && testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress + && !testWidget.greenWidget->seenMouseMove + && !testWidget.greenWidget->seenMouseRelease); + + // again, ignoring the TouchEnd + testWidget.greenWidget->reset(); + testWidget.greenWidget->acceptTouchBegin = true; + testWidget.greenWidget->acceptTouchUpdate = true; + // testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // again, ignoring TouchUpdates + testWidget.greenWidget->reset(); + testWidget.greenWidget->acceptTouchBegin = true; + // testWidget.greenWidget->acceptTouchUpdate = true; + testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // last time, ignoring TouchUpdates and TouchEnd + testWidget.greenWidget->reset(); + testWidget.greenWidget->acceptTouchBegin = true; + // testWidget.greenWidget->acceptTouchUpdate = true; + // testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); +} + +void tst_ManualMultitouch::touchEventPropagation() { + // first, make sure the greenWidget gets TouchBegin even though blueWidget is not touch aware MultitouchTestWidget testWidget; - testWidget.testNameLabel->setText("Touch event propagation"); - testWidget.testDescriptionLabel->setText("Touch, move, and release your finger over the green widget."); + testWidget.testNameLabel->setText("QTouchEvent propagation test"); + testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the blue widget."); + testWidget.redWidget->hide(); + // testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greenWidget->acceptTouchBegin = true; testWidget.greenWidget->acceptTouchUpdate = true; @@ -47,6 +143,36 @@ void tst_ManualMultitouch::touchBeginPropagation() testWidget.showMaximized(); (void) qApp->exec(); + QVERIFY(!testWidget.blueWidget->seenTouchBegin); + QVERIFY(!testWidget.blueWidget->seenTouchUpdate); + QVERIFY(!testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // again, but this time blueWidget should see the TouchBegin + testWidget.blueWidget->reset(); + testWidget.greenWidget->reset(); + testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greenWidget->acceptTouchBegin = true; + testWidget.greenWidget->acceptTouchUpdate = true; + testWidget.greenWidget->acceptTouchEnd = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(!testWidget.blueWidget->seenTouchUpdate); + QVERIFY(!testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); @@ -63,6 +189,12 @@ void tst_ManualMultitouch::touchBeginPropagation() testWidget.showMaximized(); (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(!testWidget.blueWidget->seenTouchUpdate); + QVERIFY(!testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); @@ -79,6 +211,12 @@ void tst_ManualMultitouch::touchBeginPropagation() testWidget.showMaximized(); (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(!testWidget.blueWidget->seenTouchUpdate); + QVERIFY(!testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); @@ -95,6 +233,12 @@ void tst_ManualMultitouch::touchBeginPropagation() testWidget.showMaximized(); (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(!testWidget.blueWidget->seenTouchUpdate); + QVERIFY(!testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); diff --git a/tests/manual/qtouchevent/touchwidget.cpp b/tests/manual/qtouchevent/touchwidget.cpp index 54ca685..1e57c36 100644 --- a/tests/manual/qtouchevent/touchwidget.cpp +++ b/tests/manual/qtouchevent/touchwidget.cpp @@ -1,9 +1,10 @@ #include "touchwidget.h" -#include +#include +#include +#include #include - void TouchWidget::reset() { acceptTouchBegin @@ -69,7 +70,7 @@ bool TouchWidget::event(QEvent *event) case QEvent::MouseButtonRelease: seenMouseRelease = true; if (closeWindowOnMouseRelease) - window()->close(); + window()->close(); if (acceptMouseRelease) { event->accept(); return true; -- cgit v0.12 From ec2d71f0e4af0e0c286f0027d0242f456fdb2bb6 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 23 Apr 2009 17:57:46 +0200 Subject: Fix tests/manual/qtouchevent by choking mouse events even for ignored TouchUpdate and TouchEnd events. The behavior we want is that no mouse events are sent if the widget accepts the TouchBegin. --- src/gui/kernel/qapplication_p.h | 1 + src/gui/kernel/qapplication_win.cpp | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index ce7110d..91857ec 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -429,6 +429,7 @@ public: #endif QPointer currentMultitouchWidget; + bool currentMultitouchWidgetAcceptedTouchBegin; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); #if defined(Q_WS_WIN) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 4878ea7..5a4efd6 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4003,6 +4003,7 @@ void QApplicationPrivate::initializeMultitouch() CloseTouchInputHandle = static_cast(library.resolve("CloseTouchInputHandle")); currentMultitouchWidget = 0; + currentMultitouchWidgetAcceptedTouchBegin = false; touchInputIDToTouchPointID.clear(); allTouchPoints.clear(); currentTouchPoints.clear(); @@ -4106,6 +4107,9 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) if (!child) child = window; currentMultitouchWidget = child; + // if the TouchBegin handler recurses, we assume that means the event + // has been implicitly accepted and continue to send touch events + currentMultitouchWidgetAcceptedTouchBegin = true; } } @@ -4131,8 +4135,19 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) q->keyboardModifiers(), activeTouchPoints); updateTouchPointsForWidget(widget, &touchEvent); - bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent); - return (qt_tabletChokeMouse = res && touchEvent.isAccepted()); + if (sendTouchBegin) { + bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent); + qt_tabletChokeMouse + = currentMultitouchWidgetAcceptedTouchBegin + = (res && touchEvent.isAccepted()); + } else if (currentMultitouchWidgetAcceptedTouchBegin) { + (void) QApplication::sendSpontaneousEvent(widget, &touchEvent); + qt_tabletChokeMouse = true; + } else { + qt_tabletChokeMouse = false; + } + + return qt_tabletChokeMouse; } return false; -- cgit v0.12 From 34788b2cb7af9e515fe9de5c4dc81744dda68d1e Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 24 Apr 2009 12:41:44 +0200 Subject: Test that accepting the TouchBegin stops propagation --- tests/manual/qtouchevent/main.cpp | 106 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index 7ed9b15..a66a546 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -129,13 +129,110 @@ void tst_ManualMultitouch::basicEventHandling() void tst_ManualMultitouch::touchEventPropagation() { - // first, make sure the greenWidget gets TouchBegin even though blueWidget is not touch aware MultitouchTestWidget testWidget; testWidget.testNameLabel->setText("QTouchEvent propagation test"); testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the blue widget."); - testWidget.redWidget->hide(); - // testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.redWidget->hide(); + + // test that accepting the TouchBegin event on the + // blueWidget stops propagation to the greenWidget + testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.blueWidget->acceptTouchBegin = true; + testWidget.blueWidget->acceptTouchUpdate = true; + testWidget.blueWidget->acceptTouchEnd = true; + testWidget.blueWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(testWidget.blueWidget->seenTouchUpdate); + QVERIFY(testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); + QVERIFY(!testWidget.greenWidget->seenTouchBegin); + QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + QVERIFY(!testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // ignoring TouchEnd + testWidget.blueWidget->reset(); + testWidget.greenWidget->reset(); + testWidget.blueWidget->acceptTouchBegin = true; + testWidget.blueWidget->acceptTouchUpdate = true; + // testWidget.blueWidget->acceptTouchEnd = true; + testWidget.blueWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(testWidget.blueWidget->seenTouchUpdate); + QVERIFY(testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); + QVERIFY(!testWidget.greenWidget->seenTouchBegin); + QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + QVERIFY(!testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // ignoring TouchUpdate + testWidget.blueWidget->reset(); + testWidget.greenWidget->reset(); + testWidget.blueWidget->acceptTouchBegin = true; + // testWidget.blueWidget->acceptTouchUpdate = true; + testWidget.blueWidget->acceptTouchEnd = true; + testWidget.blueWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(testWidget.blueWidget->seenTouchUpdate); + QVERIFY(testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); + QVERIFY(!testWidget.greenWidget->seenTouchBegin); + QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + QVERIFY(!testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // ignoring TouchUpdate and TouchEnd + testWidget.blueWidget->reset(); + testWidget.greenWidget->reset(); + testWidget.blueWidget->acceptTouchBegin = true; + // testWidget.blueWidget->acceptTouchUpdate = true; + // testWidget.blueWidget->acceptTouchEnd = true; + testWidget.blueWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + + (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(testWidget.blueWidget->seenTouchUpdate); + QVERIFY(testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); + QVERIFY(!testWidget.greenWidget->seenTouchBegin); + QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + QVERIFY(!testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + // repeat the test above, now ignoring touch events in the + // blueWidget, they should be propagated to the greenWidget + testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents, false); + testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.blueWidget->reset(); + testWidget.greenWidget->reset(); testWidget.greenWidget->acceptTouchBegin = true; testWidget.greenWidget->acceptTouchUpdate = true; testWidget.greenWidget->acceptTouchEnd = true; @@ -181,6 +278,7 @@ void tst_ManualMultitouch::touchEventPropagation() QVERIFY(!testWidget.greenWidget->seenMouseRelease); // again, ignoring the TouchEnd + testWidget.blueWidget->reset(); testWidget.greenWidget->reset(); testWidget.greenWidget->acceptTouchBegin = true; testWidget.greenWidget->acceptTouchUpdate = true; @@ -203,6 +301,7 @@ void tst_ManualMultitouch::touchEventPropagation() QVERIFY(!testWidget.greenWidget->seenMouseRelease); // again, ignoring TouchUpdates + testWidget.blueWidget->reset(); testWidget.greenWidget->reset(); testWidget.greenWidget->acceptTouchBegin = true; // testWidget.greenWidget->acceptTouchUpdate = true; @@ -225,6 +324,7 @@ void tst_ManualMultitouch::touchEventPropagation() QVERIFY(!testWidget.greenWidget->seenMouseRelease); // last time, ignoring TouchUpdates and TouchEnd + testWidget.blueWidget->reset(); testWidget.greenWidget->reset(); testWidget.greenWidget->acceptTouchBegin = true; // testWidget.greenWidget->acceptTouchUpdate = true; -- cgit v0.12 From 76b3d0526b524b79603e4e1b337c32f3bfcc6340 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 24 Apr 2009 12:55:48 +0200 Subject: change the layout a bit in the test's form --- tests/manual/qtouchevent/form.ui | 3672 +++++++++++++++++++------------------- 1 file changed, 1838 insertions(+), 1834 deletions(-) diff --git a/tests/manual/qtouchevent/form.ui b/tests/manual/qtouchevent/form.ui index 6cf4cba..4c2da3a 100644 --- a/tests/manual/qtouchevent/form.ui +++ b/tests/manual/qtouchevent/form.ui @@ -13,924 +13,8 @@ Form - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 212 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 113 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 170 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 212 - 127 - 127 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 212 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 113 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 170 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 212 - 127 - 127 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 85 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 212 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 113 - 0 - 0 - - - - - - - 85 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 85 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 170 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - 180 - 120 - - - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - + + @@ -944,7 +28,7 @@ - + @@ -956,921 +40,1841 @@ - - - - - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 170 - 0 - - - - - - - 0 - 255 - 0 - - - - - - - 0 - 212 - 0 - - - - - - - 0 - 85 - 0 - - - - - - - 0 - 113 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 170 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 127 - 212 - 127 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 170 - 0 - - - - - - - 0 - 255 - 0 - - - - - - - 0 - 212 - 0 - - - - - - - 0 - 85 - 0 - - - - - - - 0 - 113 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 170 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 127 - 212 - 127 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 85 - 0 - - - - - - - 0 - 170 - 0 - - - - - - - 0 - 255 - 0 - - - - - - - 0 - 212 - 0 - - - - - - - 0 - 85 - 0 - - - - - - - 0 - 113 - 0 - - - - - - - 0 - 85 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 85 - 0 - - - - - - - 0 - 170 - 0 - - - - - - - 0 - 170 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 170 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - 180 - 120 - - - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 170 - - - - - - - 0 - 0 - 255 - - - - - - - 0 - 0 - 212 - - - - - - - 0 - 0 - 85 - - - - - - - 0 - 0 - 113 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 170 - - - - - - - 0 - 0 - 0 - - - - - - - 127 - 127 - 212 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 170 - - - - - - - 0 - 0 - 255 - - - - - - - 0 - 0 - 212 - - - - - - - 0 - 0 - 85 - - - - - - - 0 - 0 - 113 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 170 - - - - - - - 0 - 0 - 0 - - - - - - - 127 - 127 - 212 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 85 - - - - - - - 0 - 0 - 170 - - - - - - - 0 - 0 - 255 - - - - - - - 0 - 0 - 212 - - - - - - - 0 - 0 - 85 - - - - - - - 0 - 0 - 113 - - - - - - - 0 - 0 - 85 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 85 - - - - - - - 0 - 0 - 170 - - - - - - - 0 - 0 - 170 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 170 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 180 + 120 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 255 + + + + + + + 0 + 0 + 212 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 113 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 127 + 212 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 255 + + + + + + + 0 + 0 + 212 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 113 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 127 + 212 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 255 + + + + + + + 0 + 0 + 212 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 113 + + + + + + + 0 + 0 + 85 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 85 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 170 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 212 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 113 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 212 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 212 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 113 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 212 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 85 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 212 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 113 + 0 + 0 + + + + + + + 85 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 85 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 180 + 120 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + -- cgit v0.12 From ed2e1e64d922e3e531984d099a800515037c89f6 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 24 Apr 2009 13:00:31 +0200 Subject: Use a different widget color to indicate that we are testing different things --- tests/manual/qtouchevent/main.cpp | 196 +++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 96 deletions(-) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index a66a546..05fdc44 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -130,7 +130,7 @@ void tst_ManualMultitouch::basicEventHandling() void tst_ManualMultitouch::touchEventPropagation() { MultitouchTestWidget testWidget; - testWidget.testNameLabel->setText("QTouchEvent propagation test"); + testWidget.testNameLabel->setText("QTouchEvent propagation test, Blue blocks Green"); testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the blue widget."); testWidget.redWidget->hide(); @@ -228,123 +228,127 @@ void tst_ManualMultitouch::touchEventPropagation() QVERIFY(!testWidget.greenWidget->seenMouseRelease); // repeat the test above, now ignoring touch events in the - // blueWidget, they should be propagated to the greenWidget - testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents, false); - testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); - testWidget.blueWidget->reset(); - testWidget.greenWidget->reset(); - testWidget.greenWidget->acceptTouchBegin = true; - testWidget.greenWidget->acceptTouchUpdate = true; - testWidget.greenWidget->acceptTouchEnd = true; - testWidget.greenWidget->closeWindowOnTouchEnd = true; + // greyWidget, they should be propagated to the redWidget + testWidget.testNameLabel->setText("QTouchEvent propagation test, Red handles Grey's ignored events"); + testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the grey widget."); + testWidget.greenWidget->hide(); + testWidget.redWidget->show(); + testWidget.greyWidget->setAttribute(Qt::WA_AcceptTouchEvents, false); + testWidget.redWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greyWidget->reset(); + testWidget.redWidget->reset(); + testWidget.redWidget->acceptTouchBegin = true; + testWidget.redWidget->acceptTouchUpdate = true; + testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); (void) qApp->exec(); - QVERIFY(!testWidget.blueWidget->seenTouchBegin); - QVERIFY(!testWidget.blueWidget->seenTouchUpdate); - QVERIFY(!testWidget.blueWidget->seenTouchEnd); - QVERIFY(!testWidget.blueWidget->seenMousePress); - QVERIFY(!testWidget.blueWidget->seenMouseMove); - QVERIFY(!testWidget.blueWidget->seenMouseRelease); - QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); - QVERIFY(testWidget.greenWidget->seenTouchEnd); - QVERIFY(!testWidget.greenWidget->seenMousePress); - QVERIFY(!testWidget.greenWidget->seenMouseMove); - QVERIFY(!testWidget.greenWidget->seenMouseRelease); - - // again, but this time blueWidget should see the TouchBegin - testWidget.blueWidget->reset(); - testWidget.greenWidget->reset(); - testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); - testWidget.greenWidget->acceptTouchBegin = true; - testWidget.greenWidget->acceptTouchUpdate = true; - testWidget.greenWidget->acceptTouchEnd = true; - testWidget.greenWidget->closeWindowOnTouchEnd = true; + QVERIFY(!testWidget.greyWidget->seenTouchBegin); + QVERIFY(!testWidget.greyWidget->seenTouchUpdate); + QVERIFY(!testWidget.greyWidget->seenTouchEnd); + QVERIFY(!testWidget.greyWidget->seenMousePress); + QVERIFY(!testWidget.greyWidget->seenMouseMove); + QVERIFY(!testWidget.greyWidget->seenMouseRelease); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(!testWidget.redWidget->seenMousePress); + QVERIFY(!testWidget.redWidget->seenMouseMove); + QVERIFY(!testWidget.redWidget->seenMouseRelease); + + // again, but this time greyWidget should see the TouchBegin + testWidget.greyWidget->reset(); + testWidget.redWidget->reset(); + testWidget.greyWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.redWidget->acceptTouchBegin = true; + testWidget.redWidget->acceptTouchUpdate = true; + testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); (void) qApp->exec(); - QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(!testWidget.blueWidget->seenTouchUpdate); - QVERIFY(!testWidget.blueWidget->seenTouchEnd); - QVERIFY(!testWidget.blueWidget->seenMousePress); - QVERIFY(!testWidget.blueWidget->seenMouseMove); - QVERIFY(!testWidget.blueWidget->seenMouseRelease); - QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); - QVERIFY(testWidget.greenWidget->seenTouchEnd); - QVERIFY(!testWidget.greenWidget->seenMousePress); - QVERIFY(!testWidget.greenWidget->seenMouseMove); - QVERIFY(!testWidget.greenWidget->seenMouseRelease); + QVERIFY(testWidget.greyWidget->seenTouchBegin); + QVERIFY(!testWidget.greyWidget->seenTouchUpdate); + QVERIFY(!testWidget.greyWidget->seenTouchEnd); + QVERIFY(!testWidget.greyWidget->seenMousePress); + QVERIFY(!testWidget.greyWidget->seenMouseMove); + QVERIFY(!testWidget.greyWidget->seenMouseRelease); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(!testWidget.redWidget->seenMousePress); + QVERIFY(!testWidget.redWidget->seenMouseMove); + QVERIFY(!testWidget.redWidget->seenMouseRelease); // again, ignoring the TouchEnd - testWidget.blueWidget->reset(); - testWidget.greenWidget->reset(); - testWidget.greenWidget->acceptTouchBegin = true; - testWidget.greenWidget->acceptTouchUpdate = true; - // testWidget.greenWidget->acceptTouchEnd = true; - testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.greyWidget->reset(); + testWidget.redWidget->reset(); + testWidget.redWidget->acceptTouchBegin = true; + testWidget.redWidget->acceptTouchUpdate = true; + // testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); (void) qApp->exec(); - QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(!testWidget.blueWidget->seenTouchUpdate); - QVERIFY(!testWidget.blueWidget->seenTouchEnd); - QVERIFY(!testWidget.blueWidget->seenMousePress); - QVERIFY(!testWidget.blueWidget->seenMouseMove); - QVERIFY(!testWidget.blueWidget->seenMouseRelease); - QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); - QVERIFY(testWidget.greenWidget->seenTouchEnd); - QVERIFY(!testWidget.greenWidget->seenMousePress); - QVERIFY(!testWidget.greenWidget->seenMouseMove); - QVERIFY(!testWidget.greenWidget->seenMouseRelease); + QVERIFY(testWidget.greyWidget->seenTouchBegin); + QVERIFY(!testWidget.greyWidget->seenTouchUpdate); + QVERIFY(!testWidget.greyWidget->seenTouchEnd); + QVERIFY(!testWidget.greyWidget->seenMousePress); + QVERIFY(!testWidget.greyWidget->seenMouseMove); + QVERIFY(!testWidget.greyWidget->seenMouseRelease); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(!testWidget.redWidget->seenMousePress); + QVERIFY(!testWidget.redWidget->seenMouseMove); + QVERIFY(!testWidget.redWidget->seenMouseRelease); // again, ignoring TouchUpdates - testWidget.blueWidget->reset(); - testWidget.greenWidget->reset(); - testWidget.greenWidget->acceptTouchBegin = true; - // testWidget.greenWidget->acceptTouchUpdate = true; - testWidget.greenWidget->acceptTouchEnd = true; - testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.greyWidget->reset(); + testWidget.redWidget->reset(); + testWidget.redWidget->acceptTouchBegin = true; + // testWidget.redWidget->acceptTouchUpdate = true; + testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); (void) qApp->exec(); - QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(!testWidget.blueWidget->seenTouchUpdate); - QVERIFY(!testWidget.blueWidget->seenTouchEnd); - QVERIFY(!testWidget.blueWidget->seenMousePress); - QVERIFY(!testWidget.blueWidget->seenMouseMove); - QVERIFY(!testWidget.blueWidget->seenMouseRelease); - QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); - QVERIFY(testWidget.greenWidget->seenTouchEnd); - QVERIFY(!testWidget.greenWidget->seenMousePress); - QVERIFY(!testWidget.greenWidget->seenMouseMove); - QVERIFY(!testWidget.greenWidget->seenMouseRelease); + QVERIFY(testWidget.greyWidget->seenTouchBegin); + QVERIFY(!testWidget.greyWidget->seenTouchUpdate); + QVERIFY(!testWidget.greyWidget->seenTouchEnd); + QVERIFY(!testWidget.greyWidget->seenMousePress); + QVERIFY(!testWidget.greyWidget->seenMouseMove); + QVERIFY(!testWidget.greyWidget->seenMouseRelease); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(!testWidget.redWidget->seenMousePress); + QVERIFY(!testWidget.redWidget->seenMouseMove); + QVERIFY(!testWidget.redWidget->seenMouseRelease); // last time, ignoring TouchUpdates and TouchEnd - testWidget.blueWidget->reset(); - testWidget.greenWidget->reset(); - testWidget.greenWidget->acceptTouchBegin = true; - // testWidget.greenWidget->acceptTouchUpdate = true; - // testWidget.greenWidget->acceptTouchEnd = true; - testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.greyWidget->reset(); + testWidget.redWidget->reset(); + testWidget.redWidget->acceptTouchBegin = true; + // testWidget.redWidget->acceptTouchUpdate = true; + // testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); (void) qApp->exec(); - QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(!testWidget.blueWidget->seenTouchUpdate); - QVERIFY(!testWidget.blueWidget->seenTouchEnd); - QVERIFY(!testWidget.blueWidget->seenMousePress); - QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(testWidget.greyWidget->seenTouchBegin); + QVERIFY(!testWidget.greyWidget->seenTouchUpdate); + QVERIFY(!testWidget.greyWidget->seenTouchEnd); + QVERIFY(!testWidget.greyWidget->seenMousePress); + QVERIFY(!testWidget.greyWidget->seenMouseMove); QVERIFY(!testWidget.blueWidget->seenMouseRelease); - QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); - QVERIFY(testWidget.greenWidget->seenTouchEnd); - QVERIFY(!testWidget.greenWidget->seenMousePress); - QVERIFY(!testWidget.greenWidget->seenMouseMove); - QVERIFY(!testWidget.greenWidget->seenMouseRelease); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(!testWidget.redWidget->seenMousePress); + QVERIFY(!testWidget.redWidget->seenMouseMove); + QVERIFY(!testWidget.redWidget->seenMouseRelease); } QTEST_MAIN(tst_ManualMultitouch) -- cgit v0.12 From 96d2094b48db6cccd7a0fb34a778bd3e71ba43b4 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 27 Apr 2009 13:21:12 +0200 Subject: add basic multi-touch event handling test, split existing tests into more logical pieces --- tests/manual/qtouchevent/main.cpp | 130 +++++++++++++++++++++++++------ tests/manual/qtouchevent/touchwidget.cpp | 4 + tests/manual/qtouchevent/touchwidget.h | 1 + 3 files changed, 110 insertions(+), 25 deletions(-) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index 05fdc44..8b28967 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -32,8 +32,11 @@ public: ~tst_ManualMultitouch(); private slots: - void basicEventHandling(); - void touchEventPropagation(); + void ignoringTouchEventsEmulatesMouseEvents(); + void basicSingleTouchEventHandling(); + void basicMultiTouchEventHandling(); + void acceptingTouchBeginStopsPropagation(); + void ignoringTouchBeginPropagatesToParent(); }; tst_ManualMultitouch::tst_ManualMultitouch() @@ -42,17 +45,16 @@ tst_ManualMultitouch::tst_ManualMultitouch() tst_ManualMultitouch::~tst_ManualMultitouch() { } -void tst_ManualMultitouch::basicEventHandling() +void tst_ManualMultitouch::ignoringTouchEventsEmulatesMouseEvents() { // first, make sure that we get mouse events when not enabling touch events MultitouchTestWidget testWidget; - testWidget.testNameLabel->setText("Basic QTouchEvent handling test"); + testWidget.testNameLabel->setText("Mouse Event Emulation Test"); testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the green widget."); testWidget.redWidget->hide(); testWidget.blueWidget->hide(); testWidget.greenWidget->closeWindowOnMouseRelease = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(!testWidget.greenWidget->seenTouchBegin); QVERIFY(!testWidget.greenWidget->seenTouchUpdate); @@ -61,15 +63,34 @@ void tst_ManualMultitouch::basicEventHandling() // QVERIFY(testWidget.greenWidget->seenMouseMove); QVERIFY(testWidget.greenWidget->seenMouseRelease); - // now enable touch and make sure we get the touch events + // enable touch, but don't accept the events testWidget.greenWidget->reset(); testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greenWidget->closeWindowOnMouseRelease = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + QVERIFY(!testWidget.greenWidget->seenTouchEnd); + QVERIFY(testWidget.greenWidget->seenMousePress); + // QVERIFY(testWidget.greenWidget->seenMouseMove); + QVERIFY(testWidget.greenWidget->seenMouseRelease); +} + +void tst_ManualMultitouch::basicSingleTouchEventHandling() +{ + // now enable touch and make sure we get the touch events + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Basic Single-Touch Event Handling Test"); + testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the green widget."); + testWidget.redWidget->hide(); + testWidget.blueWidget->hide(); + testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greenWidget->acceptTouchBegin = true; testWidget.greenWidget->acceptTouchUpdate = true; testWidget.greenWidget->acceptTouchEnd = true; testWidget.greenWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin && testWidget.greenWidget->seenTouchUpdate @@ -85,7 +106,6 @@ void tst_ManualMultitouch::basicEventHandling() // testWidget.greenWidget->acceptTouchEnd = true; testWidget.greenWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); @@ -101,7 +121,6 @@ void tst_ManualMultitouch::basicEventHandling() testWidget.greenWidget->acceptTouchEnd = true; testWidget.greenWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); @@ -117,7 +136,6 @@ void tst_ManualMultitouch::basicEventHandling() // testWidget.greenWidget->acceptTouchEnd = true; testWidget.greenWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); QVERIFY(testWidget.greenWidget->seenTouchUpdate); @@ -127,15 +145,83 @@ void tst_ManualMultitouch::basicEventHandling() QVERIFY(!testWidget.greenWidget->seenMouseRelease); } -void tst_ManualMultitouch::touchEventPropagation() +void tst_ManualMultitouch::basicMultiTouchEventHandling() { + // repeat, this time looking for multiple fingers MultitouchTestWidget testWidget; - testWidget.testNameLabel->setText("QTouchEvent propagation test, Blue blocks Green"); - testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the blue widget."); - testWidget.redWidget->hide(); + testWidget.testNameLabel->setText("Basic Multi-Touch Event Handling Test"); + testWidget.testDescriptionLabel->setText("Touch, hold, and release several fingers on the red widget."); + testWidget.greenWidget->hide(); + testWidget.greyWidget->hide(); + testWidget.redWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.redWidget->acceptTouchBegin = true; + testWidget.redWidget->acceptTouchUpdate = true; + testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(testWidget.redWidget->touchPointCount > 1); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + testWidget.redWidget->reset(); + testWidget.redWidget->acceptTouchBegin = true; + // testWidget.redWidget->acceptTouchUpdate = true; + testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(testWidget.redWidget->touchPointCount > 1); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + testWidget.redWidget->reset(); + testWidget.redWidget->acceptTouchBegin = true; + testWidget.redWidget->acceptTouchUpdate = true; + // testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(testWidget.redWidget->touchPointCount > 1); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + + testWidget.redWidget->reset(); + testWidget.redWidget->acceptTouchBegin = true; + // testWidget.redWidget->acceptTouchUpdate = true; + // testWidget.redWidget->acceptTouchEnd = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(testWidget.redWidget->touchPointCount > 1); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); +} + +void tst_ManualMultitouch::acceptingTouchBeginStopsPropagation() +{ // test that accepting the TouchBegin event on the // blueWidget stops propagation to the greenWidget + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Touch Event Propagation Test: Accepting in Blue Blocks Green"); + testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the blue widget."); + testWidget.redWidget->hide(); testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.blueWidget->acceptTouchBegin = true; @@ -143,7 +229,6 @@ void tst_ManualMultitouch::touchEventPropagation() testWidget.blueWidget->acceptTouchEnd = true; testWidget.blueWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); QVERIFY(testWidget.blueWidget->seenTouchUpdate); @@ -166,7 +251,6 @@ void tst_ManualMultitouch::touchEventPropagation() // testWidget.blueWidget->acceptTouchEnd = true; testWidget.blueWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); QVERIFY(testWidget.blueWidget->seenTouchUpdate); @@ -189,7 +273,6 @@ void tst_ManualMultitouch::touchEventPropagation() testWidget.blueWidget->acceptTouchEnd = true; testWidget.blueWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); QVERIFY(testWidget.blueWidget->seenTouchUpdate); @@ -212,7 +295,6 @@ void tst_ManualMultitouch::touchEventPropagation() // testWidget.blueWidget->acceptTouchEnd = true; testWidget.blueWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); QVERIFY(testWidget.blueWidget->seenTouchUpdate); @@ -226,13 +308,16 @@ void tst_ManualMultitouch::touchEventPropagation() QVERIFY(!testWidget.greenWidget->seenMousePress); QVERIFY(!testWidget.greenWidget->seenMouseMove); QVERIFY(!testWidget.greenWidget->seenMouseRelease); +} +void tst_ManualMultitouch::ignoringTouchBeginPropagatesToParent() +{ // repeat the test above, now ignoring touch events in the // greyWidget, they should be propagated to the redWidget - testWidget.testNameLabel->setText("QTouchEvent propagation test, Red handles Grey's ignored events"); + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Touch Event Propagation Test: Ignoring in Grey Propagates to Red"); testWidget.testDescriptionLabel->setText("Touch, hold, and release your finger on the grey widget."); testWidget.greenWidget->hide(); - testWidget.redWidget->show(); testWidget.greyWidget->setAttribute(Qt::WA_AcceptTouchEvents, false); testWidget.redWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greyWidget->reset(); @@ -242,7 +327,6 @@ void tst_ManualMultitouch::touchEventPropagation() testWidget.redWidget->acceptTouchEnd = true; testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(!testWidget.greyWidget->seenTouchBegin); QVERIFY(!testWidget.greyWidget->seenTouchUpdate); @@ -266,7 +350,6 @@ void tst_ManualMultitouch::touchEventPropagation() testWidget.redWidget->acceptTouchEnd = true; testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greyWidget->seenTouchBegin); QVERIFY(!testWidget.greyWidget->seenTouchUpdate); @@ -289,7 +372,6 @@ void tst_ManualMultitouch::touchEventPropagation() // testWidget.redWidget->acceptTouchEnd = true; testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greyWidget->seenTouchBegin); QVERIFY(!testWidget.greyWidget->seenTouchUpdate); @@ -312,7 +394,6 @@ void tst_ManualMultitouch::touchEventPropagation() testWidget.redWidget->acceptTouchEnd = true; testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greyWidget->seenTouchBegin); QVERIFY(!testWidget.greyWidget->seenTouchUpdate); @@ -335,7 +416,6 @@ void tst_ManualMultitouch::touchEventPropagation() // testWidget.redWidget->acceptTouchEnd = true; testWidget.redWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); - (void) qApp->exec(); QVERIFY(testWidget.greyWidget->seenTouchBegin); QVERIFY(!testWidget.greyWidget->seenTouchUpdate); diff --git a/tests/manual/qtouchevent/touchwidget.cpp b/tests/manual/qtouchevent/touchwidget.cpp index 1e57c36..0516c5b 100644 --- a/tests/manual/qtouchevent/touchwidget.cpp +++ b/tests/manual/qtouchevent/touchwidget.cpp @@ -24,6 +24,7 @@ void TouchWidget::reset() = closeWindowOnMouseRelease = false; + touchPointCount = 0; } bool TouchWidget::event(QEvent *event) @@ -31,6 +32,7 @@ bool TouchWidget::event(QEvent *event) switch (event->type()) { case QEvent::TouchBegin: seenTouchBegin = true; + touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (acceptTouchBegin) { event->accept(); return true; @@ -38,6 +40,7 @@ bool TouchWidget::event(QEvent *event) break; case QEvent::TouchUpdate: seenTouchUpdate = true; + touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (acceptTouchUpdate) { event->accept(); return true; @@ -45,6 +48,7 @@ bool TouchWidget::event(QEvent *event) break; case QEvent::TouchEnd: seenTouchEnd = true; + touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (closeWindowOnTouchEnd) window()->close(); if (acceptTouchEnd) { diff --git a/tests/manual/qtouchevent/touchwidget.h b/tests/manual/qtouchevent/touchwidget.h index 3e95610..2726deb 100644 --- a/tests/manual/qtouchevent/touchwidget.h +++ b/tests/manual/qtouchevent/touchwidget.h @@ -11,6 +11,7 @@ public: bool acceptTouchBegin, acceptTouchUpdate, acceptTouchEnd; bool seenTouchBegin, seenTouchUpdate, seenTouchEnd; bool closeWindowOnTouchEnd; + int touchPointCount; bool acceptMousePress, acceptMouseMove, acceptMouseRelease; bool seenMousePress, seenMouseMove, seenMouseRelease; -- cgit v0.12 From fecf5bf95329dd6e4aae6bac07e92fb637925101 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 27 Apr 2009 17:13:53 +0200 Subject: center the labels in the test's form --- tests/manual/qtouchevent/form.ui | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/manual/qtouchevent/form.ui b/tests/manual/qtouchevent/form.ui index 4c2da3a..c7fbb78 100644 --- a/tests/manual/qtouchevent/form.ui +++ b/tests/manual/qtouchevent/form.ui @@ -26,6 +26,9 @@ Test Name + + Qt::AlignCenter + @@ -38,6 +41,9 @@ Test description + + Qt::AlignCenter + -- cgit v0.12 From 2a0f0fdc016259dcc956599f41aa024ed06116b5 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 27 Apr 2009 17:32:22 +0200 Subject: 2 new tests for touch points 1. outside the widgets area and 2. over a child --- tests/manual/qtouchevent/main.cpp | 58 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index 8b28967..bc33f16 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -37,6 +37,8 @@ private slots: void basicMultiTouchEventHandling(); void acceptingTouchBeginStopsPropagation(); void ignoringTouchBeginPropagatesToParent(); + void secondTouchPointOnParentGoesToChild(); + void secondTouchPointOnChildGoesToParent(); }; tst_ManualMultitouch::tst_ManualMultitouch() @@ -431,6 +433,62 @@ void tst_ManualMultitouch::ignoringTouchBeginPropagatesToParent() QVERIFY(!testWidget.redWidget->seenMouseRelease); } +void tst_ManualMultitouch::secondTouchPointOnParentGoesToChild() +{ + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Additional Touch-Points Outside Child's Rect Go to Child"); + testWidget.testDescriptionLabel->setText("Press and hold a finger on the blue widget, then on the green one, and release."); + testWidget.redWidget->hide(); + testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.blueWidget->acceptTouchBegin = true; + testWidget.greenWidget->acceptTouchBegin = true; + testWidget.blueWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(testWidget.blueWidget->seenTouchUpdate); + QVERIFY(testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); + QVERIFY(testWidget.blueWidget->touchPointCount > 1); + QVERIFY(!testWidget.greenWidget->seenTouchBegin); + QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + QVERIFY(!testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); +} + +void tst_ManualMultitouch::secondTouchPointOnChildGoesToParent() +{ + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Additional Touch-Points Over Child's Rect Go to Parent"); + testWidget.testDescriptionLabel->setText("Press and hold a finger on the red widget, then on the red one, and release."); + testWidget.greenWidget->hide(); + testWidget.redWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greyWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greyWidget->acceptTouchBegin = true; + testWidget.redWidget->acceptTouchBegin = true; + testWidget.redWidget->closeWindowOnTouchEnd = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(!testWidget.redWidget->seenMousePress); + QVERIFY(!testWidget.redWidget->seenMouseMove); + QVERIFY(!testWidget.redWidget->seenMouseRelease); + QVERIFY(testWidget.redWidget->touchPointCount > 1); + QVERIFY(!testWidget.greyWidget->seenTouchBegin); + QVERIFY(!testWidget.greyWidget->seenTouchUpdate); + QVERIFY(!testWidget.greyWidget->seenTouchEnd); + QVERIFY(!testWidget.greyWidget->seenMousePress); + QVERIFY(!testWidget.greyWidget->seenMouseMove); + QVERIFY(!testWidget.greyWidget->seenMouseRelease); +} + QTEST_MAIN(tst_ManualMultitouch) #include "main.moc" -- cgit v0.12 From 4cbbcf62949421ae5cad422b904b3ed1a004a724 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 28 Apr 2009 08:53:24 +0200 Subject: correct a typo in the test description --- tests/manual/qtouchevent/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index bc33f16..d48f487 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -465,7 +465,7 @@ void tst_ManualMultitouch::secondTouchPointOnChildGoesToParent() { MultitouchTestWidget testWidget; testWidget.testNameLabel->setText("Additional Touch-Points Over Child's Rect Go to Parent"); - testWidget.testDescriptionLabel->setText("Press and hold a finger on the red widget, then on the red one, and release."); + testWidget.testDescriptionLabel->setText("Press and hold a finger on the red widget, then on the grey one, and release."); testWidget.greenWidget->hide(); testWidget.redWidget->setAttribute(Qt::WA_AcceptTouchEvents); testWidget.greyWidget->setAttribute(Qt::WA_AcceptTouchEvents); -- cgit v0.12 From c4b9bccbecddbeeefc8ac9db91421ddaa2193858 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 28 Apr 2009 09:23:34 +0200 Subject: added 2 new tests for testing multi-touch on widget siblings and cousins --- tests/manual/qtouchevent/main.cpp | 62 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index d48f487..9f2d413 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -12,7 +12,7 @@ public: MultitouchTestWidget(QWidget *parent = 0) : QWidget(parent) { - setAttribute(Qt::WA_QuitOnClose, false);\ + setAttribute(Qt::WA_QuitOnClose, false); setupUi(this); } @@ -39,6 +39,8 @@ private slots: void ignoringTouchBeginPropagatesToParent(); void secondTouchPointOnParentGoesToChild(); void secondTouchPointOnChildGoesToParent(); + void secondTouchPointOnSiblingGoesToSibling(); + void secondTouchPointOnCousinGoesToCousin(); }; tst_ManualMultitouch::tst_ManualMultitouch() @@ -489,6 +491,64 @@ void tst_ManualMultitouch::secondTouchPointOnChildGoesToParent() QVERIFY(!testWidget.greyWidget->seenMouseRelease); } +void tst_ManualMultitouch::secondTouchPointOnSiblingGoesToSibling() +{ + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Multi-Touch Interaction Test, Unrelated Widgets Get Separate Events"); + testWidget.testDescriptionLabel->setText("Press and hold a finger on the green widget, then the red one, and release."); + testWidget.blueWidget->hide(); + testWidget.greenWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greenWidget->acceptTouchBegin = true; + testWidget.greenWidget->closeWindowOnTouchEnd = true; + testWidget.greyWidget->hide(); + testWidget.redWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.redWidget->acceptTouchBegin = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); + QVERIFY(testWidget.redWidget->seenTouchBegin); + QVERIFY(testWidget.redWidget->seenTouchUpdate); + QVERIFY(testWidget.redWidget->seenTouchEnd); + QVERIFY(!testWidget.redWidget->seenMousePress); + QVERIFY(!testWidget.redWidget->seenMouseMove); + QVERIFY(!testWidget.redWidget->seenMouseRelease); + QVERIFY(testWidget.greenWidget->touchPointCount == 1); + QVERIFY(testWidget.redWidget->touchPointCount == 1); +} + +void tst_ManualMultitouch::secondTouchPointOnCousinGoesToCousin() +{ + MultitouchTestWidget testWidget; + testWidget.testNameLabel->setText("Multi-Touch Interaction Test, Unrelated Widgets Get Separate Events"); + testWidget.testDescriptionLabel->setText("Press and hold a finger on the blue widget, then the grey one, and release."); + testWidget.blueWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.blueWidget->acceptTouchBegin = true; + testWidget.blueWidget->closeWindowOnTouchEnd = true; + testWidget.greyWidget->setAttribute(Qt::WA_AcceptTouchEvents); + testWidget.greyWidget->acceptTouchBegin = true; + testWidget.showMaximized(); + (void) qApp->exec(); + QVERIFY(testWidget.blueWidget->seenTouchBegin); + QVERIFY(testWidget.blueWidget->seenTouchUpdate); + QVERIFY(testWidget.blueWidget->seenTouchEnd); + QVERIFY(!testWidget.blueWidget->seenMousePress); + QVERIFY(!testWidget.blueWidget->seenMouseMove); + QVERIFY(!testWidget.blueWidget->seenMouseRelease); + QVERIFY(testWidget.greyWidget->seenTouchBegin); + QVERIFY(testWidget.greyWidget->seenTouchUpdate); + QVERIFY(testWidget.greyWidget->seenTouchEnd); + QVERIFY(!testWidget.greyWidget->seenMousePress); + QVERIFY(!testWidget.greyWidget->seenMouseMove); + QVERIFY(!testWidget.greyWidget->seenMouseRelease); + QVERIFY(testWidget.blueWidget->touchPointCount == 1); + QVERIFY(testWidget.greyWidget->touchPointCount == 1); +} + QTEST_MAIN(tst_ManualMultitouch) #include "main.moc" -- cgit v0.12 From 520a67e50ed7717518d3072f8568de00b791f20a Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 29 Apr 2009 15:44:08 +0200 Subject: Use a widget attribute to keep track of whether or not TouchBegin has been accepted --- src/corelib/global/qnamespace.h | 1 + src/gui/kernel/qapplication.cpp | 1 + src/gui/kernel/qapplication_p.h | 1 - src/gui/kernel/qapplication_win.cpp | 12 +++++------- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index b73a873..15da2d6 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -486,6 +486,7 @@ public: WA_TranslucentBackground = 120, WA_AcceptTouchEvents = 121, + WA_AcceptedTouchBeginEvent = 122, // Add new attributes before this line WA_AttributeCount diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 4e61f66..bc9b827 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4013,6 +4013,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) res = widget->testAttribute(Qt::WA_AcceptTouchEvents) && d->notify_helper(widget, touchEvent); eventAccepted = touchEvent->isAccepted(); + widget->setAttribute(Qt::WA_AcceptedTouchBeginEvent, res && eventAccepted); touchEvent->spont = false; if (res && eventAccepted) { // the first widget to accept the TouchBegin gets an implicit grab. diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 91857ec..ce7110d 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -429,7 +429,6 @@ public: #endif QPointer currentMultitouchWidget; - bool currentMultitouchWidgetAcceptedTouchBegin; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); #if defined(Q_WS_WIN) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 7e07104..f1fb0a4 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4000,7 +4000,6 @@ void QApplicationPrivate::initializeMultitouch() CloseTouchInputHandle = static_cast(library.resolve("CloseTouchInputHandle")); currentMultitouchWidget = 0; - currentMultitouchWidgetAcceptedTouchBegin = false; touchInputIDToTouchPointID.clear(); allTouchPoints.clear(); currentTouchPoints.clear(); @@ -4106,7 +4105,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) currentMultitouchWidget = child; // if the TouchBegin handler recurses, we assume that means the event // has been implicitly accepted and continue to send touch events - currentMultitouchWidgetAcceptedTouchBegin = true; + currentMultitouchWidget->setAttribute(Qt::WA_AcceptedTouchBeginEvent); } } @@ -4133,11 +4132,10 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) updateTouchPointsForWidget(widget, &touchEvent); if (sendTouchBegin) { - bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent); - qt_tabletChokeMouse - = currentMultitouchWidgetAcceptedTouchBegin - = (res && touchEvent.isAccepted()); - } else if (currentMultitouchWidgetAcceptedTouchBegin) { + bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent) + && touchEvent.isAccepted(); + qt_tabletChokeMouse = res; + } else if (widget->testAttribute(Qt::WA_AcceptedTouchBeginEvent)) { (void) QApplication::sendSpontaneousEvent(widget, &touchEvent); qt_tabletChokeMouse = true; } else { -- cgit v0.12 From a3504361a19428c12ac7e044762f252d8ba2b26f Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 29 Apr 2009 18:08:44 +0200 Subject: Don't store activeTouchPoints in QApplicationPrivate it's only needed and updated during delivery, so the extra copy isn't necessary --- src/gui/kernel/qapplication_p.h | 2 +- src/gui/kernel/qapplication_win.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index ce7110d..da66aab 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -438,7 +438,7 @@ public: QMap touchInputIDToTouchPointID; QVector allTouchPoints; - QList currentTouchPoints, activeTouchPoints; + QList currentTouchPoints; void initializeMultitouch(); void insertActiveTouch(QTouchEvent::TouchPoint *touchPoint); diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index f1fb0a4..c7ecdae 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4003,7 +4003,6 @@ void QApplicationPrivate::initializeMultitouch() touchInputIDToTouchPointID.clear(); allTouchPoints.clear(); currentTouchPoints.clear(); - activeTouchPoints.clear(); } void QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint) @@ -4015,7 +4014,6 @@ void QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint) break; } currentTouchPoints.insert(at, touchPoint); - activeTouchPoints = currentTouchPoints; if (currentTouchPoints.count() > allTouchPoints.count()) { qFatal("Qt: INTERNAL ERROR: currentTouchPoints.count() (%d) > allTouchPoints.count() (%d)", @@ -4042,7 +4040,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) Q_Q(QApplication); bool sendTouchBegin = currentTouchPoints.isEmpty(); - activeTouchPoints = currentTouchPoints; + QList activeTouchPoints = currentTouchPoints; QVector winTouchInputs(msg.wParam); memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); @@ -4068,6 +4066,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { insertActiveTouch(touchPoint); + activeTouchPoints = currentTouchPoints; touchPoint->d->state = Qt::TouchPointPressed; touchPoint->d->globalPos = touchPoint->d->startGlobalPos = touchPoint->d->lastGlobalPos = globalPos; touchPoint->d->pressure = qreal(1.); -- cgit v0.12 From f9b4c7bea991d1786c60406abc9da95354b0d6e3 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 30 Apr 2009 10:24:20 +0200 Subject: Don't call RegisterTouchWindow() if we don't have a window id --- src/gui/kernel/qwidget_win.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 26e66b1..f53a3ef 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -498,7 +498,7 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO } // ### don't always register for touch events - if (QApplicationPrivate::RegisterTouchWindow && !desktop) + if (id && QApplicationPrivate::RegisterTouchWindow && !desktop) QApplicationPrivate::RegisterTouchWindow(id, 0); q->setAttribute(Qt::WA_WState_Created); // accept move/resize events -- cgit v0.12 From cd260501918ea767e8a3ca33a51b1cb4e1bbd45c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 30 Apr 2009 11:27:43 +0200 Subject: Made the Touch* event detection very strict TouchBegin is only "seen" once before all other touch events, TouchUpdate must be after TouchBegin but before TouchEnd, and TouchEnd is only seen once after all other touch events. --- tests/manual/qtouchevent/touchwidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/manual/qtouchevent/touchwidget.cpp b/tests/manual/qtouchevent/touchwidget.cpp index 0516c5b..f029f8b 100644 --- a/tests/manual/qtouchevent/touchwidget.cpp +++ b/tests/manual/qtouchevent/touchwidget.cpp @@ -31,7 +31,7 @@ bool TouchWidget::event(QEvent *event) { switch (event->type()) { case QEvent::TouchBegin: - seenTouchBegin = true; + seenTouchBegin = !seenTouchBegin && !seenTouchUpdate && !seenTouchEnd; touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (acceptTouchBegin) { event->accept(); @@ -39,7 +39,7 @@ bool TouchWidget::event(QEvent *event) } break; case QEvent::TouchUpdate: - seenTouchUpdate = true; + seenTouchUpdate = seenTouchBegin && !seenTouchEnd; touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (acceptTouchUpdate) { event->accept(); @@ -47,7 +47,7 @@ bool TouchWidget::event(QEvent *event) } break; case QEvent::TouchEnd: - seenTouchEnd = true; + seenTouchEnd = seenTouchBegin && !seenTouchEnd; touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (closeWindowOnTouchEnd) window()->close(); -- cgit v0.12 From 33a9b5b4c7945c800b6590b993c841fb833276e8 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 30 Apr 2009 11:33:03 +0200 Subject: Determine whether TouchBegin or TouchEnd should be sent at the time of touchpoint addition/removal This will make it easier to change later when supporting touching multiple widgets --- src/gui/kernel/qapplication_win.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 9b756a6..60bad92 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4039,7 +4039,8 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) { Q_Q(QApplication); - bool sendTouchBegin = currentTouchPoints.isEmpty(); + bool sendTouchBegin = false; + bool sendTouchEnd = false; QList activeTouchPoints = currentTouchPoints; QVector winTouchInputs(msg.wParam); @@ -4065,13 +4066,17 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) bool down = touchPoint->d->state != Qt::TouchPointReleased; QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { + sendTouchBegin = currentTouchPoints.isEmpty(); insertActiveTouch(touchPoint); activeTouchPoints = currentTouchPoints; + touchPoint->d->state = Qt::TouchPointPressed; touchPoint->d->globalPos = touchPoint->d->startGlobalPos = touchPoint->d->lastGlobalPos = globalPos; touchPoint->d->pressure = qreal(1.); } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { removeActiveTouch(touchPoint); + sendTouchEnd = currentTouchPoints.isEmpty(); + touchPoint->d->state = Qt::TouchPointReleased; touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; touchPoint->d->globalPos = globalPos; @@ -4087,8 +4092,6 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) } QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); - bool sendTouchEnd = currentTouchPoints.isEmpty(); - if (activeTouchPoints.isEmpty()) return false; -- cgit v0.12 From bdeefa6bd4b8721c737aeb63794034c3495be9a1 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 30 Apr 2009 13:33:44 +0200 Subject: Use the HWND from the Windows MSG to find which widget to send events to the logic for determining the event type has refactored as well --- src/gui/kernel/qapplication_p.h | 4 +-- src/gui/kernel/qapplication_win.cpp | 63 +++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index e9db8f3..ef375d3 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -441,8 +441,8 @@ public: QList currentTouchPoints; void initializeMultitouch(); - void insertActiveTouch(QTouchEvent::TouchPoint *touchPoint); - void removeActiveTouch(QTouchEvent::TouchPoint *touchPoint); + QEvent::Type insertActiveTouch(QTouchEvent::TouchPoint *touchPoint); + QEvent::Type removeActiveTouch(QTouchEvent::TouchPoint *touchPoint); bool translateTouchEvent(const MSG &msg); #endif diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 60bad92..b9262af 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4005,9 +4005,13 @@ void QApplicationPrivate::initializeMultitouch() currentTouchPoints.clear(); } -void QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint) +QEvent::Type QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint) { - // insort deviceNumber + QEvent::Type eventType = currentTouchPoints.isEmpty() + ? QEvent::TouchBegin + : QEvent::TouchUpdate; + + // insort touch point int at = 0; for (; at < currentTouchPoints.count(); ++at) { if (currentTouchPoints.at(at)->id() > touchPoint->id()) @@ -4020,9 +4024,11 @@ void QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint) currentTouchPoints.count(), allTouchPoints.count()); } + + return eventType; } -void QApplicationPrivate::removeActiveTouch(QTouchEvent::TouchPoint *touchPoint) +QEvent::Type QApplicationPrivate::removeActiveTouch(QTouchEvent::TouchPoint *touchPoint) { for (int i = qMin(currentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { if (currentTouchPoints.at(i) == touchPoint) { @@ -4031,16 +4037,18 @@ void QApplicationPrivate::removeActiveTouch(QTouchEvent::TouchPoint *touchPoint) } } - // leave activeTouchPoints unchanged, since we need to make sure - // that the Release for deviceNumber is sent + return currentTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate; } bool QApplicationPrivate::translateTouchEvent(const MSG &msg) { Q_Q(QApplication); - bool sendTouchBegin = false; - bool sendTouchEnd = false; + QWidget *widgetForHwnd = QWidget::find(msg.hwnd); + if (!widgetForHwnd) + return false; + + QEvent::Type eventType = QEvent::None; QList activeTouchPoints = currentTouchPoints; QVector winTouchInputs(msg.wParam); @@ -4066,22 +4074,22 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) bool down = touchPoint->d->state != Qt::TouchPointReleased; QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { - sendTouchBegin = currentTouchPoints.isEmpty(); - insertActiveTouch(touchPoint); + eventType = insertActiveTouch(touchPoint); + // make sure new points are added to activeTouchPoints as well activeTouchPoints = currentTouchPoints; touchPoint->d->state = Qt::TouchPointPressed; touchPoint->d->globalPos = touchPoint->d->startGlobalPos = touchPoint->d->lastGlobalPos = globalPos; touchPoint->d->pressure = qreal(1.); } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { - removeActiveTouch(touchPoint); - sendTouchEnd = currentTouchPoints.isEmpty(); + eventType = removeActiveTouch(touchPoint); touchPoint->d->state = Qt::TouchPointReleased; touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; touchPoint->d->globalPos = globalPos; touchPoint->d->pressure = qreal(0.); } else if (down) { + eventType = QEvent::TouchUpdate; touchPoint->d->state = globalPos == touchPoint->d->globalPos ? Qt::TouchPointStationary : Qt::TouchPointMoved; @@ -4092,30 +4100,28 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) } QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); - if (activeTouchPoints.isEmpty()) + Q_ASSERT(eventType != QEvent::None); + if (eventType == QEvent::None || activeTouchPoints.isEmpty()) return false; - if (sendTouchBegin) { + if (eventType == QEvent::TouchBegin) { // the window under the first touch point gets the touch event int firstTouchId = activeTouchPoints.first()->id(); const QPoint &globalPos = allTouchPoints.at(firstTouchId)->d->globalPos.toPoint(); - QWidget *window = q->topLevelAt(globalPos); - if (window) { - QWidget *child = window->childAt(window->mapFromGlobal(globalPos)); - if (!child) - child = window; - currentMultitouchWidget = child; - // if the TouchBegin handler recurses, we assume that means the event - // has been implicitly accepted and continue to send touch events - currentMultitouchWidget->setAttribute(Qt::WA_AcceptedTouchBeginEvent); - } + QWidget *child = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(globalPos)); + if (!child) + child = widgetForHwnd; + currentMultitouchWidget = child; + // if the TouchBegin handler recurses, we assume that means the event + // has been implicitly accepted and continue to send touch events + currentMultitouchWidget->setAttribute(Qt::WA_AcceptedTouchBeginEvent); } QWidget *widget = q->activePopupWidget(); if (!widget) widget = currentMultitouchWidget; - if (sendTouchEnd) { + if (eventType == QEvent::TouchEnd) { // reset currentMultitouchWindow when the last touch is released currentMultitouchWidget = 0; if (!currentTouchPoints.isEmpty()) { @@ -4125,15 +4131,10 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) // deliver the event if (widget && QApplicationPrivate::tryModalHelper(widget, 0)) { - QTouchEvent touchEvent((sendTouchBegin - ? QEvent::TouchBegin - : sendTouchEnd - ? QEvent::TouchEnd - : QEvent::TouchUpdate), - q->keyboardModifiers(), activeTouchPoints); + QTouchEvent touchEvent(eventType, q->keyboardModifiers(), activeTouchPoints); updateTouchPointsForWidget(widget, &touchEvent); - if (sendTouchBegin) { + if (eventType == QEvent::TouchBegin) { bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent) && touchEvent.isAccepted(); qt_tabletChokeMouse = res; -- cgit v0.12 From 36c97c3e530969d172179d715805804ffb96e792 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 4 May 2009 12:22:44 +0200 Subject: Initialize activeTouchPoints in the loop itself instead of before This will make it easier to make the change to sending touch events to multiple widgets. --- src/gui/kernel/qapplication_win.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index b9262af..fbedd19 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4049,8 +4049,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) return false; QEvent::Type eventType = QEvent::None; - QList activeTouchPoints = currentTouchPoints; - + QList activeTouchPoints; QVector winTouchInputs(msg.wParam); memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); @@ -4082,6 +4081,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) touchPoint->d->globalPos = touchPoint->d->startGlobalPos = touchPoint->d->lastGlobalPos = globalPos; touchPoint->d->pressure = qreal(1.); } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { + activeTouchPoints = currentTouchPoints; eventType = removeActiveTouch(touchPoint); touchPoint->d->state = Qt::TouchPointReleased; @@ -4089,6 +4089,8 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) touchPoint->d->globalPos = globalPos; touchPoint->d->pressure = qreal(0.); } else if (down) { + if (activeTouchPoints.isEmpty()) + activeTouchPoints = currentTouchPoints; eventType = QEvent::TouchUpdate; touchPoint->d->state = globalPos == touchPoint->d->globalPos ? Qt::TouchPointStationary -- cgit v0.12 From 2d0f49df3a3c44601bf2e41ea722c2384b71aab0 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 4 May 2009 17:17:34 +0200 Subject: Support sending touch events (with multiple touch points) to multiple widgets simultaneously This is a first attempt, and it works, but it will need to be cleaned up to remove as much state from QWidgetPrivate as possible. --- src/gui/kernel/qapplication.cpp | 9 +- src/gui/kernel/qapplication_p.h | 11 ++- src/gui/kernel/qapplication_win.cpp | 191 +++++++++++++++++++++++------------- src/gui/kernel/qevent.h | 2 + src/gui/kernel/qevent_p.h | 1 + src/gui/kernel/qwidget_p.h | 3 + 6 files changed, 141 insertions(+), 76 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 81c02e3..3a25abb 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -3997,6 +3997,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) // Note: TouchUpdate and TouchEnd events are sent to d->currentMultitouchWidget and never propagated { QWidget *widget = static_cast(receiver); + QWidget *origin = widget; QTouchEvent *touchEvent = static_cast(e); bool eventAccepted = touchEvent->isAccepted(); @@ -4017,7 +4018,13 @@ bool QApplication::notify(QObject *receiver, QEvent *e) touchEvent->spont = false; if (res && eventAccepted) { // the first widget to accept the TouchBegin gets an implicit grab. - d->currentMultitouchWidget = widget; + for (int i = 0; i < touchEvent->_touchPoints.count(); ++i) { + QTouchEvent::TouchPoint *touchPoint = touchEvent->_touchPoints.at(i); + touchPoint->d->widget = widget; + } + if (origin != widget) + origin->d_func()->currentTouchPoints.clear(); + widget->d_func()->currentTouchPoints = touchEvent->_touchPoints; break; } else if (widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) { break; diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index ef375d3..1a2bad2 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -428,7 +428,6 @@ public: void sendSyntheticEnterLeave(QWidget *widget); #endif - QPointer currentMultitouchWidget; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); #if defined(Q_WS_WIN) @@ -437,12 +436,14 @@ public: static qt_CloseTouchInputHandlePtr CloseTouchInputHandle; QMap touchInputIDToTouchPointID; - QVector allTouchPoints; - QList currentTouchPoints; + QVector appAllTouchPoints; + QList appCurrentTouchPoints; void initializeMultitouch(); - QEvent::Type insertActiveTouch(QTouchEvent::TouchPoint *touchPoint); - QEvent::Type removeActiveTouch(QTouchEvent::TouchPoint *touchPoint); + static QTouchEvent::TouchPoint *findClosestTouchPoint(const QList &activeTouchPoints, + const QPointF &pos); + QEvent::Type appendTouchPoint(QTouchEvent::TouchPoint *touchPoint); + QEvent::Type removeTouchPoint(QTouchEvent::TouchPoint *touchPoint); bool translateTouchEvent(const MSG &msg); #endif diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index fbedd19..f13ab5d 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -75,6 +75,7 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c #include "qcolormap.h" #include "qlayout.h" #include "qtooltip.h" +#include "qset.h" #include "qt_windows.h" #if defined(QT_NON_COMMERCIAL) #include "qnc_win.h" @@ -3999,45 +4000,76 @@ void QApplicationPrivate::initializeMultitouch() GetTouchInputInfo = static_cast(library.resolve("GetTouchInputInfo")); CloseTouchInputHandle = static_cast(library.resolve("CloseTouchInputHandle")); - currentMultitouchWidget = 0; touchInputIDToTouchPointID.clear(); - allTouchPoints.clear(); - currentTouchPoints.clear(); + appAllTouchPoints.clear(); } -QEvent::Type QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint) +QTouchEvent::TouchPoint *QApplicationPrivate::findClosestTouchPoint(const QList &appActiveTouchPoints, + const QPointF &pos) { - QEvent::Type eventType = currentTouchPoints.isEmpty() + QTouchEvent::TouchPoint *closestTouchPoint = 0; + qreal closestDistance; + for (int i = 0; i < appActiveTouchPoints.count(); ++i) { + QTouchEvent::TouchPoint *touchPoint = appActiveTouchPoints.at(i); + qreal distance = QLineF(pos, touchPoint->d->globalPos).length(); + if (!closestTouchPoint || distance < closestDistance) { + closestTouchPoint = touchPoint; + closestDistance = distance; + } + } + return closestTouchPoint; +} + +QEvent::Type QApplicationPrivate::appendTouchPoint(QTouchEvent::TouchPoint *touchPoint) +{ + QWidget *widget = touchPoint->d->widget; + QEvent::Type eventType = widget->d_func()->currentTouchPoints.isEmpty() ? QEvent::TouchBegin : QEvent::TouchUpdate; - // insort touch point + // insort touch point (for the app) int at = 0; - for (; at < currentTouchPoints.count(); ++at) { - if (currentTouchPoints.at(at)->id() > touchPoint->id()) + for (; at < appCurrentTouchPoints.count(); ++at) { + if (appCurrentTouchPoints.at(at)->id() > touchPoint->id()) break; } - currentTouchPoints.insert(at, touchPoint); + appCurrentTouchPoints.insert(at, touchPoint); + // again, for the widget's currentTouchPoints + for (at = 0; at < widget->d_func()->currentTouchPoints.count(); ++at) { + if (widget->d_func()->currentTouchPoints.at(at)->id() > touchPoint->id()) + break; + } + widget->d_func()->currentTouchPoints.insert(at, touchPoint); - if (currentTouchPoints.count() > allTouchPoints.count()) { - qFatal("Qt: INTERNAL ERROR: currentTouchPoints.count() (%d) > allTouchPoints.count() (%d)", - currentTouchPoints.count(), - allTouchPoints.count()); + if (appCurrentTouchPoints.count() > appAllTouchPoints.count()) { + qFatal("Qt: INTERNAL ERROR: appCurrentTouchPoints.count() (%d) > appAllTouchPoints.count() (%d)", + appCurrentTouchPoints.count(), + appAllTouchPoints.count()); } return eventType; } -QEvent::Type QApplicationPrivate::removeActiveTouch(QTouchEvent::TouchPoint *touchPoint) +QEvent::Type QApplicationPrivate::removeTouchPoint(QTouchEvent::TouchPoint *touchPoint) { - for (int i = qMin(currentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { - if (currentTouchPoints.at(i) == touchPoint) { - currentTouchPoints.removeAt(i); + QWidget *widget = touchPoint->d->widget; + + // remove touch point from all known touch points + for (int i = qMin(appCurrentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { + if (appCurrentTouchPoints.at(i) == touchPoint) { + appCurrentTouchPoints.removeAt(i); + break; + } + } + // again, for the widget's currentTouchPoints + for (int i = qMin(widget->d_func()->currentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { + if (widget->d_func()->currentTouchPoints.at(i) == touchPoint) { + widget->d_func()->currentTouchPoints.removeAt(i); break; } } - return currentTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate; + return widget->d_func()->currentTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate; } bool QApplicationPrivate::translateTouchEvent(const MSG &msg) @@ -4048,8 +4080,9 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) if (!widgetForHwnd) return false; - QEvent::Type eventType = QEvent::None; - QList activeTouchPoints; + QList appActiveTouchPoints = appCurrentTouchPoints; + + QSet widgetsNeedingEvents; QVector winTouchInputs(msg.wParam); memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); @@ -4062,36 +4095,56 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) touchInputIDToTouchPointID.insert(touchInput.dwID, touchPointID); } - if (allTouchPoints.count() <= touchPointID) - allTouchPoints.resize(touchPointID + 1); + if (appAllTouchPoints.count() <= touchPointID) + appAllTouchPoints.resize(touchPointID + 1); - QTouchEvent::TouchPoint *touchPoint = allTouchPoints.at(touchPointID); + QTouchEvent::TouchPoint *touchPoint = appAllTouchPoints.at(touchPointID); if (!touchPoint) - touchPoint = allTouchPoints[touchPointID] = new QTouchEvent::TouchPoint(touchPointID); + touchPoint = appAllTouchPoints[touchPointID] = new QTouchEvent::TouchPoint(touchPointID); // update state bool down = touchPoint->d->state != Qt::TouchPointReleased; QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { - eventType = insertActiveTouch(touchPoint); + // determine which widget this event will go to + QWidget *w = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(globalPos.toPoint())); + if (!w) + w = widgetForHwnd; + + QTouchEvent::TouchPoint *closestTouchPoint = findClosestTouchPoint(appActiveTouchPoints, globalPos); + if (closestTouchPoint + && (w->isAncestorOf(closestTouchPoint->d->widget) + || closestTouchPoint->d->widget->isAncestorOf(w))) { + w = closestTouchPoint->d->widget; + } + touchPoint->d->widget = w; + + w->d_func()->touchEventType = appendTouchPoint(touchPoint); // make sure new points are added to activeTouchPoints as well - activeTouchPoints = currentTouchPoints; + appActiveTouchPoints = appCurrentTouchPoints; + w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints; touchPoint->d->state = Qt::TouchPointPressed; - touchPoint->d->globalPos = touchPoint->d->startGlobalPos = touchPoint->d->lastGlobalPos = globalPos; + touchPoint->d->globalPos + = touchPoint->d->startGlobalPos + = touchPoint->d->lastGlobalPos + = globalPos; touchPoint->d->pressure = qreal(1.); } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { - activeTouchPoints = currentTouchPoints; - eventType = removeActiveTouch(touchPoint); + QWidget *w = touchPoint->d->widget; + appActiveTouchPoints = appCurrentTouchPoints; + w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints; + w->d_func()->touchEventType = removeTouchPoint(touchPoint); touchPoint->d->state = Qt::TouchPointReleased; touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; touchPoint->d->globalPos = globalPos; touchPoint->d->pressure = qreal(0.); } else if (down) { - if (activeTouchPoints.isEmpty()) - activeTouchPoints = currentTouchPoints; - eventType = QEvent::TouchUpdate; + QWidget *w = touchPoint->d->widget; + appActiveTouchPoints = appCurrentTouchPoints; + w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints; + w->d_func()->touchEventType = QEvent::TouchUpdate; touchPoint->d->state = globalPos == touchPoint->d->globalPos ? Qt::TouchPointStationary : Qt::TouchPointMoved; @@ -4099,58 +4152,56 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) touchPoint->d->globalPos = globalPos; // pressure should still be 1. } + + if (touchPoint->d->state != Qt::TouchPointStationary) + widgetsNeedingEvents.insert(touchPoint->d->widget); } QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); - Q_ASSERT(eventType != QEvent::None); - if (eventType == QEvent::None || activeTouchPoints.isEmpty()) + if (widgetsNeedingEvents.isEmpty()) return false; - if (eventType == QEvent::TouchBegin) { - // the window under the first touch point gets the touch event - int firstTouchId = activeTouchPoints.first()->id(); - const QPoint &globalPos = allTouchPoints.at(firstTouchId)->d->globalPos.toPoint(); - QWidget *child = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(globalPos)); - if (!child) - child = widgetForHwnd; - currentMultitouchWidget = child; - // if the TouchBegin handler recurses, we assume that means the event - // has been implicitly accepted and continue to send touch events - currentMultitouchWidget->setAttribute(Qt::WA_AcceptedTouchBeginEvent); - } + bool returnValue = false; - QWidget *widget = q->activePopupWidget(); - if (!widget) - widget = currentMultitouchWidget; - - if (eventType == QEvent::TouchEnd) { - // reset currentMultitouchWindow when the last touch is released - currentMultitouchWidget = 0; - if (!currentTouchPoints.isEmpty()) { - qFatal("Qt: INTERNAL ERROR, currentTouchPoints should be empty!"); - } - } + QSet::ConstIterator it = widgetsNeedingEvents.constBegin(); + const QSet::ConstIterator end = widgetsNeedingEvents.constEnd(); + for (; it != end; ++it) { + QWidget *widget = *it; + if (!QApplicationPrivate::tryModalHelper(widget, 0)) + continue; - // deliver the event - if (widget && QApplicationPrivate::tryModalHelper(widget, 0)) { - QTouchEvent touchEvent(eventType, q->keyboardModifiers(), activeTouchPoints); + QTouchEvent touchEvent(widget->d_func()->touchEventType, q->keyboardModifiers(), widget->d_func()->activeTouchPoints); updateTouchPointsForWidget(widget, &touchEvent); - if (eventType == QEvent::TouchBegin) { + switch (widget->d_func()->touchEventType) { + case QEvent::TouchBegin: + { + // if the TouchBegin handler recurses, we assume that means the event + // has been implicitly accepted and continue to send touch events + widget->setAttribute(Qt::WA_AcceptedTouchBeginEvent); bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent) && touchEvent.isAccepted(); - qt_tabletChokeMouse = res; - } else if (widget->testAttribute(Qt::WA_AcceptedTouchBeginEvent)) { - (void) QApplication::sendSpontaneousEvent(widget, &touchEvent); - qt_tabletChokeMouse = true; - } else { - qt_tabletChokeMouse = false; + returnValue = returnValue || (qt_tabletChokeMouse = res); + break; + } + case QEvent::TouchEnd: + if (!widget->d_func()->currentTouchPoints.isEmpty()) { + qFatal("Qt: INTERNAL ERROR, the widget's currentTouchPoints should be empty!"); + } + // fall-through intended + default: + if (widget->testAttribute(Qt::WA_AcceptedTouchBeginEvent)) { + (void) QApplication::sendSpontaneousEvent(widget, &touchEvent); + qt_tabletChokeMouse = true; + } else { + qt_tabletChokeMouse = false; + } + returnValue = returnValue || qt_tabletChokeMouse; + break; } - - return qt_tabletChokeMouse; } - return false; + return returnValue; } QT_END_NAMESPACE diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 506e0c1..e9a1386 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -746,6 +746,7 @@ public: private: QTouchEventTouchPointPrivate *d; + friend class QApplication; friend class QApplicationPrivate; }; @@ -759,6 +760,7 @@ public: protected: QList _touchPoints; + friend class QApplication; friend class QApplicationPrivate; }; diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 5e24eeb..df7143e 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -98,6 +98,7 @@ public: pressure(qreal(-1.)) { } + QPointer widget; int id; Qt::TouchPointState state; QPointF pos, startPos, lastPos; diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 8731551..df17566 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -663,6 +663,9 @@ public: } QSize adjustedSize() const; + + QEvent::Type touchEventType; + QList currentTouchPoints, activeTouchPoints; }; inline QWExtra *QWidgetPrivate::extraData() const -- cgit v0.12 From 7a5da4486708b096084fdaf83aba88f5d1b83e00 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 6 May 2009 14:08:34 +0200 Subject: fix QtOpenGL compilation, add missing include --- src/gui/kernel/qwidget_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 2dd3061..2ab9ffe 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -60,6 +60,7 @@ #include "QtGui/qregion.h" #include "QtGui/qsizepolicy.h" #include "QtGui/qstyle.h" +#include "QtGui/qevent.h" #ifdef Q_WS_WIN #include "QtCore/qt_windows.h" -- cgit v0.12 From 58f25669087bf52357bc69f398b4713d33b03d1e Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 8 May 2009 13:04:19 +0200 Subject: don't store event state in QWidgetPrivate the only thing we store in the QWidgetPrivate is the current touch point list, nothing more (the rest is local state in the event translation code) --- src/gui/kernel/qapplication_win.cpp | 34 +++++++++++++++++++--------------- src/gui/kernel/qwidget_p.h | 3 +-- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 91c24da..60c6fd7 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -75,7 +75,6 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c #include "qcolormap.h" #include "qlayout.h" #include "qtooltip.h" -#include "qset.h" #include "qt_windows.h" #if defined(QT_NON_COMMERCIAL) #include "qnc_win.h" @@ -4091,7 +4090,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) QList appActiveTouchPoints = appCurrentTouchPoints; - QSet widgetsNeedingEvents; + QHash widgetsNeedingEvents; QVector winTouchInputs(msg.wParam); memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); @@ -4114,6 +4113,8 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) // update state bool down = touchPoint->d->state != Qt::TouchPointReleased; QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); + QEvent::Type eventType = QEvent::None; + QList activeTouchPoints; if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { // determine which widget this event will go to QWidget *w = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(globalPos.toPoint())); @@ -4128,10 +4129,10 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) } touchPoint->d->widget = w; - w->d_func()->touchEventType = appendTouchPoint(touchPoint); + eventType = appendTouchPoint(touchPoint); // make sure new points are added to activeTouchPoints as well appActiveTouchPoints = appCurrentTouchPoints; - w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints; + activeTouchPoints = w->d_func()->currentTouchPoints; touchPoint->d->state = Qt::TouchPointPressed; touchPoint->d->globalPos @@ -4142,8 +4143,8 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { QWidget *w = touchPoint->d->widget; appActiveTouchPoints = appCurrentTouchPoints; - w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints; - w->d_func()->touchEventType = removeTouchPoint(touchPoint); + activeTouchPoints = w->d_func()->currentTouchPoints; + eventType = removeTouchPoint(touchPoint); touchPoint->d->state = Qt::TouchPointReleased; touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; @@ -4152,8 +4153,8 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) } else if (down) { QWidget *w = touchPoint->d->widget; appActiveTouchPoints = appCurrentTouchPoints; - w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints; - w->d_func()->touchEventType = QEvent::TouchUpdate; + activeTouchPoints = w->d_func()->currentTouchPoints; + eventType = QEvent::TouchUpdate; touchPoint->d->state = globalPos == touchPoint->d->globalPos ? Qt::TouchPointStationary : Qt::TouchPointMoved; @@ -4161,9 +4162,12 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) touchPoint->d->globalPos = globalPos; // pressure should still be 1. } + Q_ASSERT(eventType != QEvent::None); - if (touchPoint->d->state != Qt::TouchPointStationary) - widgetsNeedingEvents.insert(touchPoint->d->widget); + if (touchPoint->d->state != Qt::TouchPointStationary) { + widgetsNeedingEvents.insert(touchPoint->d->widget, + QTouchEvent(eventType, q->keyboardModifiers(), activeTouchPoints)); + } } QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); @@ -4172,17 +4176,17 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) bool returnValue = false; - QSet::ConstIterator it = widgetsNeedingEvents.constBegin(); - const QSet::ConstIterator end = widgetsNeedingEvents.constEnd(); + QHash::ConstIterator it = widgetsNeedingEvents.constBegin(); + const QHash::ConstIterator end = widgetsNeedingEvents.constEnd(); for (; it != end; ++it) { - QWidget *widget = *it; + QWidget *widget = it.key(); if (!QApplicationPrivate::tryModalHelper(widget, 0)) continue; - QTouchEvent touchEvent(widget->d_func()->touchEventType, q->keyboardModifiers(), widget->d_func()->activeTouchPoints); + QTouchEvent touchEvent = it.value(); updateTouchPointsForWidget(widget, &touchEvent); - switch (widget->d_func()->touchEventType) { + switch (touchEvent.type()) { case QEvent::TouchBegin: { // if the TouchBegin handler recurses, we assume that means the event diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 2ab9ffe..5f5e1b1 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -666,8 +666,7 @@ public: QSize adjustedSize() const; - QEvent::Type touchEventType; - QList currentTouchPoints, activeTouchPoints; + QList currentTouchPoints; }; inline QWExtra *QWidgetPrivate::extraData() const -- cgit v0.12 From c45ba5929d2e6326449df124d42fc60032c1c93c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 8 May 2009 13:06:47 +0200 Subject: warn about events that happen out of sequence --- tests/manual/qtouchevent/touchwidget.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/manual/qtouchevent/touchwidget.cpp b/tests/manual/qtouchevent/touchwidget.cpp index f029f8b..a8141cc 100644 --- a/tests/manual/qtouchevent/touchwidget.cpp +++ b/tests/manual/qtouchevent/touchwidget.cpp @@ -31,6 +31,9 @@ bool TouchWidget::event(QEvent *event) { switch (event->type()) { case QEvent::TouchBegin: + if (seenTouchBegin) qWarning("TouchBegin: already seen a TouchBegin"); + if (seenTouchUpdate) qWarning("TouchBegin: TouchUpdate cannot happen before TouchBegin"); + if (seenTouchEnd) qWarning("TouchBegin: TouchEnd cannot happen before TouchBegin"); seenTouchBegin = !seenTouchBegin && !seenTouchUpdate && !seenTouchEnd; touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (acceptTouchBegin) { @@ -39,6 +42,8 @@ bool TouchWidget::event(QEvent *event) } break; case QEvent::TouchUpdate: + if (!seenTouchBegin) qWarning("TouchUpdate: have not seen TouchBegin"); + if (seenTouchEnd) qWarning("TouchUpdate: TouchEnd cannot happen before TouchUpdate"); seenTouchUpdate = seenTouchBegin && !seenTouchEnd; touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (acceptTouchUpdate) { @@ -47,6 +52,8 @@ bool TouchWidget::event(QEvent *event) } break; case QEvent::TouchEnd: + if (!seenTouchBegin) qWarning("TouchEnd: have not seen TouchBegin"); + if (seenTouchEnd) qWarning("TouchEnd: already seen a TouchEnd"); seenTouchEnd = seenTouchBegin && !seenTouchEnd; touchPointCount = qMax(touchPointCount, static_cast(event)->touchPoints().count()); if (closeWindowOnTouchEnd) -- cgit v0.12 From 711701da5cc958bb44cd5ff2d6562cfca1f7eb85 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 8 May 2009 15:45:43 +0200 Subject: don't require TouchUpdate events for the single touch cases the finger may not move, meaning no update with move event is generated, even if we say "press, hold, release" when testing (the finger may not wobble enough) --- tests/manual/qtouchevent/main.cpp | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/manual/qtouchevent/main.cpp b/tests/manual/qtouchevent/main.cpp index 9f2d413..8097ab0 100644 --- a/tests/manual/qtouchevent/main.cpp +++ b/tests/manual/qtouchevent/main.cpp @@ -61,7 +61,7 @@ void tst_ManualMultitouch::ignoringTouchEventsEmulatesMouseEvents() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(!testWidget.greenWidget->seenTouchBegin); - QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + // QVERIFY(!testWidget.greenWidget->seenTouchUpdate); QVERIFY(!testWidget.greenWidget->seenTouchEnd); QVERIFY(testWidget.greenWidget->seenMousePress); // QVERIFY(testWidget.greenWidget->seenMouseMove); @@ -74,7 +74,7 @@ void tst_ManualMultitouch::ignoringTouchEventsEmulatesMouseEvents() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(!testWidget.greenWidget->seenTouchUpdate); + // QVERIFY(!testWidget.greenWidget->seenTouchUpdate); QVERIFY(!testWidget.greenWidget->seenTouchEnd); QVERIFY(testWidget.greenWidget->seenMousePress); // QVERIFY(testWidget.greenWidget->seenMouseMove); @@ -96,12 +96,12 @@ void tst_ManualMultitouch::basicSingleTouchEventHandling() testWidget.greenWidget->closeWindowOnTouchEnd = true; testWidget.showMaximized(); (void) qApp->exec(); - QVERIFY(testWidget.greenWidget->seenTouchBegin - && testWidget.greenWidget->seenTouchUpdate - && testWidget.greenWidget->seenTouchEnd); - QVERIFY(!testWidget.greenWidget->seenMousePress - && !testWidget.greenWidget->seenMouseMove - && !testWidget.greenWidget->seenMouseRelease); + QVERIFY(testWidget.greenWidget->seenTouchBegin); + // QVERIFY(testWidget.greenWidget->seenTouchUpdate); + QVERIFY(testWidget.greenWidget->seenTouchEnd); + QVERIFY(!testWidget.greenWidget->seenMousePress); + QVERIFY(!testWidget.greenWidget->seenMouseMove); + QVERIFY(!testWidget.greenWidget->seenMouseRelease); // again, ignoring the TouchEnd testWidget.greenWidget->reset(); @@ -112,7 +112,7 @@ void tst_ManualMultitouch::basicSingleTouchEventHandling() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); + // QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); QVERIFY(!testWidget.greenWidget->seenMousePress); QVERIFY(!testWidget.greenWidget->seenMouseMove); @@ -127,7 +127,7 @@ void tst_ManualMultitouch::basicSingleTouchEventHandling() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); + // QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); QVERIFY(!testWidget.greenWidget->seenMousePress); QVERIFY(!testWidget.greenWidget->seenMouseMove); @@ -142,7 +142,7 @@ void tst_ManualMultitouch::basicSingleTouchEventHandling() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); + // QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); QVERIFY(!testWidget.greenWidget->seenMousePress); QVERIFY(!testWidget.greenWidget->seenMouseMove); @@ -235,7 +235,7 @@ void tst_ManualMultitouch::acceptingTouchBeginStopsPropagation() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(testWidget.blueWidget->seenTouchUpdate); + // QVERIFY(testWidget.blueWidget->seenTouchUpdate); QVERIFY(testWidget.blueWidget->seenTouchEnd); QVERIFY(!testWidget.blueWidget->seenMousePress); QVERIFY(!testWidget.blueWidget->seenMouseMove); @@ -257,7 +257,7 @@ void tst_ManualMultitouch::acceptingTouchBeginStopsPropagation() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(testWidget.blueWidget->seenTouchUpdate); + // QVERIFY(testWidget.blueWidget->seenTouchUpdate); QVERIFY(testWidget.blueWidget->seenTouchEnd); QVERIFY(!testWidget.blueWidget->seenMousePress); QVERIFY(!testWidget.blueWidget->seenMouseMove); @@ -279,7 +279,7 @@ void tst_ManualMultitouch::acceptingTouchBeginStopsPropagation() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(testWidget.blueWidget->seenTouchUpdate); + // QVERIFY(testWidget.blueWidget->seenTouchUpdate); QVERIFY(testWidget.blueWidget->seenTouchEnd); QVERIFY(!testWidget.blueWidget->seenMousePress); QVERIFY(!testWidget.blueWidget->seenMouseMove); @@ -301,7 +301,7 @@ void tst_ManualMultitouch::acceptingTouchBeginStopsPropagation() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(testWidget.blueWidget->seenTouchUpdate); + // QVERIFY(testWidget.blueWidget->seenTouchUpdate); QVERIFY(testWidget.blueWidget->seenTouchEnd); QVERIFY(!testWidget.blueWidget->seenMousePress); QVERIFY(!testWidget.blueWidget->seenMouseMove); @@ -339,7 +339,7 @@ void tst_ManualMultitouch::ignoringTouchBeginPropagatesToParent() QVERIFY(!testWidget.greyWidget->seenMouseMove); QVERIFY(!testWidget.greyWidget->seenMouseRelease); QVERIFY(testWidget.redWidget->seenTouchBegin); - QVERIFY(testWidget.redWidget->seenTouchUpdate); + // QVERIFY(testWidget.redWidget->seenTouchUpdate); QVERIFY(testWidget.redWidget->seenTouchEnd); QVERIFY(!testWidget.redWidget->seenMousePress); QVERIFY(!testWidget.redWidget->seenMouseMove); @@ -362,7 +362,7 @@ void tst_ManualMultitouch::ignoringTouchBeginPropagatesToParent() QVERIFY(!testWidget.greyWidget->seenMouseMove); QVERIFY(!testWidget.greyWidget->seenMouseRelease); QVERIFY(testWidget.redWidget->seenTouchBegin); - QVERIFY(testWidget.redWidget->seenTouchUpdate); + // QVERIFY(testWidget.redWidget->seenTouchUpdate); QVERIFY(testWidget.redWidget->seenTouchEnd); QVERIFY(!testWidget.redWidget->seenMousePress); QVERIFY(!testWidget.redWidget->seenMouseMove); @@ -384,7 +384,7 @@ void tst_ManualMultitouch::ignoringTouchBeginPropagatesToParent() QVERIFY(!testWidget.greyWidget->seenMouseMove); QVERIFY(!testWidget.greyWidget->seenMouseRelease); QVERIFY(testWidget.redWidget->seenTouchBegin); - QVERIFY(testWidget.redWidget->seenTouchUpdate); + // QVERIFY(testWidget.redWidget->seenTouchUpdate); QVERIFY(testWidget.redWidget->seenTouchEnd); QVERIFY(!testWidget.redWidget->seenMousePress); QVERIFY(!testWidget.redWidget->seenMouseMove); @@ -406,7 +406,7 @@ void tst_ManualMultitouch::ignoringTouchBeginPropagatesToParent() QVERIFY(!testWidget.greyWidget->seenMouseMove); QVERIFY(!testWidget.greyWidget->seenMouseRelease); QVERIFY(testWidget.redWidget->seenTouchBegin); - QVERIFY(testWidget.redWidget->seenTouchUpdate); + // QVERIFY(testWidget.redWidget->seenTouchUpdate); QVERIFY(testWidget.redWidget->seenTouchEnd); QVERIFY(!testWidget.redWidget->seenMousePress); QVERIFY(!testWidget.redWidget->seenMouseMove); @@ -428,7 +428,7 @@ void tst_ManualMultitouch::ignoringTouchBeginPropagatesToParent() QVERIFY(!testWidget.greyWidget->seenMouseMove); QVERIFY(!testWidget.blueWidget->seenMouseRelease); QVERIFY(testWidget.redWidget->seenTouchBegin); - QVERIFY(testWidget.redWidget->seenTouchUpdate); + // QVERIFY(testWidget.redWidget->seenTouchUpdate); QVERIFY(testWidget.redWidget->seenTouchEnd); QVERIFY(!testWidget.redWidget->seenMousePress); QVERIFY(!testWidget.redWidget->seenMouseMove); @@ -449,7 +449,7 @@ void tst_ManualMultitouch::secondTouchPointOnParentGoesToChild() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(testWidget.blueWidget->seenTouchUpdate); + // QVERIFY(testWidget.blueWidget->seenTouchUpdate); QVERIFY(testWidget.blueWidget->seenTouchEnd); QVERIFY(!testWidget.blueWidget->seenMousePress); QVERIFY(!testWidget.blueWidget->seenMouseMove); @@ -477,7 +477,7 @@ void tst_ManualMultitouch::secondTouchPointOnChildGoesToParent() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.redWidget->seenTouchBegin); - QVERIFY(testWidget.redWidget->seenTouchUpdate); + // QVERIFY(testWidget.redWidget->seenTouchUpdate); QVERIFY(testWidget.redWidget->seenTouchEnd); QVERIFY(!testWidget.redWidget->seenMousePress); QVERIFY(!testWidget.redWidget->seenMouseMove); @@ -506,13 +506,13 @@ void tst_ManualMultitouch::secondTouchPointOnSiblingGoesToSibling() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.greenWidget->seenTouchBegin); - QVERIFY(testWidget.greenWidget->seenTouchUpdate); + // QVERIFY(testWidget.greenWidget->seenTouchUpdate); QVERIFY(testWidget.greenWidget->seenTouchEnd); QVERIFY(!testWidget.greenWidget->seenMousePress); QVERIFY(!testWidget.greenWidget->seenMouseMove); QVERIFY(!testWidget.greenWidget->seenMouseRelease); QVERIFY(testWidget.redWidget->seenTouchBegin); - QVERIFY(testWidget.redWidget->seenTouchUpdate); + // QVERIFY(testWidget.redWidget->seenTouchUpdate); QVERIFY(testWidget.redWidget->seenTouchEnd); QVERIFY(!testWidget.redWidget->seenMousePress); QVERIFY(!testWidget.redWidget->seenMouseMove); @@ -534,13 +534,13 @@ void tst_ManualMultitouch::secondTouchPointOnCousinGoesToCousin() testWidget.showMaximized(); (void) qApp->exec(); QVERIFY(testWidget.blueWidget->seenTouchBegin); - QVERIFY(testWidget.blueWidget->seenTouchUpdate); + // QVERIFY(testWidget.blueWidget->seenTouchUpdate); QVERIFY(testWidget.blueWidget->seenTouchEnd); QVERIFY(!testWidget.blueWidget->seenMousePress); QVERIFY(!testWidget.blueWidget->seenMouseMove); QVERIFY(!testWidget.blueWidget->seenMouseRelease); QVERIFY(testWidget.greyWidget->seenTouchBegin); - QVERIFY(testWidget.greyWidget->seenTouchUpdate); + // QVERIFY(testWidget.greyWidget->seenTouchUpdate); QVERIFY(testWidget.greyWidget->seenTouchEnd); QVERIFY(!testWidget.greyWidget->seenMousePress); QVERIFY(!testWidget.greyWidget->seenMouseMove); -- cgit v0.12 From 5d7e624593172a9901d0bb44d57fe6e7697fd113 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 8 May 2009 15:55:06 +0200 Subject: set qt_tabletChokeMouse to true whenever ANY touch event was accepted this makes sure we always get touch events, and the don't drop out seemingly randomly. --- src/gui/kernel/qapplication_win.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 60c6fd7..723a3b4 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4175,6 +4175,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) return false; bool returnValue = false; + qt_tabletChokeMouse = false; QHash::ConstIterator it = widgetsNeedingEvents.constBegin(); const QHash::ConstIterator end = widgetsNeedingEvents.constEnd(); @@ -4194,7 +4195,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) widget->setAttribute(Qt::WA_AcceptedTouchBeginEvent); bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent) && touchEvent.isAccepted(); - returnValue = returnValue || (qt_tabletChokeMouse = res); + returnValue = returnValue || (qt_tabletChokeMouse = qt_tabletChokeMouse || res); break; } case QEvent::TouchEnd: @@ -4206,8 +4207,6 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) if (widget->testAttribute(Qt::WA_AcceptedTouchBeginEvent)) { (void) QApplication::sendSpontaneousEvent(widget, &touchEvent); qt_tabletChokeMouse = true; - } else { - qt_tabletChokeMouse = false; } returnValue = returnValue || qt_tabletChokeMouse; break; -- cgit v0.12 From 1283c8734b137d1d7cc7c88e5e120e14c45852d1 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 8 May 2009 16:58:45 +0200 Subject: build the multitouch examples from the top-level build just needed to add multitouch to SUBDIRS in examples/examples.pro --- examples/examples.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/examples.pro b/examples/examples.pro index 41501a0..2d49103 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -20,7 +20,8 @@ SUBDIRS = \ widgets \ uitools \ xml \ - script + script \ + multitouch contains(QT_CONFIG, phonon):!static: SUBDIRS += phonon contains(QT_CONFIG, webkit): SUBDIRS += webkit -- cgit v0.12 From 771e18e96d86f09bb047e5cf45cc33568e259a5d Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 8 May 2009 17:13:09 +0200 Subject: by default, ignore touch events if the widget is disabled --- src/gui/kernel/qwidget.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index cbf9585..968a423 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -7525,6 +7525,9 @@ bool QWidget::event(QEvent *event) case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: case QEvent::ContextMenu: #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: -- cgit v0.12 From a3d8cfb1ee89e0600add864db53d67552820b95a Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 26 Feb 2009 15:39:00 +0100 Subject: Merge of the maemo-gestures branch onto qt/4.5.0 This is a squashed merge of all of the changes in the maemo-gestures branch on-top of the qt/4.5.0 branch. --- examples/gestures/gestures.pro | 11 + examples/gestures/graphicsview/graphicsview.pro | 2 + examples/gestures/graphicsview/main.cpp | 151 +++++++ examples/gestures/imageviewer/imageviewer.pro | 12 + examples/gestures/imageviewer/imagewidget.cpp | 338 +++++++++++++++ examples/gestures/imageviewer/imagewidget.h | 122 ++++++ examples/gestures/imageviewer/main.cpp | 88 ++++ src/corelib/global/qnamespace.h | 15 + src/corelib/kernel/qcoreevent.h | 2 + src/gui/graphicsview/qgraphicsitem.cpp | 28 ++ src/gui/graphicsview/qgraphicsitem.h | 3 + src/gui/graphicsview/qgraphicsitem_p.h | 2 + src/gui/graphicsview/qgraphicsscene.cpp | 62 +++ src/gui/graphicsview/qgraphicsscene.h | 2 + src/gui/graphicsview/qgraphicsscene_p.h | 9 + src/gui/graphicsview/qgraphicsview.cpp | 9 +- src/gui/kernel/kernel.pri | 14 +- src/gui/kernel/qapplication.cpp | 57 ++- src/gui/kernel/qapplication.h | 1 + src/gui/kernel/qapplication_p.h | 1 + src/gui/kernel/qapplication_x11.cpp | 2 +- src/gui/kernel/qdirectionrecognizer.cpp | 176 ++++++++ src/gui/kernel/qdirectionrecognizer_p.h | 121 ++++++ src/gui/kernel/qdirectionsimplificator_p.h | 172 ++++++++ src/gui/kernel/qevent.cpp | 24 ++ src/gui/kernel/qevent.h | 34 ++ src/gui/kernel/qgesture.cpp | 133 ++++++ src/gui/kernel/qgesture.h | 128 ++++++ src/gui/kernel/qgesture_p.h | 100 +++++ src/gui/kernel/qgesturemanager.cpp | 405 ++++++++++++++++++ src/gui/kernel/qgesturemanager_p.h | 102 +++++ src/gui/kernel/qgesturerecognizer.h | 83 ++++ src/gui/kernel/qgesturestandardrecognizers.cpp | 543 ++++++++++++++++++++++++ src/gui/kernel/qgesturestandardrecognizers_p.h | 169 ++++++++ src/gui/kernel/qwidget.cpp | 45 ++ src/gui/kernel/qwidget.h | 10 + src/gui/kernel/qwidget_p.h | 3 + src/gui/widgets/qabstractscrollarea.cpp | 1 + 38 files changed, 3174 insertions(+), 6 deletions(-) create mode 100644 examples/gestures/gestures.pro create mode 100644 examples/gestures/graphicsview/graphicsview.pro create mode 100644 examples/gestures/graphicsview/main.cpp create mode 100644 examples/gestures/imageviewer/imageviewer.pro create mode 100644 examples/gestures/imageviewer/imagewidget.cpp create mode 100644 examples/gestures/imageviewer/imagewidget.h create mode 100644 examples/gestures/imageviewer/main.cpp create mode 100644 src/gui/kernel/qdirectionrecognizer.cpp create mode 100644 src/gui/kernel/qdirectionrecognizer_p.h create mode 100644 src/gui/kernel/qdirectionsimplificator_p.h create mode 100644 src/gui/kernel/qgesture.cpp create mode 100644 src/gui/kernel/qgesture.h create mode 100644 src/gui/kernel/qgesture_p.h create mode 100644 src/gui/kernel/qgesturemanager.cpp create mode 100644 src/gui/kernel/qgesturemanager_p.h create mode 100644 src/gui/kernel/qgesturerecognizer.h create mode 100644 src/gui/kernel/qgesturestandardrecognizers.cpp create mode 100644 src/gui/kernel/qgesturestandardrecognizers_p.h diff --git a/examples/gestures/gestures.pro b/examples/gestures/gestures.pro new file mode 100644 index 0000000..1e0f1a7 --- /dev/null +++ b/examples/gestures/gestures.pro @@ -0,0 +1,11 @@ +TEMPLATE = \ + subdirs +SUBDIRS = \ + imageviewer \ + graphicsview + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/gestures +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS gestures.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/gestures +INSTALLS += target sources diff --git a/examples/gestures/graphicsview/graphicsview.pro b/examples/gestures/graphicsview/graphicsview.pro new file mode 100644 index 0000000..9cef564 --- /dev/null +++ b/examples/gestures/graphicsview/graphicsview.pro @@ -0,0 +1,2 @@ +SOURCES = main.cpp + diff --git a/examples/gestures/graphicsview/main.cpp b/examples/gestures/graphicsview/main.cpp new file mode 100644 index 0000000..a0236ef --- /dev/null +++ b/examples/gestures/graphicsview/main.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class PannableGraphicsView : public QGraphicsView +{ +public: + PannableGraphicsView() + { + grabGesture(Qt::Pan); + } +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *ge = static_cast(event); + if (const QGesture *g = ge->gesture(Qt::Pan)) { + QPoint pt = g->pos() - g->lastPos(); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() - pt.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() - pt.y()); + event->accept(); + return true; + } + } + return QGraphicsView::event(event); + } +}; + +class ImageItem : public QGraphicsItem +{ +public: + ImageItem() + : colored(false) + { + grabGesture(Qt::DoubleTap); + } + + QRectF boundingRect() const + { + return pixmap.isNull() ? QRectF(0, 0, 100, 100) + : QRectF(QPointF(0,0), QSizeF(pixmap.size())); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*) + { + if (pixmap.isNull()) { + painter->setBrush(QBrush( colored ? Qt::green : Qt::white)); + painter->drawRect(0, 0, 100, 100); + painter->drawLine(0, 0, 100, 100); + painter->drawLine(0, 100, 100, 0); + return; + } + painter->drawPixmap(0, 0, pixmap); + } + + bool sceneEvent(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *gestureEvent = static_cast(event); + if (gestureEvent->gesture(Qt::DoubleTap)) { + event->accept(); + colored = !colored; + update(); + return true; + } else { + qWarning("Item received unknown gesture"); + } + } + return QGraphicsItem::sceneEvent(event); + } + +private: + QPixmap pixmap; + bool colored; +}; + +class MainWidget : public QWidget +{ + Q_OBJECT + +public: + MainWidget(QWidget *parent = 0) + : QWidget(parent) + { + QVBoxLayout *l = new QVBoxLayout(this); + view = new PannableGraphicsView; + l->addWidget(view); + scene = new QGraphicsScene(0, 0, 1024, 768, view); + view->setScene(scene); + + ImageItem *item = new ImageItem; + scene->addItem(item); + item->setPos(scene->width()/3, scene->height()/3); + } + +signals: +public slots: +private: + QGraphicsView *view; + QGraphicsScene *scene; +}; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QApplication::setAttribute(Qt::AA_EnableGestures); + MainWidget w; + w.show(); + return app.exec(); +} + +#include "main.moc" diff --git a/examples/gestures/imageviewer/imageviewer.pro b/examples/gestures/imageviewer/imageviewer.pro new file mode 100644 index 0000000..4c35dce --- /dev/null +++ b/examples/gestures/imageviewer/imageviewer.pro @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Thu Sep 11 17:18:17 2008 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += imagewidget.h +SOURCES += imagewidget.cpp main.cpp diff --git a/examples/gestures/imageviewer/imagewidget.cpp b/examples/gestures/imageviewer/imagewidget.cpp new file mode 100644 index 0000000..8932e29 --- /dev/null +++ b/examples/gestures/imageviewer/imagewidget.cpp @@ -0,0 +1,338 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "imagewidget.h" + +#include + +ImageWidget::ImageWidget(QWidget *parent) + : QWidget(parent) +{ + setAttribute(Qt::WA_PaintOnScreen); + setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute(Qt::WA_NoSystemBackground); + + setObjectName("ImageWidget"); + + setMinimumSize(QSize(100,100)); + + position = 0; + zoomed = rotated = false; + + zoomedIn = false; + horizontalOffset = 0; + verticalOffset = 0; + + grabGesture(Qt::DoubleTap); + grabGesture(Qt::Pan); + grabGesture(Qt::LongTap); +} + +void ImageWidget::paintEvent(QPaintEvent*) +{ + QPainter p(this); + if (currentImage.isNull()) { + p.fillRect(geometry(), Qt::white); + return; + } + int hoffset = 0; + int voffset = 0; + const int w = pixmap.width(); + const int h = pixmap.height(); + p.save(); + if (zoomedIn) { + hoffset = horizontalOffset; + voffset = verticalOffset; + if (horizontalOffset > 0) + p.fillRect(0, 0, horizontalOffset, height(), Qt::white); + if (verticalOffset > 0) + p.fillRect(0, 0, width(), verticalOffset, Qt::white); + } + p.drawPixmap(hoffset, voffset, pixmap); + if (hoffset + w < width()) + p.fillRect(hoffset + w, 0, width() - w - hoffset, height(), Qt::white); + if (voffset + h < height()) + p.fillRect(0, voffset + h, width(), height() - h - voffset, Qt::white); + + // paint touch feedback + if (touchFeedback.tapped || touchFeedback.doubleTapped) { + p.setPen(QPen(Qt::gray, 2)); + p.drawEllipse(touchFeedback.position, 5, 5); + if (touchFeedback.doubleTapped) { + p.setPen(QPen(Qt::gray, 2, Qt::DotLine)); + p.drawEllipse(touchFeedback.position, 15, 15); + } else if (touchFeedback.tapAndHoldState != 0) { + QPoint pts[8] = { + touchFeedback.position + QPoint( 0, -15), + touchFeedback.position + QPoint( 10, -10), + touchFeedback.position + QPoint( 15, 0), + touchFeedback.position + QPoint( 10, 10), + touchFeedback.position + QPoint( 0, 15), + touchFeedback.position + QPoint(-10, 10), + touchFeedback.position + QPoint(-15, 0) + }; + for (int i = 0; i < (touchFeedback.tapAndHoldState-20)/10; ++i) + p.drawEllipse(pts[i], 3, 3); + } + } else if (touchFeedback.sliding) { + p.setPen(QPen(Qt::red, 3)); + QPoint endPos = QPoint(touchFeedback.position.x(), touchFeedback.slidingStartPosition.y()); + p.drawLine(touchFeedback.slidingStartPosition, endPos); + int dx = 10; + if (touchFeedback.slidingStartPosition.x() < endPos.x()) + dx = -1*dx; + p.drawLine(endPos, endPos + QPoint(dx, 5)); + p.drawLine(endPos, endPos + QPoint(dx, -5)); + } + + for (int i = 0; i < TouchFeedback::MaximumNumberOfTouches; ++i) { + if (touchFeedback.touches[i].isNull()) + break; + p.drawEllipse(touchFeedback.touches[i], 10, 10); + } + p.restore(); +} + +void ImageWidget::gestureEvent(QGestureEvent *event) +{ + touchFeedback.doubleTapped = false; + + Q_ASSERT(event); + if (event->contains(Qt::Tap)) { + // + } else if (const QGesture *g = event->gesture(Qt::DoubleTap)) { + touchFeedback.doubleTapped = true; + horizontalOffset = g->hotSpot().x() - currentImage.width()*1.0*g->hotSpot().x()/width(); + verticalOffset = g->hotSpot().y() - currentImage.height()*1.0*g->hotSpot().y()/height(); + setZoomedIn(!zoomedIn); + zoomed = rotated = false; + updateImage(); + } else if (const QGesture *g = event->gesture(Qt::Pan)) { + if (zoomedIn) { + // usual panning + if (g->state() == Qt::GestureStarted) + setCursor(Qt::SizeAllCursor); + else + setCursor(Qt::ArrowCursor); + const int dx = g->pos().x() - g->lastPos().x(); + const int dy = g->pos().y() - g->lastPos().y(); + horizontalOffset += dx; + verticalOffset += dy; + update(); + } else { + // only slide gesture should be accepted + const QPannableGesture *pg = dynamic_cast(g); + if (pg && pg->direction() != pg->lastDirection()) { + // ###: event->cancel(); + } + if (g->state() == Qt::GestureFinished) { + touchFeedback.sliding = false; + zoomed = rotated = false; + if (pg->direction() == QPannableGesture::Right) { + qDebug() << "slide right"; + goNextImage(); + } else if (pg->direction() == QPannableGesture::Left) { + qDebug() << "slide left"; + goPrevImage(); + } + updateImage(); + } + } + } else if (const QGesture *g = event->gesture(Qt::LongTap)) { + if (g->state() == Qt::GestureFinished) { + qDebug() << "tap and hold detected"; + touchFeedback.reset(); + update(); + + QMenu menu; + menu.addAction("Action 1"); + menu.addAction("Action 2"); + menu.addAction("Action 3"); + menu.exec(mapToGlobal(g->hotSpot())); + } + } else if (const QGesture *g = event->gesture(Qt::LongTap)) { + qDebug() << "long tap: " << (g->state() == Qt::GestureStarted ? "started" : "finished"); + } else { + qDebug() << "unknown gesture"; + } + feedbackFadeOutTimer.start(500, this); + event->accept(); +} + +void ImageWidget::resizeEvent(QResizeEvent*) +{ + updateImage(); +} + +void ImageWidget::updateImage() +{ + // should use qtconcurrent here? + transformation = QTransform(); + if (zoomedIn) { + } else { + if (currentImage.isNull()) + return; + if (zoomed) { + transformation = transformation.scale(zoom, zoom); + } else { + double xscale = (double)width()/currentImage.width(); + double yscale = (double)height()/currentImage.height(); + if (xscale < yscale) + yscale = xscale; + else + xscale = yscale; + transformation = transformation.scale(xscale, yscale); + } + if (rotated) + transformation = transformation.rotate(angle); + } + pixmap = QPixmap::fromImage(currentImage).transformed(transformation); + update(); +} + +void ImageWidget::openDirectory(const QString &path) +{ + this->path = path; + QDir dir(path); + QStringList nameFilters; + nameFilters << "*.jpg" << "*.png"; + files = dir.entryList(nameFilters, QDir::Files|QDir::Readable, QDir::Name); + + position = 0; + goToImage(0); + updateImage(); +} + +QImage ImageWidget::loadImage(const QString &fileName) +{ + QImageReader reader(fileName); + if (!reader.canRead()) { + qDebug() << fileName << ": can't load image"; + return QImage(); + } + QImage image; + if (!reader.read(&image)) { + qDebug() << fileName << ": corrupted image"; + return QImage(); + } + return image; +} + +void ImageWidget::setZoomedIn(bool zoomed) +{ + zoomedIn = zoomed; +} + +void ImageWidget::goNextImage() +{ + if (files.isEmpty()) + return; + if (position < files.size()-1) { + ++position; + prevImage = currentImage; + currentImage = nextImage; + if (position+1 < files.size()) + nextImage = loadImage(path+QLatin1String("/")+files.at(position+1)); + else + nextImage = QImage(); + } + setZoomedIn(false); + updateImage(); +} + +void ImageWidget::goPrevImage() +{ + if (files.isEmpty()) + return; + if (position > 0) { + --position; + nextImage = currentImage; + currentImage = prevImage; + if (position > 0) + prevImage = loadImage(path+QLatin1String("/")+files.at(position-1)); + else + prevImage = QImage(); + } + setZoomedIn(false); + updateImage(); +} + +void ImageWidget::goToImage(int index) +{ + if (files.isEmpty()) + return; + if (index < 0 || index >= files.size()) { + qDebug() << "goToImage: invalid index: " << index; + return; + } + if (index == position+1) { + goNextImage(); + return; + } + if (position > 0 && index == position-1) { + goPrevImage(); + return; + } + position = index; + pixmap = QPixmap(); + if (index > 0) + prevImage = loadImage(path+QLatin1String("/")+files.at(position-1)); + else + prevImage = QImage(); + currentImage = loadImage(path+QLatin1String("/")+files.at(position)); + if (position+1 < files.size()) + nextImage = loadImage(path+QLatin1String("/")+files.at(position+1)); + else + nextImage = QImage(); + setZoomedIn(false); + updateImage(); +} + +void ImageWidget::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == touchFeedback.tapTimer.timerId()) { + touchFeedback.tapTimer.stop(); + } else if (event->timerId() == feedbackFadeOutTimer.timerId()) { + feedbackFadeOutTimer.stop(); + touchFeedback.reset(); + } + update(); +} diff --git a/examples/gestures/imageviewer/imagewidget.h b/examples/gestures/imageviewer/imagewidget.h new file mode 100644 index 0000000..56fcb40 --- /dev/null +++ b/examples/gestures/imageviewer/imagewidget.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef IMAGEWIDGET_H +#define IMAGEWIDGET_H + +#include +#include +#include + +#include + +class ImageWidget : public QWidget +{ +public: + ImageWidget(QWidget *parent = 0); + + void openDirectory(const QString &path); + +protected: + void paintEvent(QPaintEvent*); + void gestureEvent(QGestureEvent *event); + void resizeEvent(QResizeEvent*); + void timerEvent(QTimerEvent*); + +private: + void updateImage(); + QImage loadImage(const QString &fileName); + void loadImage(); + void setZoomedIn(bool zoomed); + void goNextImage(); + void goPrevImage(); + void goToImage(int index); + + QString path; + QStringList files; + int position; + + QImage prevImage, nextImage; + QImage currentImage; + QPixmap pixmap; + QTransform transformation; + + bool zoomedIn; + int horizontalOffset; + int verticalOffset; + + bool zoomed; + qreal zoom; + bool rotated; + qreal angle; + + struct TouchFeedback + { + bool tapped; + QPoint position; + bool sliding; + QPoint slidingStartPosition; + QBasicTimer tapTimer; + int tapState; + bool doubleTapped; + int tapAndHoldState; + + enum { MaximumNumberOfTouches = 5 }; + QPoint touches[MaximumNumberOfTouches]; + + inline TouchFeedback() { reset(); } + inline void reset() + { + tapped = false; + sliding = false; + tapTimer.stop(); + tapState = 0; + doubleTapped = false; + tapAndHoldState = 0; + for (int i = 0; i < MaximumNumberOfTouches; ++i) { + touches[i] = QPoint(); + } + } + } touchFeedback; + QBasicTimer feedbackFadeOutTimer; +}; + +#endif diff --git a/examples/gestures/imageviewer/main.cpp b/examples/gestures/imageviewer/main.cpp new file mode 100644 index 0000000..5494b12 --- /dev/null +++ b/examples/gestures/imageviewer/main.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "imagewidget.h" + +class MainWidget : public QMainWindow +{ + Q_OBJECT + +public: + MainWidget(QWidget *parent = 0); + +public slots: + void openDirectory(const QString &path); + +private: + bool loadImage(const QString &fileName); + + ImageWidget *imageWidget; +}; + +MainWidget::MainWidget(QWidget *parent) + : QMainWindow(parent) +{ + resize(400, 300); + imageWidget = new ImageWidget(this); + setCentralWidget(imageWidget); +} + +void MainWidget::openDirectory(const QString &path) +{ + imageWidget->openDirectory(path); +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QApplication::setAttribute(Qt::AA_EnableGestures); + + MainWidget w; + w.show(); + + if (QApplication::arguments().size() > 1) + w.openDirectory(QApplication::arguments().at(1)); + return app.exec(); +} + +#include "main.moc" diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 1635f8a..5747c3c 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1550,6 +1550,21 @@ public: TouchPointStationary, TouchPointReleased }; + + typedef QString GestureType; + static const char UnknownGesture[] = "???"; + static const char Tap[] = "Tap"; + static const char DoubleTap[] = "DoubleTap"; + static const char LongTap[] = "LongTap"; + static const char Pan[] = "Pan"; + static const char Pinch[] = "Pinch"; + + enum GestureState + { + GestureStarted = 0, + GestureFinished = 1 + }; + } #ifdef Q_MOC_RUN ; diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 42a75f8..c9f9f24 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -273,6 +273,8 @@ public: GraphicsSceneTouchUpdate = 197, GraphicsSceneTouchEnd = 198, + Gesture = 191, + // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 3eef396..58f04f0 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -5788,6 +5788,20 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const return QVariant(); } +void QGraphicsItem::grabGesture(const Qt::GestureType &type) +{ + d_ptr->gestures.insert(type); + if (d_ptr->scene) + d_ptr->scene->d_func()->grabGesture(this, type); +} + +void QGraphicsItem::releaseGesture(const Qt::GestureType &type) +{ + d_ptr->gestures.remove(type); + if (d_ptr->scene) + d_ptr->scene->d_func()->releaseGesture(this, type); +} + /*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this @@ -5812,6 +5826,20 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const QVariant QGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value) { Q_UNUSED(change); + if (change == QGraphicsItem::ItemSceneChange) { + if (!qVariantValue(value)) { + // the item has been removed from a scene, unsubscribe gestures. + Q_ASSERT(d_ptr->scene); + foreach(const Qt::GestureType &gesture, d_ptr->gestures) + d_ptr->scene->d_func()->releaseGesture(this, gesture); + } + } else if (change == QGraphicsItem::ItemSceneHasChanged) { + if (QGraphicsScene *scene = qVariantValue(value)) { + // item has been added to the scene + foreach(const Qt::GestureType &gesture, d_ptr->gestures) + scene->d_func()->grabGesture(this, gesture); + } + } return value; } diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index ec3373a..42a5110 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -338,6 +338,9 @@ public: QVariant data(int key) const; void setData(int key, const QVariant &value); + void grabGesture(const Qt::GestureType &type); + void releaseGesture(const Qt::GestureType &type); + enum { Type = 1, UserType = 65536 diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 5e77dfd..a67a49a 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -54,6 +54,7 @@ // #include "qgraphicsitem.h" +#include "qset.h" #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW @@ -297,6 +298,7 @@ public: int siblingIndex; int index; int depth; + QSet gestures; // Packed 32 bytes quint32 acceptedMouseButtons : 5; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index f322305..fe6dde1 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3950,6 +3950,9 @@ bool QGraphicsScene::event(QEvent *event) // geometries that do not have an explicit style set. update(); break; + case QEvent::Gesture: + gestureEvent(static_cast(event)); + break; case QEvent::GraphicsSceneTouchBegin: d->touchBeginEvent(static_cast(event)); break; @@ -5586,6 +5589,65 @@ void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) } } +void QGraphicsScenePrivate::addView(QGraphicsView *view) +{ + views << view; + foreach(const Qt::GestureType &gesture, grabbedGestures) + view->grabGesture(gesture); +} + +void QGraphicsScenePrivate::removeView(QGraphicsView *view) +{ + views.removeAll(view); + foreach(const Qt::GestureType &gesture, grabbedGestures) + view->releaseGesture(gesture); +} + +void QGraphicsScenePrivate::sendGestureEvent(QGraphicsItem *item, QGestureEvent *event) +{ + //### TODO: position translation + sendEvent(item, event); +} + +void QGraphicsScene::gestureEvent(QGestureEvent *event) +{ + Q_D(QGraphicsScene); + QList gestureTypes = event->gestureTypes(); + QList pts; + QGraphicsView *view = qobject_cast(event->targetWidget()); + if (!view) { + // something is wrong. + Q_ASSERT(view); + return; + } + foreach(const Qt::GestureType &type, gestureTypes) + pts << view->mapToScene(event->gesture(type)->hotSpot()); + foreach(QGraphicsItem *item, d->itemsWithGestures) { + for (int i = 0; i < pts.size(); ++i) { + if (item->contains(item->mapFromScene(pts.at(i)))) { + d->sendGestureEvent(item, event); + if (event->isAccepted()) + break; + } + } + } +} + +void QGraphicsScenePrivate::grabGesture(QGraphicsItem *item, const Qt::GestureType &type) +{ + if (!grabbedGestures.contains(type)) { + foreach(QGraphicsView *view, views) + view->grabGesture(type); + } + itemsWithGestures << item; + grabbedGestures << type; +} + +void QGraphicsScenePrivate::releaseGesture(QGraphicsItem *item, const Qt::GestureType &type) +{ + //### +} + // ### FIXME: the code for touch event support is mosly copied from // ### QGraphicsScenePrivate::mousePressEventHandler() and friends, need to // ### refactor to reduce code duplication diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 9802f87..4680455 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -50,6 +50,7 @@ #include #include #include +#include QT_BEGIN_HEADER @@ -254,6 +255,7 @@ protected: virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); virtual void wheelEvent(QGraphicsSceneWheelEvent *event); virtual void inputMethodEvent(QInputMethodEvent *event); + virtual void gestureEvent(QGestureEvent *event); virtual void drawBackground(QPainter *painter, const QRectF &rect); virtual void drawForeground(QPainter *painter, const QRectF &rect); diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index de39205..cea0553 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -182,6 +182,9 @@ public: void storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event); QList views; + void addView(QGraphicsView *view); + void removeView(QGraphicsView *view); + bool painterStateProtection; QMultiMap sceneEventFilters; @@ -264,6 +267,12 @@ public: void resolvePalette(); void updatePalette(const QPalette &palette); + QSet itemsWithGestures; + QSet grabbedGestures; + void grabGesture(QGraphicsItem *item, const Qt::GestureType &type); + void releaseGesture(QGraphicsItem *item, const Qt::GestureType &type); + void sendGestureEvent(QGraphicsItem *item, QGestureEvent *event); + mutable QVector sceneTransformCache; mutable QBitArray validTransforms; mutable QVector freeSceneTransformSlots; diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 1aaaab9..acce717 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -1690,7 +1690,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) this, SLOT(updateScene(QList))); disconnect(d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect(QRectF))); - d->scene->d_func()->views.removeAll(this); + d->scene->d_func()->removeView(this); } // Assign the new scene and update the contents (scrollbars, etc.)). @@ -1698,7 +1698,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) connect(d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect(QRectF))); d->updateSceneSlotReimplementedChecked = false; - d->scene->d_func()->views << this; + d->scene->d_func()->addView(this); d->recalculateContentSize(); d->lastCenterPoint = sceneRect().center(); d->keepLastCenterPoint = true; @@ -2860,6 +2860,11 @@ bool QGraphicsView::event(QEvent *event) } } break; + case QEvent::Gesture: + QApplication::sendEvent(d->scene, event); + if (event->isAccepted()) + return true; + break; default: break; } diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index a1b982a..b6ef6b2 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -41,7 +41,13 @@ HEADERS += \ kernel/qwidgetaction.h \ kernel/qwidgetaction_p.h \ kernel/qwindowdefs.h \ - kernel/qkeymapper_p.h + kernel/qkeymapper_p.h \ + kernel/qgesture.h \ + kernel/qgesturemanager_p.h \ + kernel/qgesturerecognizer.h \ + kernel/qgesturestandardrecognizers_p.h \ + kernel/qdirectionrecognizer_p.h \ + kernel/qdirectionsimplificator_p.h SOURCES += \ kernel/qaction.cpp \ @@ -70,7 +76,11 @@ SOURCES += \ kernel/qwhatsthis.cpp \ kernel/qwidget.cpp \ kernel/qwidgetaction.cpp \ - kernel/qkeymapper.cpp + kernel/qkeymapper.cpp \ + kernel/qgesture.cpp \ + kernel/qgesturemanager.cpp \ + kernel/qgesturestandardrecognizers.cpp \ + kernel/qdirectionrecognizer.cpp win32 { DEFINES += QT_NO_DIRECTDRAW diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 1f4e1fe..b39cbc3 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -90,6 +90,8 @@ #include "qapplication.h" +#include + #ifdef Q_WS_WINCE #include "qdatetime.h" #include "qguifunctions_wince.h" @@ -98,6 +100,8 @@ extern bool qt_wince_is_mobile(); //qguifunctions_wince.cpp extern bool qt_wince_is_pocket_pc(); //qguifunctions_wince.cpp #endif +#include "qdatetime.h" + //#define ALIEN_DEBUG static void initResources() @@ -133,6 +137,8 @@ int QApplicationPrivate::autoMaximizeThreshold = -1; bool QApplicationPrivate::autoSipEnabled = false; #endif +QGestureManager *gestureManager = 0; + QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type) : QCoreApplicationPrivate(argc, argv) { @@ -3700,6 +3706,27 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QMouseEvent* mouse = static_cast(e); QPoint relpos = mouse->pos(); + if (QApplication::testAttribute(Qt::AA_EnableGestures)) { + if (!gestureManager) + gestureManager = new QGestureManager; + // if we are in gesture mode, we send all mouse events + // directly to gesture recognizer. + if (gestureManager->inGestureMode()) { + // ### should I send events through all application event filters? + if (gestureManager->filterEvent(e)) + return true; + } + if (w && (mouse->type() != QEvent::MouseMove || mouse->buttons() != 0)) { + // find the gesture target widget + QWidget *target = w; + while (target && target->gestures().isEmpty()) + target = target->parentWidget(); + if (target) { + gestureManager->setGestureTargetWidget(target); + res = gestureManager->filterEvent(e); + } + } + } if (e->spontaneous()) { if (e->type() == QEvent::MouseButtonPress) { QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, @@ -3993,6 +4020,35 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } break; #endif + + case QEvent::Gesture: { + QWidget *w = static_cast(receiver); + QGestureEvent *g = static_cast(e); + bool eventAccepted = g->isAccepted(); + // Q_ASSERT(g->gesture() != 0); + // const QGesture &gesture = *g->gesture(); + QPoint relPos(0,0); + while (w) { + // QGesture qge(gesture.gestureType(), gesture.startPos()+relPos, gesture.lastPos()+relPos, + // gesture.currentPos()+relPos, gesture.direction(), gesture.rect().translated(relPos), + // gesture.hotSpot()+relPos, gesture.state(), gesture.startTime()); + // QGestureEvent ge(&qge, false); + // ### TODO: fix widget-relative positions in gesture event. + QGestureEvent ge = *g; + ge.m_targetWidget = w; + ge.spont = g->spontaneous(); + res = d->notify_helper(w, w == receiver ? g : &ge); + g->spont = false; + eventAccepted = (w == receiver ? g : &ge)->isAccepted(); + if (res && eventAccepted) + break; + if (w->isWindow()) + break; + relPos += w->pos(); + w = w->parentWidget(); + } + break; + } case QEvent::TouchBegin: // Note: TouchUpdate and TouchEnd events are sent to d->currentMultitouchWidget and never propagated { @@ -4000,7 +4056,6 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QWidget *origin = widget; QTouchEvent *touchEvent = static_cast(e); bool eventAccepted = touchEvent->isAccepted(); - if (widget->testAttribute(Qt::WA_AcceptTouchEvents) && e->spontaneous()) { // give the widget focus if the focus policy allows it QApplicationPrivate::giveFocusAccordingToFocusPolicy(widget, diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index 2baf6dc..634226f 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -374,6 +374,7 @@ private: friend class QDirectPainter; friend class QDirectPainterPrivate; #endif + friend class QGestureManager; #if defined(Q_WS_WIN) friend QApplicationPrivate* getQApplicationPrivateInternal(); diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 1a2bad2..40e928d 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -428,6 +428,7 @@ public: void sendSyntheticEnterLeave(QWidget *widget); #endif + QMap grabbedGestures; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); #if defined(Q_WS_WIN) diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index 90376b3..060e948 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -4414,7 +4414,6 @@ bool QETWidget::translateMouseEvent(const XEvent *event) QMouseEvent e(type, pos, globalPos, button, buttons, modifiers); QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver); - if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { @@ -5022,6 +5021,7 @@ bool QETWidget::translatePropertyEvent(const XEvent *event) return true; } + // // Paint event translation // diff --git a/src/gui/kernel/qdirectionrecognizer.cpp b/src/gui/kernel/qdirectionrecognizer.cpp new file mode 100644 index 0000000..37b64e7 --- /dev/null +++ b/src/gui/kernel/qdirectionrecognizer.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectionrecognizer_p.h" + +#include + +QT_BEGIN_NAMESPACE + +static const int SIZE = 20; + +QDirectionSimpleRecognizer::QDirectionSimpleRecognizer() +{ +} + +Direction QDirectionSimpleRecognizer::addPosition(const QPoint &pos) +{ + if (!directions.isEmpty()) { + const QPoint tmp = pos - directions.back().point; + if (tmp.manhattanLength() < 5) + return Direction(); + } + if (lastPoint.isNull()) { + lastPoint = pos; + return Direction(); + } + int dx = pos.x() - lastPoint.x(); + int dy = pos.y() - lastPoint.y(); + Direction::DirectionType direction = Direction::None; + if (dx < 0) { + if (-1*dx >= SIZE/2) + direction = Direction::Left; + } else { + if (dx >= SIZE/2) + direction = Direction::Right; + } + if (dy < 0) { + if (-1*dy >= SIZE/2) + direction = Direction::Up; + } else { + if (dy >= SIZE/2) + direction = Direction::Down; + } + if (direction == Direction::None) + return Direction(); + + lastPoint = pos; + directions.push_back(Direction(direction, pos)); + return Direction(direction, pos); +} + + +DirectionList QDirectionSimpleRecognizer::getDirections() const +{ + return directions; +} + +void QDirectionSimpleRecognizer::reset() +{ + directions.clear(); + lastPoint = QPoint(); +} + + +/// QDirectionDiagonalRecognizer + +QDirectionDiagonalRecognizer::QDirectionDiagonalRecognizer() +{ +} + +Direction QDirectionDiagonalRecognizer::addPosition(const QPoint &pos) +{ + if (!directions.isEmpty()) { + const QPoint tmp = pos - directions.back().point; + if (tmp.manhattanLength() < 5) + return Direction(); + } + if (lastPoint.isNull()) { + lastPoint = pos; + return Direction(); + } + int dx = pos.x() - lastPoint.x(); + int dy = pos.y() - lastPoint.y(); + int distance = sqrt(static_cast(dx*dx + dy*dy)); + if (distance < SIZE/2) + return Direction(); + + Direction::DirectionType direction = Direction::None; + double angle = atan(1.0*qAbs(lastPoint.y() - pos.y())/qAbs(pos.x() - lastPoint.x())) * 180. / M_PI; + if (dx < 0 && dy <= 0) { + angle = 180 - angle; + } else if (dx <= 0 && dy > 0) { + angle += 180; + } else if (dx > 0 && dy > 0) { + angle = 360-angle; + } + if (angle < 0) + angle += 360; + if (angle <= 20) + direction = Direction::Right; + else if (angle <= 65) + direction = Direction::RightUp; + else if (angle <= 110) + direction = Direction::Up; + else if (angle <= 155) + direction = Direction::LeftUp; + else if (angle <= 200) + direction = Direction::Left; + else if (angle <= 245) + direction = Direction::LeftDown; + else if (angle <= 290) + direction = Direction::Down; + else if (angle <= 335) + direction = Direction::RightDown; + else + direction = Direction::Right; + + if (direction == Direction::None) + return Direction(); + + lastPoint = pos; + directions.push_back(Direction(direction, pos)); + return Direction(direction, pos); +} + + +DirectionList QDirectionDiagonalRecognizer::getDirections() const +{ + return directions; +} + +void QDirectionDiagonalRecognizer::reset() +{ + directions.clear(); + lastPoint = QPoint(); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qdirectionrecognizer_p.h b/src/gui/kernel/qdirectionrecognizer_p.h new file mode 100644 index 0000000..6390990 --- /dev/null +++ b/src/gui/kernel/qdirectionrecognizer_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTIONRECOGNIZER_P_H +#define QDIRECTIONRECOGNIZER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpoint.h" +#include "qlist.h" + +QT_BEGIN_NAMESPACE + +struct Direction +{ + enum DirectionType + { + None = 0, + LeftDown = 1, + DownLeft = LeftDown, + Down = 2, + RightDown = 3, + DownRight = RightDown, + Left = 4, + Right = 6, + LeftUp = 7, + UpLeft = LeftUp, + Up = 8, + RightUp = 9, + UpRight = RightUp + }; + DirectionType direction; + QPoint point; + + Direction(DirectionType dir, const QPoint &pt) + : direction(dir), point(pt) { } + Direction() + : direction(None) { } + + inline bool isEmpty() const { return direction == None; } + inline bool isNull() const { return direction == None; } +}; + +typedef QList DirectionList; + +class QDirectionSimpleRecognizer +{ +public: + QDirectionSimpleRecognizer(); + Direction addPosition(const QPoint &pos); + DirectionList getDirections() const; + void reset(); + +private: + QPoint lastPoint; + DirectionList directions; +}; + +class QDirectionDiagonalRecognizer +{ +public: + QDirectionDiagonalRecognizer(); + Direction addPosition(const QPoint &pos); + DirectionList getDirections() const; + void reset(); + +private: + QPoint lastPoint; + DirectionList directions; +}; + +QT_END_NAMESPACE + +#endif // QDIRECTIONRECOGNIZER_P_H diff --git a/src/gui/kernel/qdirectionsimplificator_p.h b/src/gui/kernel/qdirectionsimplificator_p.h new file mode 100644 index 0000000..7b71ca0 --- /dev/null +++ b/src/gui/kernel/qdirectionsimplificator_p.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTIONSIMPLIFICATOR_P_H +#define QDIRECTIONSIMPLIFICATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qdirectionrecognizer_p.h" + +QT_BEGIN_NAMESPACE + +class QDirectionSimplificator +{ +public: + QDirectionSimplificator(const DirectionList &dir); + + bool simplify(DirectionList *result); + +private: + DirectionList directions; + DirectionList lastResult; + enum State { + None, + Trim, // remove first and last element + AccidentalMoves, // 66866 => 6666 + ComplexAccidentalMoves, // 778788 => 777888 (swapping elements without changing direction) + ShortMoves, // (moves of length 1) + } state; + + struct SimplifyTrim + { + SimplifyTrim() : state(0) { } + bool operator()(DirectionList &directions) + { + if (state == 0) { + directions.removeFirst(); + state = 1; + } else if (state == 1) { + directions.removeLast(); + state = 2; + } else if (state == 2 && directions.size() >= 2) { + directions.removeFirst(); + directions.removeLast(); + state = 3; + } else { + return false; + } + return true; + } + int state; + }; + struct SimplifyAccidentalMoves + { + SimplifyAccidentalMoves() : state(0) { } + bool operator()(DirectionList &directions) + { + return false; + } + int state; + }; + struct SimplifyComplexAccidentalMoves + { + SimplifyComplexAccidentalMoves() : state(0) { } + bool operator()(DirectionList &directions) + { + return false; + } + int state; + }; + + SimplifyTrim trim; + SimplifyAccidentalMoves accidentalMoves; + SimplifyComplexAccidentalMoves complexAccidentalMoves; + //SimplifyShortMoves shortMoves; +}; + +QDirectionSimplificator::QDirectionSimplificator(const DirectionList &dir) + : directions(dir), state(None) +{ +} + +bool QDirectionSimplificator::simplify(DirectionList *result) +{ + if (directions.isEmpty() || !result) + return false; + *result = directions; + switch(state) { + case None: + state = Trim; + trim = SimplifyTrim(); + case Trim: + if (trim(*result)) + break; + *result = lastResult; + state = AccidentalMoves; + accidentalMoves = SimplifyAccidentalMoves(); + case AccidentalMoves: + if (accidentalMoves(*result)) + break; + *result = lastResult; + state = ComplexAccidentalMoves; + complexAccidentalMoves = SimplifyComplexAccidentalMoves(); + case ComplexAccidentalMoves: + if (complexAccidentalMoves(*result)) + break; + *result = lastResult; + // state = ShortMoves; + // shortMoves = SimplifyShortMoves(); + // case ShortMoves: + // if (shortMoves(*result)) + // break; + // state = None; + default: + return false; + } + lastResult = *result; + if (lastResult.isEmpty()) + return false; + return true; +} + +QT_END_NAMESPACE + +#endif // QDIRECTIONSIMPLIFICATOR_P_H diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index d571212..a7e1101 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3310,6 +3310,9 @@ QDebug operator<<(QDebug dbg, const QEvent *e) { case QEvent::ChildRemoved: n = n ? n : "ChildRemoved"; dbg.nospace() << "QChildEvent(" << n << ", " << (static_cast(e))->child(); return dbg.space(); + case QEvent::Gesture: + n = "Gesture"; + break; default: dbg.nospace() << "QEvent(" << (const void *)e << ", type = " << e->type() << ')'; return dbg.space(); @@ -3506,6 +3509,27 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) #endif +QGestureEvent::QGestureEvent(QWidget *targetWidget, const QList &gestures, + const QSet &cancelledGestures) + : QEvent(QEvent::Gesture), m_targetWidget(targetWidget), + m_cancelledGestures(cancelledGestures) +{ + foreach(QGesture *r, gestures) + m_gestures.insert(r->gestureType(), QSharedPointer(r)); +} + +QGestureEvent::QGestureEvent(const QGestureEvent &event, const QPoint &offset) + : QEvent(QEvent::Gesture), m_targetWidget(event.m_targetWidget), + m_gestures(event.m_gestures), + m_cancelledGestures(event.m_cancelledGestures) +{ + //### use offset! +} + +QGestureEvent::~QGestureEvent() +{ +} + /*! \class QTouchEvent \brief The QTouchEvent class contains parameters that describe a touch event . diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index e9a1386..858e2ea 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -52,6 +52,10 @@ #include #include #include +#include +#include +#include +#include QT_BEGIN_HEADER @@ -710,6 +714,36 @@ private: }; #endif +class Q_GUI_EXPORT QGestureEvent : public QEvent +{ +public: + QGestureEvent(QWidget *targetWidget, const QList &gestures, + const QSet &cancelledGestures = QSet()); + // internal ctor + QGestureEvent(const QGestureEvent &gestures, const QPoint &offset); + ~QGestureEvent(); + + QWidget *targetWidget() const + { return m_targetWidget; } + + inline bool contains(const Qt::GestureType &gestureType) const + { return gesture(gestureType) != 0; } + inline QList gestureTypes() const + { return m_gestures.keys(); } + inline const QGesture* gesture(const Qt::GestureType &gestureType) const + { return m_gestures.value(gestureType, QSharedPointer()).data(); } + + inline QSet cancelledGestures() const + { return m_cancelledGestures; } + +protected: + QWidget *m_targetWidget; + QHash > m_gestures; + QSet m_cancelledGestures; + + friend class QApplication; +}; + #ifndef QT_NO_DEBUG_STREAM Q_GUI_EXPORT QDebug operator<<(QDebug, const QEvent *); #endif diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp new file mode 100644 index 0000000..fbabb8f --- /dev/null +++ b/src/gui/kernel/qgesture.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesture.h" +#include + +QGesture::QGesture(const Qt::GestureType &type, Qt::GestureState state) + : d(new QGesturePrivate), gestureType_(type), gestureState_(state) +{ +} + +QGesture::QGesture(const Qt::GestureType &type, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state) + : d(new QGesturePrivate), gestureType_(type), gestureState_(state) +{ + d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); +} + +QGesture::QGesture(QGesturePrivate &dd, const Qt::GestureType &type, + Qt::GestureState state) + : d(&dd), gestureType_(type), gestureState_(state) +{ +} + +QGesture::~QGesture() +{ + delete d; d = 0; +} + +QRect QGesture::rect() const +{ + return d->rect; +} + +QPoint QGesture::hotSpot() const +{ + return d->hotSpot; +} + +QDateTime QGesture::startTime() const +{ + return d->startTime; +} + +uint QGesture::duration() const +{ + return d->duration; +} + +QPoint QGesture::startPos() const +{ + return d->startPos; +} + +QPoint QGesture::lastPos() const +{ + return d->lastPos; +} + +QPoint QGesture::pos() const +{ + return d->pos; +} + +QPannableGesture::QPannableGesture(const Qt::GestureType &type, Qt::GestureState state) + : QGesture(*new QPannableGesturePrivate, type, state) +{ +} + +QPannableGesture::QPannableGesture(const Qt::GestureType &type, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state) + : QGesture(*new QPannableGesturePrivate, type, state) +{ + d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); + ((QPannableGesturePrivate*)d)->lastDirection = QPannableGesture::None; + ((QPannableGesturePrivate*)d)->direction = QPannableGesture::None; +} + +QPannableGesture::~QPannableGesture() +{ +} + +QPannableGesture::DirectionType QPannableGesture::lastDirection() const +{ + return ((QPannableGesturePrivate*)d)->lastDirection; +} + +QPannableGesture::DirectionType QPannableGesture::direction() const +{ + return ((QPannableGesturePrivate*)d)->direction; +} diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h new file mode 100644 index 0000000..16a5704 --- /dev/null +++ b/src/gui/kernel/qgesture.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURE_H +#define QGESTURE_H + +#include "qobject.h" +#include "qlist.h" +#include "qdatetime.h" +#include "qpoint.h" +#include "qrect.h" +#include "qsharedpointer.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGesturePrivate; +class Q_GUI_EXPORT QGesture +{ +public: + explicit QGesture(const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted); + QGesture(const Qt::GestureType &type, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state); + virtual ~QGesture(); + + inline Qt::GestureType gestureType() const { return gestureType_; } + inline Qt::GestureState state() const { return gestureState_; } + + QRect rect() const; + QPoint hotSpot() const; + QDateTime startTime() const; + uint duration() const; + + QPoint startPos() const; + QPoint lastPos() const; + QPoint pos() const; + +protected: + QGesture(QGesturePrivate &dd, const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted); + QGesturePrivate *d; + +private: + Qt::GestureType gestureType_; + Qt::GestureState gestureState_; +}; + +class Q_GUI_EXPORT QPannableGesture : public QGesture +{ +public: + enum DirectionType + { + None = 0, + LeftDown = 1, + DownLeft = LeftDown, + Down = 2, + RightDown = 3, + DownRight = RightDown, + Left = 4, + Right = 6, + LeftUp = 7, + UpLeft = LeftUp, + Up = 8, + RightUp = 9, + UpRight = RightUp + }; + +public: + explicit QPannableGesture(const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted); + QPannableGesture(const Qt::GestureType &type, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state); + ~QPannableGesture(); + + DirectionType lastDirection() const; + DirectionType direction() const; + + friend class QGestureRecognizerPan; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGESTURE_H diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h new file mode 100644 index 0000000..745590b --- /dev/null +++ b/src/gui/kernel/qgesture_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURE_P_H +#define QGESTURE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrect.h" +#include "qpoint.h" +#include "qdatetime.h" + +QT_BEGIN_NAMESPACE + +class QGesturePrivate +{ +public: + QGesturePrivate() + : duration(0) { } + + void init(const QPoint &startPos, const QPoint &lastPos, + const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration) + { + this->rect = rect; + this->hotSpot = hotSpot; + this->startTime = startTime; + this->duration = duration; + this->startPos = startPos; + this->lastPos = lastPos; + this->pos = pos; + } + + QRect rect; + QPoint hotSpot; + QDateTime startTime; + uint duration; + QPoint startPos; + QPoint lastPos; + QPoint pos; +}; + +class QPannableGesturePrivate : public QGesturePrivate +{ +public: + QPannableGesture::DirectionType lastDirection; + QPannableGesture::DirectionType direction; +}; + +QT_END_NAMESPACE + +#endif // QGESTURE_P_H diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp new file mode 100644 index 0000000..c244a0b --- /dev/null +++ b/src/gui/kernel/qgesturemanager.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesturemanager_p.h" +#include "qgesture.h" +#include "qevent.h" + +#include "qapplication.h" +#include "private/qapplication_p.h" + +#include "private/qgesturestandardrecognizers_p.h" + +#include "qdebug.h" + +// #define GESTURE_DEBUG +#ifndef GESTURE_DEBUG +# define DEBUG if (0) qDebug +#else +# define DEBUG qDebug +#endif + +QT_BEGIN_NAMESPACE + +bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); +static bool qt_sendGestureEvent(QWidget *receiver, QGestureEvent *event) +{ + QSet eventGestures = event->gestureTypes().toSet(); + while (receiver && (receiver->gestures() & eventGestures).isEmpty()) + receiver = receiver->parentWidget(); + return receiver ? qt_sendSpontaneousEvent(receiver, event) : false; +} + +static const unsigned int maximumGestureRecognitionTimeout = 2000; + +QGestureManager::QGestureManager() + : targetWidget(0), state(NotGesture) +{ + recognizers << new QDoubleTapGestureRecognizer(); + recognizers << new QLongTapGestureRecognizer(); + recognizers << new QGestureRecognizerPan(); + // recognizers << new QMultiTouchGestureRecognizer(); + + foreach(QGestureRecognizer *r, recognizers) + connect(r, SIGNAL(triggered(QGestureRecognizer::Result)), + this, SLOT(recognizerTriggered(QGestureRecognizer::Result))); +} + +bool QGestureManager::filterEvent(QEvent *event) +{ + if (!QApplication::testAttribute(Qt::AA_EnableGestures)) + return false; + + QList events; + events << event; + + QPoint currentPos; + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + currentPos = static_cast(event)->pos(); break; + default: break; + } + + const QMap &grabbedGestures = qApp->d_func()->grabbedGestures; + + bool ret = false; + QSet startedGestures; + QSet finishedGestures; + QSet newMaybeGestures; + QSet cancelledGestures; + QSet notGestures; + if (state == NotGesture || state == MaybeGesture) { + DEBUG() << "QGestureManager: current event processing state: " + << (state == NotGesture ? "NotGesture" : "MaybeGesture"); + + Q_ASSERT(targetWidget != 0); + QSet stillMaybeGestures; + // try other recognizers. + foreach(QGestureRecognizer *r, recognizers) { + if (grabbedGestures.value(r->gestureType(), 0) <= 0) + continue; + QGestureRecognizer::Result result = r->recognize(events); + if (result == QGestureRecognizer::GestureStarted) { + DEBUG() << "QGestureManager: gesture started: " << r; + startedGestures << r; + } else if (result == QGestureRecognizer::GestureFinished) { + DEBUG() << "QGestureManager: gesture finished: " << r; + finishedGestures << r; + } else if (result == QGestureRecognizer::MaybeGesture) { + DEBUG() << "QGestureManager: maybe gesture: " << r; + newMaybeGestures << r; + } else { + // if it was maybe gesture, but isn't a gesture anymore. + DEBUG() << "QGestureManager: not gesture: " << r; + notGestures << r; + } + } + activeGestures -= newMaybeGestures; + activeGestures += startedGestures; + foreach(QGestureRecognizer *r, startedGestures+notGestures) { + QMap::iterator it = maybeGestures.find(r); + if (it != maybeGestures.end()) { + killTimer(it.value()); + maybeGestures.erase(it); + } + } + foreach(QGestureRecognizer *r, newMaybeGestures) { + if (!maybeGestures.contains(r)) { + int timerId = startTimer(maximumGestureRecognitionTimeout); + if (!timerId) + qWarning("QGestureManager: couldn't start timer!"); + maybeGestures.insert(r, timerId); + } + } + if (!finishedGestures.isEmpty() || !activeGestures.isEmpty()) { + // gesture found! + ret = true; + DEBUG() << "QGestureManager: sending gesture event for: " + << activeGestures << " and " << finishedGestures; + + QList gestures; + foreach(QGestureRecognizer *r, finishedGestures) { + if (QGesture *gesture = r->makeEvent()) + gestures << gesture; + } + foreach(QGestureRecognizer *r, activeGestures) { + if (QGesture *gesture = r->makeEvent()) + gestures << gesture; + } + Q_ASSERT(!gestures.isEmpty()); + QGestureEvent event(targetWidget, gestures); + qt_sendGestureEvent(targetWidget, &event); + + if (!activeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = Gesture"; + state = Gesture; + } else if (!maybeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = Maybe"; + state = MaybeGesture; + } else { + DEBUG() << "QGestureManager: new state = NotGesture"; + state = NotGesture; + } + } else if (!maybeGestures.isEmpty()) { + if (state != MaybeGesture) { + // We got a new set of events that look like a start + // of some gesture, so we switch to state MaybeGesture + // and wait for more events. + DEBUG() << "QGestureManager: new state = Maybe. Waiting for events"; + state = MaybeGesture; + // start gesture timer + } else { + // we still not sure if it is a gesture or not. + } + } else if (state == MaybeGesture) { + // last time we thought it looks like gesture, but now we + // know for sure that it isn't. + DEBUG() << "QGestureManager: new state = NotGesture"; + state = NotGesture; + } + foreach(QGestureRecognizer *r, finishedGestures) + r->reset(); + foreach(QGestureRecognizer *r, cancelledGestures) + r->reset(); + foreach(QGestureRecognizer *r, notGestures) + r->reset(); + } else if (state == Gesture) { + DEBUG() << "QGestureManager: current event processing state: Gesture"; + Q_ASSERT(!activeGestures.isEmpty()); + + foreach(QGestureRecognizer *r, recognizers) { + if (grabbedGestures.value(r->gestureType(), 0) <= 0) + continue; + QGestureRecognizer::Result result = r->recognize(events); + if (result == QGestureRecognizer::GestureStarted) { + DEBUG() << "QGestureManager: gesture started: " << r; + startedGestures << r; + } else if (result == QGestureRecognizer::GestureFinished) { + DEBUG() << "QGestureManager: gesture finished: " << r; + finishedGestures << r; + } else if (result == QGestureRecognizer::MaybeGesture) { + DEBUG() << "QGestureManager: maybe gesture: " << r; + newMaybeGestures << r; + } else { + // if it was an active gesture, but isn't a gesture anymore. + if (activeGestures.contains(r)) { + DEBUG() << "QGestureManager: cancelled gesture: " << r; + cancelledGestures << r; + } else { + DEBUG() << "QGestureManager: not gesture: " << r; + notGestures << r; + } + } + } + + activeGestures -= newMaybeGestures; + activeGestures -= cancelledGestures; + activeGestures -= finishedGestures; + activeGestures += startedGestures; + foreach(QGestureRecognizer *r, startedGestures+finishedGestures+notGestures) { + QMap::iterator it = maybeGestures.find(r); + if (it != maybeGestures.end()) { + killTimer(it.value()); + maybeGestures.erase(it); + } + } + foreach(QGestureRecognizer *r, newMaybeGestures) { + if (!maybeGestures.contains(r)) { + int timerId = startTimer(maximumGestureRecognitionTimeout); + if (!timerId) + qWarning("QGestureManager: couldn't start timer!"); + maybeGestures.insert(r, timerId); + } + } + QList gestures; + if (!finishedGestures.isEmpty() || !activeGestures.isEmpty()) { + // another gesture found! + ret = true; + DEBUG() << "QGestureManager: sending gesture event for: " + << activeGestures << " and " << finishedGestures; + + foreach(QGestureRecognizer *r, finishedGestures) { + if (QGesture *gesture = r->makeEvent()) + gestures << gesture; + } + foreach(QGestureRecognizer *r, activeGestures) { + if (QGesture *gesture = r->makeEvent()) + gestures << gesture; + } + } + QSet cancelledGestureNames; + foreach(QGestureRecognizer *r, cancelledGestures) + cancelledGestureNames << r->gestureType(); + if(!gestures.isEmpty()) { + QGestureEvent event(targetWidget, gestures, cancelledGestureNames); + qt_sendGestureEvent(targetWidget, &event); + } + + foreach(QGestureRecognizer *r, finishedGestures) + r->reset(); + foreach(QGestureRecognizer *r, cancelledGestures) + r->reset(); + foreach(QGestureRecognizer *r, notGestures) + r->reset(); + if (!activeGestures.isEmpty()) { + // nothing changed, we are still handling a gesture + } else if (!maybeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = Maybe. Waiting for events: " << maybeGestures; + state = MaybeGesture; + } else { + DEBUG() << "QGestureManager: new state = NotGesture"; + state = NotGesture; + } + ret = true; + } + + lastPos = currentPos; + return ret; +} + +void QGestureManager::timerEvent(QTimerEvent *event) +{ + // sanity checks, remove later + Q_ASSERT((state == Gesture && !activeGestures.isEmpty()) || (state != Gesture && activeGestures.isEmpty())); + + typedef QMap MaybeGestureMap; + for (MaybeGestureMap::iterator it = maybeGestures.begin(), e = maybeGestures.end(); + it != e; ++it) { + if (it.value() == event->timerId()) { + DEBUG() << "QGestureManager: gesture timeout."; + QGestureRecognizer *r = it.key(); + r->reset(); + maybeGestures.erase(it); + killTimer(event->timerId()); + break; + } + } + + if (state == MaybeGesture && maybeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = NotGesture because of timeout"; + state = NotGesture; + } +} + +bool QGestureManager::inGestureMode() +{ + return state == Gesture; +} + +void QGestureManager::setGestureTargetWidget(QWidget *widget) +{ + targetWidget = widget; +} + +void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) +{ + if (!QApplication::testAttribute(Qt::AA_EnableGestures)) + return; + + QGestureRecognizer *recognizer = qobject_cast(sender()); + if (!recognizer) + return; + if (qApp->d_func()->grabbedGestures.value(recognizer->gestureType(), 0) <= 0) { + recognizer->reset(); + return; + } + + switch (result) { + case QGestureRecognizer::GestureStarted: + case QGestureRecognizer::GestureFinished: { + if (result == QGestureRecognizer::GestureStarted) { + DEBUG() << "QGestureManager: gesture started: " << recognizer; + activeGestures << recognizer; + DEBUG() << "QGestureManager: new state = Gesture"; + state = Gesture; + } else { + DEBUG() << "QGestureManager: gesture finished: " << recognizer; + } + if (maybeGestures.contains(recognizer)) { + killTimer(maybeGestures.value(recognizer)); + maybeGestures.remove(recognizer); + } + QList gestures; + if (QGesture *gesture = recognizer->makeEvent()) + gestures << gesture; + if(!gestures.isEmpty()) { + QGestureEvent event(targetWidget, gestures); + qt_sendGestureEvent(targetWidget, &event); + } + if (result == QGestureRecognizer::GestureFinished) + recognizer->reset(); + } + break; + case QGestureRecognizer::MaybeGesture: { + DEBUG() << "QGestureManager: maybe gesture: " << recognizer; + if (activeGestures.contains(recognizer)) { + QGestureEvent event(targetWidget, QList(), + QSet() << recognizer->gestureType()); + qt_sendGestureEvent(targetWidget, &event); + } + if (!maybeGestures.contains(recognizer)) { + int timerId = startTimer(maximumGestureRecognitionTimeout); + if (!timerId) + qWarning("QGestureManager: couldn't start timer!"); + maybeGestures.insert(recognizer, timerId); + } + } + break; + case QGestureRecognizer::NotGesture: + DEBUG() << "QGestureManager: not gesture: " << recognizer; + if (maybeGestures.contains(recognizer)) { + killTimer(maybeGestures.value(recognizer)); + maybeGestures.remove(recognizer); + } + recognizer->reset(); + break; + default: + Q_ASSERT(false); + } +} + +QT_END_NAMESPACE + +#include "moc_qgesturemanager_p.cpp" + diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h new file mode 100644 index 0000000..9853f1a --- /dev/null +++ b/src/gui/kernel/qgesturemanager_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTUREMANAGER_P_H +#define QGESTUREMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qlist.h" +#include "qset.h" +#include "qevent.h" +#include "qbasictimer.h" + +#include "qgesturerecognizer.h" + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QGestureManager : public QObject +{ + Q_OBJECT +public: + QGestureManager(); + + // should be internal + void setGestureTargetWidget(QWidget *widget); + + bool filterEvent(QEvent *event); + bool inGestureMode(); + +protected: + void timerEvent(QTimerEvent *event); + +private slots: + void recognizerTriggered(QGestureRecognizer::Result); + +private: + QSet activeGestures; + QMap maybeGestures; + QSet recognizers; + + QWidget *targetWidget; + QPoint lastPos; + + enum State { + Gesture, + NotGesture, + MaybeGesture // that mean timers are up and waiting for some + // more events, and input events are handled by + // gesture recognizer explicitely + } state; +}; + +QT_END_NAMESPACE + +#endif // QGESTUREMANAGER_P_H diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h new file mode 100644 index 0000000..3ed96c0 --- /dev/null +++ b/src/gui/kernel/qgesturerecognizer.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURERECOGNIZER_H +#define QGESTURERECOGNIZER_H + +#include "qgesture.h" +#include "qevent.h" +#include "qlist.h" +#include "qset.h" + +QT_BEGIN_NAMESPACE + +class QGestureRecognizer : public QObject +{ + Q_OBJECT +public: + enum Result + { + NotGesture, + GestureStarted, + GestureFinished, + MaybeGesture + }; + + inline QGestureRecognizer() { } + //### remove this ctor + inline QGestureRecognizer(const char *type) : type_(QLatin1String(type)) { } + inline QGestureRecognizer(const Qt::GestureType &type) : type_(type) { } + inline Qt::GestureType gestureType() const { return type_; } + + virtual Result recognize(const QList &inputEvents) = 0; + virtual QGesture* makeEvent() const = 0; + virtual void reset() = 0; + +signals: + void triggered(QGestureRecognizer::Result result); + +protected: + Qt::GestureType type_; +}; + +QT_END_NAMESPACE + +#endif // QGESTURERECOGNIZER_P_H diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp new file mode 100644 index 0000000..3a2fa1d --- /dev/null +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesturestandardrecognizers_p.h" +#include "qgesture_p.h" + +// #define GESTURE_RECOGNIZER_DEBUG +#ifndef GESTURE_RECOGNIZER_DEBUG +# define DEBUG if (0) qDebug +#else +# define DEBUG qDebug +#endif + +QT_BEGIN_NAMESPACE +/* +QGestureRecognizerMouseTwoButtons::QGestureRecognizerMouseTwoButtons() + : QGestureRecognizer(Qt::MouseTwoButtonClick) +{ + clear(); +} + +QGestureRecognizer::Result QGestureRecognizerMouseTwoButtons::recognize(const QList &inputEvents) +{ + // get all mouse events + QList events; + for(int i = 0; i < inputEvents.count(); ++i) { + QEvent *event = inputEvents.at(i); + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + events.push_back(static_cast(event)); + default: + break; + } + } + + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + for(int i = 0; i < events.count(); ++i) { + QMouseEvent *event = events.at(i); + if (event->type() == QEvent::MouseButtonPress) { + if (userEvents[3]) { + // something wrong, we already has a gesture. + clear(); + } else if (userEvents[2]) { + // 1d, 2d, 1u, and user press another button. not gesture. + DEBUG() << "1"; + result = QGestureRecognizer::NotGesture; + clear(); + break; + } else if (userEvents[1]) { + // 1d, 2d, and user pressed third button. not gesture. + DEBUG() << "2"; + result = QGestureRecognizer::NotGesture; + clear(); + break; + } else if (userEvents[0]) { + // second button press. + DEBUG() << "3"; + userEvents[1] = event; + result = QGestureRecognizer::MaybeGesture; + } else { + // first button press. + DEBUG() << "4"; + userEvents[0] = event; + result = QGestureRecognizer::MaybeGesture; + } + } else if (event->type() == QEvent::MouseButtonRelease) { + if (userEvents[3]) { + // something wrong, we already has a gesture. + clear(); + } else if (userEvents[2]) { + // 1d, 2d, 1u, and button release + DEBUG() << "5"; + if (userEvents[1]->button() != event->button()) { + // got weird buttonreleased event. doesn't look like gesture. + result = QGestureRecognizer::NotGesture; + clear(); + break; + } + // gesture! + userEvents[3] = event; + result = QGestureRecognizer::GestureFinished; + break; + } else if (userEvents[1]) { + // 1d, 2d, and button release + DEBUG() << "6"; + if (userEvents[0]->button() != event->button()) { + // user released the wrong button. not gesture. + result = QGestureRecognizer::NotGesture; + clear(); + break; + } + // user released the right button! looks like gesture + userEvents[2] = event; + result = QGestureRecognizer::MaybeGesture; + } else if (userEvents[0]) { + // 1d, and some button was released. not a gesture. + DEBUG() << "7"; + result = QGestureRecognizer::NotGesture; + clear(); + break; + } + } + } + return result; +} + +QGesture* QGestureRecognizerMouseTwoButtons::makeEvent() const +{ + if (!userEvents[0] || !userEvents[1] || !userEvents[2] || !userEvents[3]) + return 0; + QGesture::Direction direction = + (userEvents[0]->button() < userEvents[1]->button() ? + QGesture::Left : QGesture::Right); + QList points; + points.push_back(userEvents[0]->globalPos()); + points.push_back(userEvents[1]->globalPos()); + QGesture *gesture = + new QGesture(Qt::MouseTwoButtonClick, points.at(0), points.at(1), points.at(1), + direction, + QRect(points.at(0), points.at(1)), + points.at(0)); + return gesture; +} + +void QGestureRecognizerMouseTwoButtons::reset() +{ + clear(); +} + +void QGestureRecognizerMouseTwoButtons::clear() +{ + userEvents[0] = userEvents[1] = userEvents[2] = userEvents[3] = 0; +} +*/ + +// +// QGestureRecognizerPan +// + +QGestureRecognizerPan::QGestureRecognizerPan() + : QGestureRecognizer(Qt::Pan), mousePressed(false), gestureFinished(false), + lastDirection(Direction::None) +{ +} + +QGestureRecognizer::Result QGestureRecognizerPan::recognize(const QList &inputEvents) +{ + // get all mouse events + QList events; + for(int i = 0; i < inputEvents.count(); ++i) { + QEvent *event = inputEvents.at(i); + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + events.push_back(static_cast(event)); + default: + break; + } + } + + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + for(int i = 0; i < events.count(); ++i) { + QMouseEvent *event = events.at(i); + if (event->type() == QEvent::MouseButtonPress) { + if (lastDirection != Direction::None) { + DEBUG() << "Pan: MouseButtonPress: fail. another press during pan"; + result = QGestureRecognizer::NotGesture; + reset(); + break; + } + DEBUG() << "Pan: MouseButtonPress: maybe gesture started"; + result = QGestureRecognizer::MaybeGesture; + mousePressed = true; + pressedPos = lastPos = currentPos = event->pos(); + } else if (event->type() == QEvent::MouseButtonRelease) { + if (mousePressed && lastDirection != Direction::None) { + DEBUG() << "Pan: MouseButtonRelease: pan detected"; + result = QGestureRecognizer::GestureFinished; + gestureFinished = true; + currentPos = event->pos(); + internalReset(); + break; + } + DEBUG() << "Pan: MouseButtonRelease: some weird release detected, ignoring"; + result = QGestureRecognizer::NotGesture; + reset(); + break; + } else if (event->type() == QEvent::MouseMove) { + if (!mousePressed) + continue; + lastPos = currentPos; + currentPos = event->pos(); + Direction::DirectionType direction = + simpleRecognizer.addPosition(event->pos()).direction; + DEBUG() << "Pan: MouseMove: simplerecognizer result = " << direction; + if (lastDirection == Direction::None) { + if (direction == Direction::None) + result = QGestureRecognizer::MaybeGesture; + else + result = QGestureRecognizer::GestureStarted; + } else { + result = QGestureRecognizer::GestureStarted; + } + if (direction != Direction::None) + lastDirection = direction; + } + } + return result; +} + +QGesture* QGestureRecognizerPan::makeEvent() const +{ + QPannableGesture::DirectionType dir = QPannableGesture::None; + if (lastDirection == Direction::Left) + dir = QPannableGesture::Left; + else if (lastDirection == Direction::Right) + dir = QPannableGesture::Right; + else if (lastDirection == Direction::Up) + dir = QPannableGesture::Up; + else if (lastDirection == Direction::Down) + dir = QPannableGesture::Down; + else + return 0; + QPannableGesture *g = + new QPannableGesture(Qt::Pan, pressedPos, lastPos, currentPos, + QRect(), pressedPos, QDateTime(), 0, + gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); + QPannableGesturePrivate *d = (QPannableGesturePrivate*)g->d; + d->lastDirection = dir; ///### + d->direction = dir; + + return g; +} + +void QGestureRecognizerPan::reset() +{ + mousePressed = false; + lastDirection = Direction::None; + gestureFinished = false; + diagonalRecognizer.reset(); + simpleRecognizer.reset(); +} + +void QGestureRecognizerPan::internalReset() +{ + mousePressed = false; + diagonalRecognizer.reset(); + simpleRecognizer.reset(); +} + + +// +// QDoubleTapGestureRecognizer +// +QDoubleTapGestureRecognizer::QDoubleTapGestureRecognizer() + : QGestureRecognizer(Qt::DoubleTap) +{ +} + +QGestureRecognizer::Result QDoubleTapGestureRecognizer::recognize(const QList &inputEvents) +{ + // get all mouse events + QList events; + for(int i = 0; i < inputEvents.count(); ++i) { + QEvent *event = inputEvents.at(i); + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + events.push_back(static_cast(event)); + default: + break; + } + } + + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + for(int i = 0; i < events.count(); ++i) { + QMouseEvent *event = events.at(i); + if (event->type() == QEvent::MouseButtonPress) { + if (pressedPosition.isNull()) { + result = QGestureRecognizer::MaybeGesture; + pressedPosition = event->pos(); + } else if ((pressedPosition - event->pos()).manhattanLength() < 10) { + result = QGestureRecognizer::GestureFinished; + } + } else if (event->type() == QEvent::MouseButtonRelease) { + if (!pressedPosition.isNull() && (pressedPosition - event->pos()).manhattanLength() < 10) + result = QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseButtonDblClick) { + result = QGestureRecognizer::GestureFinished; + pressedPosition = event->pos(); + break; + } + } + return result; +} + +QGesture* QDoubleTapGestureRecognizer::makeEvent() const +{ + return new QGesture(Qt::DoubleTap, + pressedPosition, pressedPosition, pressedPosition, + QRect(), pressedPosition, QDateTime(), 0, Qt::GestureFinished); +} + +void QDoubleTapGestureRecognizer::reset() +{ + pressedPosition = QPoint(); +} + +// +// QLongTapGestureRecognizer +// +const int QLongTapGestureRecognizer::iterationCount = 40; +const int QLongTapGestureRecognizer::iterationTimeout = 50; + +QLongTapGestureRecognizer::QLongTapGestureRecognizer() + : QGestureRecognizer(Qt::LongTap), iteration(0) +{ +} + +QGestureRecognizer::Result QLongTapGestureRecognizer::recognize(const QList &inputEvents) +{ + // get all mouse events + QList events; + for(int i = 0; i < inputEvents.count(); ++i) { + QEvent *event = inputEvents.at(i); + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + events.push_back(static_cast(event)); + default: + break; + } + } + + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + for(int i = 0; i < events.count(); ++i) { + QMouseEvent *event = events.at(i); + if (event->type() == QEvent::MouseButtonPress) { + if (timer.isActive()) + timer.stop(); + timer.start(QLongTapGestureRecognizer::iterationTimeout, this); + pressedPosition = event->pos(); + result = QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseMove) { + if ((pressedPosition - event->pos()).manhattanLength() < 15) + result = QGestureRecognizer::GestureStarted; + else + result = QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseButtonRelease) { + result = QGestureRecognizer::NotGesture; + timer.stop(); + break; + } + } + return result; +} + +void QLongTapGestureRecognizer::timerEvent(QTimerEvent *event) +{ + if (event->timerId() != timer.timerId()) + return; + if (++iteration == QLongTapGestureRecognizer::iterationCount) { + emit triggered(QGestureRecognizer::GestureFinished); + timer.stop(); + } else { + emit triggered(QGestureRecognizer::GestureStarted); + } +} + +QGesture* QLongTapGestureRecognizer::makeEvent() const +{ + return new QGesture(Qt::LongTap, + pressedPosition, pressedPosition, pressedPosition, + QRect(), pressedPosition, QDateTime(), 0, + iteration >= QLongTapGestureRecognizer::iterationCount ? + Qt::GestureFinished : Qt::GestureStarted); +} + +void QLongTapGestureRecognizer::reset() +{ + pressedPosition = QPoint(); + timer.stop(); + iteration = 0; +} + +// +// QMultiTouchGestureRecognizer +// + +/* +QMultiTouchGestureRecognizer::QMultiTouchGestureRecognizer() + : QGestureRecognizer(Qt::Pinch) +{ +} + +QGestureRecognizer::Result QMultiTouchGestureRecognizer::recognize(const QList &inputEvents) +{ + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + for(int i = 0; i < inputEvents.count(); ++i) { + QEvent *inputEvent = inputEvents.at(i); + if (inputEvent->type() != QEvent::Pointer) { + if (touches.size() == 2) + result = QGestureRecognizer::GestureStarted; + else if (touches.size() == 1) + result = QGestureRecognizer::MaybeGesture; + continue; + } + QPointerEvent *event = static_cast(inputEvent); + Q_ASSERT(event->pointerCount() > 0); + for (int idx = 0; idx < event->pointerCount(); ++idx) { + switch (event->state(idx)) { + case QPointerEvent::Begin: + qDebug() << "Begin" << idx; + if (touches.size() == 2) { + // too many touches + qDebug() << "too many touches"; + result = QGestureRecognizer::MaybeGesture; + continue; + } + touches[event->pointerId(idx)].startPosition = event->pos(idx); + if (touches.size() == 1) { + result = QGestureRecognizer::MaybeGesture; + } else if (touches.size() == 2) { + result = QGestureRecognizer::GestureStarted; + } else { + result = QGestureRecognizer::NotGesture; + } + break; + + case QPointerEvent::Move: + qDebug() << "Move" << idx; + touches[event->pointerId(idx)].lastPosition = touches[event->pointerId(idx)].currentPosition; + touches[event->pointerId(idx)].currentPosition = event->pos(idx); + if (touches.size() == 1) + result = QGestureRecognizer::MaybeGesture; + else if (touches.size() == 2) + result = QGestureRecognizer::GestureStarted; + else + result = QGestureRecognizer::NotGesture; + break; + + case QPointerEvent::End: + qDebug() << "End" << idx; + touches.remove(event->pointerId(idx)); + if (touches.size() == 1) { + result = QGestureRecognizer::MaybeGesture; + } else if (touches.size() == 0) { + result = QGestureRecognizer::NotGesture; + } else { + qDebug() << "unexpected touch end event"; + return QGestureRecognizer::NotGesture; + } + break; + + case QPointerEvent::Stationary: + qDebug() << "Stationary" << idx; + if (touches.size() == 2) + result = QGestureRecognizer::GestureStarted; + else if (touches.size() == 1) + result = QGestureRecognizer::MaybeGesture; + break; + + default: + break; + } + } + } + return result; +} + +QGesture* QMultiTouchGestureRecognizer::makeEvent() const +{ + if (touches.size() != 2) + return 0; + + QGesture *g = new QGesture(Qt::Pinch); + QGesture::Data data; + + TapMap::const_iterator it = touches.begin(); + data.startPos = it->startPosition.toPoint(); + data.lastPos = it->lastPosition.toPoint(); + data.currentPos = it->currentPosition.toPoint(); + g->datas.push_back(data); + + ++it; + data.startPos = it->startPosition.toPoint(); + data.lastPos = it->lastPosition.toPoint(); + data.currentPos = it->currentPosition.toPoint(); + g->datas.push_back(data); + + return g; +} + +void QMultiTouchGestureRecognizer::reset() +{ + touches.clear(); +} +*/ + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h new file mode 100644 index 0000000..3b314e7 --- /dev/null +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURESTANDARDRECOGNIZERS_P_H +#define QGESTURESTANDARDRECOGNIZERS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qevent.h" +#include "qbasictimer.h" +#include "qdebug.h" + +#include "qgesturerecognizer.h" +#include "private/qdirectionrecognizer_p.h" + +QT_BEGIN_NAMESPACE + +/* +class QGestureRecognizerMouseTwoButtons : public QGestureRecognizer +{ + Q_OBJECT +public: + QGestureRecognizerMouseTwoButtons(); + QGestureRecognizer::Result recognize(const QList &inputEvents); + + QGesture* makeEvent() const; + void reset(); + +private: + void clear(); + + // find the last two button click events + QMouseEvent* userEvents[4]; +}; +*/ + +class QGestureRecognizerPan : public QGestureRecognizer +{ + Q_OBJECT +public: + QGestureRecognizerPan(); + + QGestureRecognizer::Result recognize(const QList &inputEvents); + QGesture* makeEvent() const; + + void reset(); + +private: + void internalReset(); + + QPoint pressedPos; + QPoint lastPos; + QPoint currentPos; + bool mousePressed; + bool gestureFinished; + Direction::DirectionType lastDirection; + QDirectionDiagonalRecognizer diagonalRecognizer; + QDirectionSimpleRecognizer simpleRecognizer; +}; + +class QDoubleTapGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + QDoubleTapGestureRecognizer(); + + QGestureRecognizer::Result recognize(const QList &inputEvents); + QGesture* makeEvent() const; + void reset(); + +private: + QPoint pressedPosition; +}; + +class QLongTapGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + QLongTapGestureRecognizer(); + + QGestureRecognizer::Result recognize(const QList &inputEvents); + QGesture* makeEvent() const; + void reset(); + +protected: + void timerEvent(QTimerEvent *event); + +private: + QPoint pressedPosition; + QBasicTimer timer; + int iteration; + static const int iterationCount; + static const int iterationTimeout; +}; + +/* +class QMultiTouchGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + QMultiTouchGestureRecognizer(); + + QMap maybeGestureCompletion(); + QGestureRecognizer::Result recognize(const QList &inputEvents); + QGesture* makeEvent() const; + void reset(); + +private: + struct Tap { + //### should I use QPointF everywhere internally ?? + QPointF startPosition; + QPointF lastPosition; + QPointF currentPosition; + }; + typedef QMap TapMap; + TapMap touches; +}; +*/ + +QT_END_NAMESPACE + +#endif // QGESTURESTANDARDRECOGNIZERS_P_H diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 968a423..f6be730 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -7532,6 +7532,7 @@ bool QWidget::event(QEvent *event) #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: #endif + case QEvent::Gesture: return false; default: break; @@ -7927,6 +7928,9 @@ bool QWidget::event(QEvent *event) d->needWindowChange = false; break; #endif + case QEvent::Gesture: + gestureEvent((QGestureEvent*)event); + break; #ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { const QByteArray &propName = static_cast(event)->propertyName(); @@ -8732,6 +8736,11 @@ bool QWidget::qwsEvent(QWSEvent *) #endif +void QWidget::gestureEvent(QGestureEvent *event) +{ + event->ignore(); +} + /*! Ensures that the widget has been polished by QStyle (i.e., has a @@ -11016,6 +11025,42 @@ QWindowSurface *QWidget::windowSurface() const return bs ? bs->windowSurface : 0; } +void QWidget::grabGesture(Qt::GestureType gesture) +{ + Q_D(QWidget); + if (d->gestures.contains(gesture)) + return; + d->gestures << gesture; + ++qApp->d_func()->grabbedGestures[gesture]; +} + +void QWidget::grabGestures(const QSet &gestures) +{ + Q_D(QWidget); + foreach(const Qt::GestureType &gesture, gestures) { + if (!d->gestures.contains(gesture)) + ++qApp->d_func()->grabbedGestures[gesture]; + } + d->gestures.unite(gestures); +} + +void QWidget::releaseGesture(Qt::GestureType gesture) +{ + Q_D(QWidget); + QMap::iterator it = + qApp->d_func()->grabbedGestures.find(gesture); + if (it != qApp->d_func()->grabbedGestures.end() && + d->gestures.contains(gesture)) + ++it.value(); + d->gestures.remove(gesture); +} + +QSet QWidget::gestures() +{ + Q_D(const QWidget); + return d->gestures; +} + void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const { if (left) diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index 6703d26..afe637d 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -59,6 +59,8 @@ #include #endif +#include + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -90,6 +92,7 @@ class QDragLeaveEvent; class QDropEvent; class QShowEvent; class QHideEvent; +class QGestureEvent; class QInputContext; class QIcon; class QWindowSurface; @@ -610,6 +613,11 @@ public: void setWindowSurface(QWindowSurface *surface); QWindowSurface *windowSurface() const; + void grabGesture(Qt::GestureType gesture); + void grabGestures(const QSet &gestures); + void releaseGesture(Qt::GestureType gesture); + QSet gestures(); + Q_SIGNALS: void customContextMenuRequested(const QPoint &pos); @@ -669,6 +677,8 @@ protected: // Misc. protected functions virtual void changeEvent(QEvent *); + virtual void gestureEvent(QGestureEvent *); + int metric(PaintDeviceMetric) const; virtual void inputMethodEvent(QInputMethodEvent *); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 5f5e1b1..178f7ba 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -57,6 +57,7 @@ #include "private/qobject_p.h" #include "QtCore/qrect.h" #include "QtCore/qlocale.h" +#include "QtCore/qset.h" #include "QtGui/qregion.h" #include "QtGui/qsizepolicy.h" #include "QtGui/qstyle.h" @@ -592,6 +593,8 @@ public: uint isGLWidget : 1; #endif + QSet gestures; + #if defined(Q_WS_X11) || defined (Q_WS_WIN) || defined(Q_WS_MAC) #ifdef Q_WS_MAC void setWSGeometry(bool dontShow=false, const QRect &oldRect = QRect()); diff --git a/src/gui/widgets/qabstractscrollarea.cpp b/src/gui/widgets/qabstractscrollarea.cpp index 9886969..190dbb5 100644 --- a/src/gui/widgets/qabstractscrollarea.cpp +++ b/src/gui/widgets/qabstractscrollarea.cpp @@ -908,6 +908,7 @@ bool QAbstractScrollArea::event(QEvent *e) case QEvent::DragMove: case QEvent::DragLeave: #endif + case QEvent::Gesture: return false; case QEvent::StyleChange: case QEvent::LayoutDirectionChange: -- cgit v0.12 From aa4334b77702d99d4aa3da853dd66104eb4a8123 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 26 Feb 2009 17:18:33 +0100 Subject: Fixed lastDirection in the Pan gesture. --- examples/gestures/graphicsview/main.cpp | 2 +- src/gui/kernel/qgesturestandardrecognizers.cpp | 48 +++++++++++++++++--------- src/gui/kernel/qgesturestandardrecognizers_p.h | 1 + 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/examples/gestures/graphicsview/main.cpp b/examples/gestures/graphicsview/main.cpp index a0236ef..250da06 100644 --- a/examples/gestures/graphicsview/main.cpp +++ b/examples/gestures/graphicsview/main.cpp @@ -53,7 +53,7 @@ protected: { if (event->type() == QEvent::Gesture) { QGestureEvent *ge = static_cast(event); - if (const QGesture *g = ge->gesture(Qt::Pan)) { + if (const QPannableGesture *g = dynamic_cast(ge->gesture(Qt::Pan))) { QPoint pt = g->pos() - g->lastPos(); horizontalScrollBar()->setValue(horizontalScrollBar()->value() - pt.x()); verticalScrollBar()->setValue(verticalScrollBar()->value() - pt.y()); diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index 3a2fa1d..504786a 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -178,7 +178,7 @@ void QGestureRecognizerMouseTwoButtons::clear() QGestureRecognizerPan::QGestureRecognizerPan() : QGestureRecognizer(Qt::Pan), mousePressed(false), gestureFinished(false), - lastDirection(Direction::None) + lastDirection(Direction::None), currentDirection(Direction::None) { } @@ -202,7 +202,7 @@ QGestureRecognizer::Result QGestureRecognizerPan::recognize(const QList for(int i = 0; i < events.count(); ++i) { QMouseEvent *event = events.at(i); if (event->type() == QEvent::MouseButtonPress) { - if (lastDirection != Direction::None) { + if (currentDirection != Direction::None) { DEBUG() << "Pan: MouseButtonPress: fail. another press during pan"; result = QGestureRecognizer::NotGesture; reset(); @@ -213,7 +213,7 @@ QGestureRecognizer::Result QGestureRecognizerPan::recognize(const QList mousePressed = true; pressedPos = lastPos = currentPos = event->pos(); } else if (event->type() == QEvent::MouseButtonRelease) { - if (mousePressed && lastDirection != Direction::None) { + if (mousePressed && currentDirection != Direction::None) { DEBUG() << "Pan: MouseButtonRelease: pan detected"; result = QGestureRecognizer::GestureFinished; gestureFinished = true; @@ -233,7 +233,7 @@ QGestureRecognizer::Result QGestureRecognizerPan::recognize(const QList Direction::DirectionType direction = simpleRecognizer.addPosition(event->pos()).direction; DEBUG() << "Pan: MouseMove: simplerecognizer result = " << direction; - if (lastDirection == Direction::None) { + if (currentDirection == Direction::None) { if (direction == Direction::None) result = QGestureRecognizer::MaybeGesture; else @@ -241,32 +241,45 @@ QGestureRecognizer::Result QGestureRecognizerPan::recognize(const QList } else { result = QGestureRecognizer::GestureStarted; } - if (direction != Direction::None) - lastDirection = direction; + if (direction != Direction::None) { + if (currentDirection != direction) + lastDirection = currentDirection; + currentDirection = direction; + } } } return result; } +static inline QPannableGesture::DirectionType convertPanningDirection(const Direction::DirectionType direction) +{ + switch (direction) { + case Direction::Left: + return QPannableGesture::Left; + case Direction::Right: + return QPannableGesture::Right; + case Direction::Up: + return QPannableGesture::Up; + case Direction::Down: + return QPannableGesture::Down; + default: + break; + } + return QPannableGesture::None; +} + QGesture* QGestureRecognizerPan::makeEvent() const { - QPannableGesture::DirectionType dir = QPannableGesture::None; - if (lastDirection == Direction::Left) - dir = QPannableGesture::Left; - else if (lastDirection == Direction::Right) - dir = QPannableGesture::Right; - else if (lastDirection == Direction::Up) - dir = QPannableGesture::Up; - else if (lastDirection == Direction::Down) - dir = QPannableGesture::Down; - else + QPannableGesture::DirectionType dir = convertPanningDirection(currentDirection); + QPannableGesture::DirectionType lastDir = convertPanningDirection(lastDirection); + if (dir == QPannableGesture::None) return 0; QPannableGesture *g = new QPannableGesture(Qt::Pan, pressedPos, lastPos, currentPos, QRect(), pressedPos, QDateTime(), 0, gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); QPannableGesturePrivate *d = (QPannableGesturePrivate*)g->d; - d->lastDirection = dir; ///### + d->lastDirection = lastDir; d->direction = dir; return g; @@ -276,6 +289,7 @@ void QGestureRecognizerPan::reset() { mousePressed = false; lastDirection = Direction::None; + currentDirection = Direction::None; gestureFinished = false; diagonalRecognizer.reset(); simpleRecognizer.reset(); diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h index 3b314e7..567cf65 100644 --- a/src/gui/kernel/qgesturestandardrecognizers_p.h +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -101,6 +101,7 @@ private: bool mousePressed; bool gestureFinished; Direction::DirectionType lastDirection; + Direction::DirectionType currentDirection; QDirectionDiagonalRecognizer diagonalRecognizer; QDirectionSimpleRecognizer simpleRecognizer; }; -- cgit v0.12 From ce50bdd854497b2a2aedbdb4417b532799086241 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 27 Feb 2009 14:33:49 +0100 Subject: Added some documentation. Fixed missing const specifiers. --- doc/src/qnamespace.qdoc | 16 +++++++++++ src/gui/kernel/qevent.cpp | 44 ++++++++++++++++++++++++++++++ src/gui/kernel/qevent.h | 8 +++--- src/gui/kernel/qgesturerecognizer.h | 54 ++++++++++++++++++++++++++++++++++++- src/gui/kernel/qwidget.cpp | 27 ++++++++++++++++--- src/gui/kernel/qwidget.h | 6 ++--- 6 files changed, 144 insertions(+), 11 deletions(-) diff --git a/doc/src/qnamespace.qdoc b/doc/src/qnamespace.qdoc index 98f7e7c..c44b41e 100644 --- a/doc/src/qnamespace.qdoc +++ b/doc/src/qnamespace.qdoc @@ -2675,3 +2675,19 @@ \sa QPixmapBorders, qDrawBorderPixmap() */ + +/*! \typedef Qt::GestureType + + A string representing a type of a gesture. +*/ + +/*! + \enum Qt::GestureState + + This enum type describes the state of a gesture. + + \value GestureStarted The continuous gesture has started. + \value GestureFinished The gesture has finished. + + \sa QGesture +*/ diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index a7e1101..d7d193c 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3509,6 +3509,50 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) #endif +/*! + \class QGestureEvent + \ingroup events + + \brief The QGestureEvent class provides the parameters used for + gesture recognition. + + The QGestureEvent class contains a list of gestures that are being + executed right now (QGestureEvent::gestureTypes()) and a list of + gestures that are cancelled (the gesture might be cancelled + because the window lost focus, or because of timeout, etc). + + \sa QGesture +*/ + +/*! \fn QWidget *QGestureEvent::targetWidget() const + + Returns the widget the gesture event is send to. +*/ + +/*! \fn bool contains(const Qt::GestureType &type) const + + Checks if the gesture event contains gesture of specific \a type. +*/ + +/*! \fn QList gestureTypes() const + + Returns a list of gesture names that the event contains. +*/ + +/*! \fn const QGesture* gesture(const Qt::GestureType &type) const + + Returns extended information about a gesture of specific \a type. +*/ + +/*! \fn QSet cancelledGestures() const + + Returns a set of gesture names that used to be executed, but got + cancelled (i.e. they were not finished properly). +*/ + + + + QGestureEvent::QGestureEvent(QWidget *targetWidget, const QList &gestures, const QSet &cancelledGestures) : QEvent(QEvent::Gesture), m_targetWidget(targetWidget), diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 858e2ea..cde7cb2 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -726,12 +726,12 @@ public: QWidget *targetWidget() const { return m_targetWidget; } - inline bool contains(const Qt::GestureType &gestureType) const - { return gesture(gestureType) != 0; } + inline bool contains(const Qt::GestureType &type) const + { return gesture(type) != 0; } inline QList gestureTypes() const { return m_gestures.keys(); } - inline const QGesture* gesture(const Qt::GestureType &gestureType) const - { return m_gestures.value(gestureType, QSharedPointer()).data(); } + inline const QGesture* gesture(const Qt::GestureType &type) const + { return m_gestures.value(type, QSharedPointer()).data(); } inline QSet cancelledGestures() const { return m_cancelledGestures; } diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h index 3ed96c0..a831d8a 100644 --- a/src/gui/kernel/qgesturerecognizer.h +++ b/src/gui/kernel/qgesturerecognizer.h @@ -49,6 +49,58 @@ QT_BEGIN_NAMESPACE +/*! + \class QGestureRecognizer + + \brief The base class for implementing custom gestures. + + This is a base class, to create a custom gesture type, you should + subclass it and implement pure virtual functions. + + Usually gesture recognizer implements state machine, storing its + state internally in the recognizer object. The recognizer receives + input events through the QGestureRecognizer::recognize() virtual + function and decides whether the parsed event should change the + state of the recognizer - i.e. if the event starts or ends a + gesture or if it isn't related to gesture at all. +*/ + +/*! \fn Qt::GestureType gestureType() const + + Returns the name of the gesture that is handled by the recognizer. +*/ + +/*! \fn Result recognize(const QList &events) + + This is a pure virtual function that need to be implemented in + subclasses. + + Parses input \a events and returns the result, saying if the event + sequence is a gesture or not. +*/ + +/*! \fn QGesture* makeEvent() const + + Creates a new gesture object that will be send to the widget. This + function is called when the gesture recognizer returned a + QGestureRecognizer::GestureStarted or + QGestureRecognizer::GestureFinished state. + + Created gesture object is owned by the caller. + */ + +/*! \fn void reset() + + Resets the internal state of the gesture recognizer. +*/ + +/*! \fn void triggered(QGestureRecognizer::Result result) + + The gesture recognizer might emit the signal when the gesture + state changes asynchronously, i.e. without any event being + received. +*/ + class QGestureRecognizer : public QObject { Q_OBJECT @@ -67,7 +119,7 @@ public: inline QGestureRecognizer(const Qt::GestureType &type) : type_(type) { } inline Qt::GestureType gestureType() const { return type_; } - virtual Result recognize(const QList &inputEvents) = 0; + virtual Result recognize(const QList &events) = 0; virtual QGesture* makeEvent() const = 0; virtual void reset() = 0; diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index f6be730..aea0003 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -11025,7 +11025,13 @@ QWindowSurface *QWidget::windowSurface() const return bs ? bs->windowSurface : 0; } -void QWidget::grabGesture(Qt::GestureType gesture) +/*! + \fn void QWidget::grabGesture(const Qt::GestureType &gesture) + + Subscribes the widget to the specified \a gesture type. +*/ + +void QWidget::grabGesture(const Qt::GestureType &gesture) { Q_D(QWidget); if (d->gestures.contains(gesture)) @@ -11034,6 +11040,11 @@ void QWidget::grabGesture(Qt::GestureType gesture) ++qApp->d_func()->grabbedGestures[gesture]; } +/*! + \fn void QWidget::grabGestures(const QSet &gestures) + + Subscribes the widget to the specified list of \a gestures. +*/ void QWidget::grabGestures(const QSet &gestures) { Q_D(QWidget); @@ -11044,7 +11055,12 @@ void QWidget::grabGestures(const QSet &gestures) d->gestures.unite(gestures); } -void QWidget::releaseGesture(Qt::GestureType gesture) +/*! + \fn void QWidget::releaseGesture(const Qt::GestureType &gesture) + + Unsubscribes the widget from the specified \a gesture type. +*/ +void QWidget::releaseGesture(const Qt::GestureType &gesture) { Q_D(QWidget); QMap::iterator it = @@ -11055,7 +11071,12 @@ void QWidget::releaseGesture(Qt::GestureType gesture) d->gestures.remove(gesture); } -QSet QWidget::gestures() +/*! + \fn QSet QWidget::gestures() const + + Returns a list of gestures that the widget is subscribed to. +*/ +QSet QWidget::gestures() const { Q_D(const QWidget); return d->gestures; diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index afe637d..e36b966 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -613,10 +613,10 @@ public: void setWindowSurface(QWindowSurface *surface); QWindowSurface *windowSurface() const; - void grabGesture(Qt::GestureType gesture); + void grabGesture(const Qt::GestureType &gesture); void grabGestures(const QSet &gestures); - void releaseGesture(Qt::GestureType gesture); - QSet gestures(); + void releaseGesture(const Qt::GestureType &gesture); + QSet gestures() const; Q_SIGNALS: void customContextMenuRequested(const QPoint &pos); -- cgit v0.12 From 4e31add7d62f26036a43dfe77db1c6b7e333e21e Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 2 Mar 2009 16:32:39 +0100 Subject: Fixes: reset the internal gesture state when gesture in state 'maybe' is finished. --- src/gui/kernel/qgesturemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index c244a0b..2d0137e 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -137,7 +137,7 @@ bool QGestureManager::filterEvent(QEvent *event) } activeGestures -= newMaybeGestures; activeGestures += startedGestures; - foreach(QGestureRecognizer *r, startedGestures+notGestures) { + foreach(QGestureRecognizer *r, startedGestures+finishedGestures+notGestures) { QMap::iterator it = maybeGestures.find(r); if (it != maybeGestures.end()) { killTimer(it.value()); -- cgit v0.12 From b349831a5ae7bca80f22240b1ed204dd3d87c71f Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 4 Mar 2009 11:39:49 +0100 Subject: Fixes: QGraphicsView respects the spontaneous event flag for graphicsscene events. Details: Copying the QEvent::spontaneous() flag from the received event to QGraphicsSceneEvent. --- src/corelib/kernel/qcoreevent.h | 3 +++ src/gui/graphicsview/qgraphicsscene.cpp | 1 + src/gui/graphicsview/qgraphicsview.cpp | 10 ++++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index c9f9f24..4ea3c70 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -313,6 +313,9 @@ private: friend class Q3AccelManager; friend class QShortcutMap; friend class QETWidget; + friend class QGraphicsView; + friend class QGraphicsViewPrivate; + friend class QGraphicsScenePrivate; }; class Q_CORE_EXPORT QTimerEvent : public QEvent diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index fe6dde1..873e28c 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -1335,6 +1335,7 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // event is converted to a press. Known limitation: // Triple-clicking will not generate a doubleclick, though. QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress); + mousePress.spont = mouseEvent->spont; mousePress.accept(); mousePress.setButton(mouseEvent->button()); mousePress.setButtons(mouseEvent->buttons()); diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index acce717..97903d3 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -295,6 +295,8 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < QT_BEGIN_NAMESPACE +bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); + inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision { if (d <= (qreal) INT_MIN) @@ -602,7 +604,7 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) lastMouseMoveScenePoint = mouseEvent.scenePos(); lastMouseMoveScreenPoint = mouseEvent.screenPos(); mouseEvent.setAccepted(false); - QApplication::sendEvent(scene, &mouseEvent); + qt_sendSpontaneousEvent(scene, &mouseEvent); // Remember whether the last event was accepted or not. lastMouseEvent.setAccepted(mouseEvent.isAccepted()); @@ -3225,7 +3227,7 @@ void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) mouseEvent.setAccepted(false); mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); - QApplication::sendEvent(d->scene, &mouseEvent); + qt_sendSpontaneousEvent(d->scene, &mouseEvent); } /*! @@ -3264,7 +3266,7 @@ void QGraphicsView::mousePressEvent(QMouseEvent *event) mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); mouseEvent.setAccepted(false); - QApplication::sendEvent(d->scene, &mouseEvent); + qt_sendSpontaneousEvent(d->scene, &mouseEvent); // Update the original mouse event accepted state. bool isAccepted = mouseEvent.isAccepted(); @@ -3434,7 +3436,7 @@ void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); mouseEvent.setAccepted(false); - QApplication::sendEvent(d->scene, &mouseEvent); + qt_sendSpontaneousEvent(d->scene, &mouseEvent); // Update the last mouse event selected state. d->lastMouseEvent.setAccepted(mouseEvent.isAccepted()); -- cgit v0.12 From fc84e3a05d01cce25949c605fc56dba4b4d3d183 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 5 Mar 2009 18:41:10 +0100 Subject: Fixes: improved gesture manager event filtering. Details: Several fixes - parsing only spontaneous mouse events and send gesture events to QGraphicsSceneItems according to their z-order --- src/gui/graphicsview/qgraphicsitem.cpp | 5 ++++ src/gui/graphicsview/qgraphicsitem.h | 1 + src/gui/graphicsview/qgraphicsscene.cpp | 19 ++++++++++----- src/gui/kernel/qapplication.cpp | 43 ++++++++++++++++++--------------- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 58f04f0..a9ad1e1 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -5802,6 +5802,11 @@ void QGraphicsItem::releaseGesture(const Qt::GestureType &type) d_ptr->scene->d_func()->releaseGesture(this, type); } +QSet QGraphicsItem::gestures() const +{ + return d_ptr->gestures; +} + /*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 42a5110..2b4cdf5 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -340,6 +340,7 @@ public: void grabGesture(const Qt::GestureType &type); void releaseGesture(const Qt::GestureType &type); + QSet gestures() const; enum { Type = 1, diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 873e28c..21c7c81 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -5614,18 +5614,25 @@ void QGraphicsScene::gestureEvent(QGestureEvent *event) { Q_D(QGraphicsScene); QList gestureTypes = event->gestureTypes(); - QList pts; QGraphicsView *view = qobject_cast(event->targetWidget()); if (!view) { // something is wrong. Q_ASSERT(view); return; } - foreach(const Qt::GestureType &type, gestureTypes) - pts << view->mapToScene(event->gesture(type)->hotSpot()); - foreach(QGraphicsItem *item, d->itemsWithGestures) { - for (int i = 0; i < pts.size(); ++i) { - if (item->contains(item->mapFromScene(pts.at(i)))) { + QPolygonF poly; + QMap hotSpots; + foreach(const Qt::GestureType &type, gestureTypes) { + QPointF pt = view->mapToScene(event->gesture(type)->hotSpot()); + hotSpots.insert(type, pt); + poly << pt; + } + + foreach(QGraphicsItem *item, items(poly, Qt::IntersectsItemBoundingRect)) { + QMap::const_iterator it = hotSpots.begin(), + e = hotSpots.end(); + for(; it != e; ++it) { + if (item->contains(item->mapFromScene(it.value())) && item->gestures().contains(it.key())) { d->sendGestureEvent(item, event); if (event->isAccepted()) break; diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index b39cbc3..88871a4 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -3706,28 +3706,31 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QMouseEvent* mouse = static_cast(e); QPoint relpos = mouse->pos(); - if (QApplication::testAttribute(Qt::AA_EnableGestures)) { - if (!gestureManager) - gestureManager = new QGestureManager; - // if we are in gesture mode, we send all mouse events - // directly to gesture recognizer. - if (gestureManager->inGestureMode()) { - // ### should I send events through all application event filters? - if (gestureManager->filterEvent(e)) - return true; - } - if (w && (mouse->type() != QEvent::MouseMove || mouse->buttons() != 0)) { - // find the gesture target widget - QWidget *target = w; - while (target && target->gestures().isEmpty()) - target = target->parentWidget(); - if (target) { - gestureManager->setGestureTargetWidget(target); - res = gestureManager->filterEvent(e); + if (e->spontaneous()) { + if (QApplication::testAttribute(Qt::AA_EnableGestures)) { + QWidget *w = static_cast(receiver); + if (!gestureManager) + gestureManager = new QGestureManager; + // if we are in gesture mode, we send all mouse events + // directly to gesture recognizer. + if (gestureManager->inGestureMode()) { + if (gestureManager->filterEvent(e)) + return true; + } else { + QMouseEvent* mouse = static_cast(e); + if (w && (mouse->type() != QEvent::MouseMove || mouse->buttons() != 0)) { + // find the gesture target widget + QWidget *target = w; + while (target && target->gestures().isEmpty()) + target = target->parentWidget(); + if (target) { + gestureManager->setGestureTargetWidget(target); + if (gestureManager->filterEvent(e)) + return true; + } + } } } - } - if (e->spontaneous()) { if (e->type() == QEvent::MouseButtonPress) { QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, Qt::ClickFocus, -- cgit v0.12 From f1d955249ff00db79252967078005db9a9fe936c Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 6 Mar 2009 12:08:48 +0100 Subject: If the event wasn't consumed by the gesture target widget, then gesture manager should consume it as well. --- src/gui/kernel/qgesturemanager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 2d0137e..c9bf0df 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -169,7 +169,8 @@ bool QGestureManager::filterEvent(QEvent *event) } Q_ASSERT(!gestures.isEmpty()); QGestureEvent event(targetWidget, gestures); - qt_sendGestureEvent(targetWidget, &event); + ret = qt_sendGestureEvent(targetWidget, &event); + ret = ret && event.isAccepted(); if (!activeGestures.isEmpty()) { DEBUG() << "QGestureManager: new state = Gesture"; @@ -273,7 +274,8 @@ bool QGestureManager::filterEvent(QEvent *event) cancelledGestureNames << r->gestureType(); if(!gestures.isEmpty()) { QGestureEvent event(targetWidget, gestures, cancelledGestureNames); - qt_sendGestureEvent(targetWidget, &event); + ret = qt_sendGestureEvent(targetWidget, &event); + ret = ret && event.isAccepted(); } foreach(QGestureRecognizer *r, finishedGestures) @@ -291,7 +293,6 @@ bool QGestureManager::filterEvent(QEvent *event) DEBUG() << "QGestureManager: new state = NotGesture"; state = NotGesture; } - ret = true; } lastPos = currentPos; -- cgit v0.12 From b231b0bcd36d531d3558cee12be263a98391b9db Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 6 Mar 2009 13:11:29 +0100 Subject: Fixes: Compilation fixes --- examples/gestures/imageviewer/imagewidget.cpp | 2 ++ src/gui/kernel/qgesturemanager_p.h | 1 + 2 files changed, 3 insertions(+) diff --git a/examples/gestures/imageviewer/imagewidget.cpp b/examples/gestures/imageviewer/imagewidget.cpp index 8932e29..0db62bb 100644 --- a/examples/gestures/imageviewer/imagewidget.cpp +++ b/examples/gestures/imageviewer/imagewidget.cpp @@ -148,10 +148,12 @@ void ImageWidget::gestureEvent(QGestureEvent *event) } else if (const QGesture *g = event->gesture(Qt::Pan)) { if (zoomedIn) { // usual panning +#ifndef QT_NO_CURSOR if (g->state() == Qt::GestureStarted) setCursor(Qt::SizeAllCursor); else setCursor(Qt::ArrowCursor); +#endif const int dx = g->pos().x() - g->lastPos().x(); const int dy = g->pos().y() - g->lastPos().y(); horizontalOffset += dx; diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 9853f1a..1079cc4 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -53,6 +53,7 @@ // We mean it. // +#include "qwidget.h" #include "qlist.h" #include "qset.h" #include "qevent.h" -- cgit v0.12 From 3cad576b7ce221ca92c07a513c4c0fbe9886f8e5 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 6 Mar 2009 14:42:51 +0100 Subject: Fixes: Add missing functions for a adding custom gesture recognizers. --- src/gui/kernel/qapplication.cpp | 23 ++++++++++++++++------- src/gui/kernel/qapplication.h | 4 ++++ src/gui/kernel/qgesturemanager.cpp | 10 ++++++++++ src/gui/kernel/qgesturemanager_p.h | 3 +++ 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 88871a4..104271d 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -137,7 +137,7 @@ int QApplicationPrivate::autoMaximizeThreshold = -1; bool QApplicationPrivate::autoSipEnabled = false; #endif -QGestureManager *gestureManager = 0; +Q_GLOBAL_STATIC(QGestureManager, gestureManager); QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type) : QCoreApplicationPrivate(argc, argv) @@ -3709,12 +3709,11 @@ bool QApplication::notify(QObject *receiver, QEvent *e) if (e->spontaneous()) { if (QApplication::testAttribute(Qt::AA_EnableGestures)) { QWidget *w = static_cast(receiver); - if (!gestureManager) - gestureManager = new QGestureManager; + QGestureManager *gm = gestureManager(); // if we are in gesture mode, we send all mouse events // directly to gesture recognizer. - if (gestureManager->inGestureMode()) { - if (gestureManager->filterEvent(e)) + if (gm->inGestureMode()) { + if (gm->filterEvent(e)) return true; } else { QMouseEvent* mouse = static_cast(e); @@ -3724,8 +3723,8 @@ bool QApplication::notify(QObject *receiver, QEvent *e) while (target && target->gestures().isEmpty()) target = target->parentWidget(); if (target) { - gestureManager->setGestureTargetWidget(target); - if (gestureManager->filterEvent(e)) + gm->setGestureTargetWidget(target); + if (gm->filterEvent(e)) return true; } } @@ -5090,6 +5089,16 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) return true; } +void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) +{ + gestureManager()->addRecognizer(recognizer); +} + +void QApplication::removeGestureRecognizer(QGestureRecognizer *recognizer) +{ + gestureManager()->removeRecognizer(recognizer); +} + /*! \fn QDecoration &QApplication::qwsDecoration() Return the QWSDecoration used for decorating windows. diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index 634226f..70458ef 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -71,6 +71,7 @@ class QStyle; class QEventLoop; class QIcon; class QInputContext; +class QGestureRecognizer; template class QList; class QLocale; #if defined(Q_WS_QWS) @@ -266,6 +267,9 @@ public: static bool keypadNavigationEnabled(); #endif + void addGestureRecognizer(QGestureRecognizer *recognizer); + void removeGestureRecognizer(QGestureRecognizer *recognizer); + Q_SIGNALS: void lastWindowClosed(); void focusChanged(QWidget *old, QWidget *now); diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index c9bf0df..7954ae9 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -83,6 +83,16 @@ QGestureManager::QGestureManager() this, SLOT(recognizerTriggered(QGestureRecognizer::Result))); } +void QGestureManager::addRecognizer(QGestureRecognizer *recognizer) +{ + recognizers << recognizer; +} + +void QGestureManager::removeRecognizer(QGestureRecognizer *recognizer) +{ + recognizers.remove(recognizer); +} + bool QGestureManager::filterEvent(QEvent *event) { if (!QApplication::testAttribute(Qt::AA_EnableGestures)) diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 1079cc4..762b70d 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -72,6 +72,9 @@ public: // should be internal void setGestureTargetWidget(QWidget *widget); + void addRecognizer(QGestureRecognizer *recognizer); + void removeRecognizer(QGestureRecognizer *recognizer); + bool filterEvent(QEvent *event); bool inGestureMode(); -- cgit v0.12 From b542556f2d75017af34e0bc8ee1336fa0c08c69d Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 6 Mar 2009 16:54:23 +0100 Subject: Forgot to export QGestureRecognizer class. --- src/gui/kernel/qgesturerecognizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h index a831d8a..ca8972b 100644 --- a/src/gui/kernel/qgesturerecognizer.h +++ b/src/gui/kernel/qgesturerecognizer.h @@ -101,7 +101,7 @@ QT_BEGIN_NAMESPACE received. */ -class QGestureRecognizer : public QObject +class Q_GUI_EXPORT QGestureRecognizer : public QObject { Q_OBJECT public: -- cgit v0.12 From 15588496830fee6fbe38fda76411e483a51ddd08 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 6 Mar 2009 14:45:43 +0100 Subject: Added colliding mice example with gesture support. --- examples/gestures/collidingmice/collidingmice.pro | 14 ++ examples/gestures/collidingmice/main.cpp | 114 ++++++++++++ examples/gestures/collidingmice/mice.qrc | 5 + examples/gestures/collidingmice/mouse.cpp | 200 ++++++++++++++++++++++ examples/gestures/collidingmice/mouse.h | 72 ++++++++ 5 files changed, 405 insertions(+) create mode 100644 examples/gestures/collidingmice/collidingmice.pro create mode 100644 examples/gestures/collidingmice/main.cpp create mode 100644 examples/gestures/collidingmice/mice.qrc create mode 100644 examples/gestures/collidingmice/mouse.cpp create mode 100644 examples/gestures/collidingmice/mouse.h diff --git a/examples/gestures/collidingmice/collidingmice.pro b/examples/gestures/collidingmice/collidingmice.pro new file mode 100644 index 0000000..5c7b42b --- /dev/null +++ b/examples/gestures/collidingmice/collidingmice.pro @@ -0,0 +1,14 @@ +HEADERS += \ + mouse.h +SOURCES += \ + main.cpp \ + mouse.cpp + +RESOURCES += \ + mice.qrc + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/gestures/collidingmice +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS collidingmice.pro images +sources.path = $$[QT_INSTALL_EXAMPLES]/gestures/collidingmice +INSTALLS += target sources diff --git a/examples/gestures/collidingmice/main.cpp b/examples/gestures/collidingmice/main.cpp new file mode 100644 index 0000000..56e9f7f --- /dev/null +++ b/examples/gestures/collidingmice/main.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mouse.h" + +#include + +#include + +static const int MouseCount = 7; + +class PannableGraphicsView : public QGraphicsView +{ +public: + PannableGraphicsView(QGraphicsScene *scene, QWidget *parent = 0) + : QGraphicsView(scene, parent) + { + grabGesture(Qt::Pan); + } +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *ge = static_cast(event); + if (const QPannableGesture *g = dynamic_cast(ge->gesture(Qt::Pan))) { + QPoint pt = g->pos() - g->lastPos(); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() - pt.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() - pt.y()); + event->accept(); + return true; + } + } + return QGraphicsView::event(event); + } +}; + +//! [0] +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + QApplication::setAttribute(Qt::AA_EnableGestures); + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); +//! [0] + +//! [1] + QGraphicsScene scene; + scene.setSceneRect(-600, -600, 1200, 1200); +//! [1] //! [2] + scene.setItemIndexMethod(QGraphicsScene::NoIndex); +//! [2] + +//! [3] + for (int i = 0; i < MouseCount; ++i) { + Mouse *mouse = new Mouse; + mouse->setPos(::sin((i * 6.28) / MouseCount) * 200, + ::cos((i * 6.28) / MouseCount) * 200); + scene.addItem(mouse); + } +//! [3] + +//! [4] + PannableGraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing); + view.setBackgroundBrush(QPixmap(":/images/cheese.jpg")); +//! [4] //! [5] + view.setCacheMode(QGraphicsView::CacheBackground); + view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + //view.setDragMode(QGraphicsView::ScrollHandDrag); +//! [5] //! [6] + view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Colliding Mice")); + view.resize(400, 300); + view.show(); + + return app.exec(); +} +//! [6] diff --git a/examples/gestures/collidingmice/mice.qrc b/examples/gestures/collidingmice/mice.qrc new file mode 100644 index 0000000..accdb4d --- /dev/null +++ b/examples/gestures/collidingmice/mice.qrc @@ -0,0 +1,5 @@ + + + images/cheese.jpg + + diff --git a/examples/gestures/collidingmice/mouse.cpp b/examples/gestures/collidingmice/mouse.cpp new file mode 100644 index 0000000..1d9fa89 --- /dev/null +++ b/examples/gestures/collidingmice/mouse.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mouse.h" + +#include +#include +#include + +#include + +static const double Pi = 3.14159265358979323846264338327950288419717; +static double TwoPi = 2.0 * Pi; + +static qreal normalizeAngle(qreal angle) +{ + while (angle < 0) + angle += TwoPi; + while (angle > TwoPi) + angle -= TwoPi; + return angle; +} + +//! [0] +Mouse::Mouse() + : angle(0), speed(0), mouseEyeDirection(0), + color(qrand() % 256, qrand() % 256, qrand() % 256) +{ + rotate(qrand() % (360 * 16)); + startTimer(1000 / 33); +} +//! [0] + +//! [1] +QRectF Mouse::boundingRect() const +{ + qreal adjust = 0.5; + return QRectF(-18 - adjust, -22 - adjust, + 36 + adjust, 60 + adjust); +} +//! [1] + +//! [2] +QPainterPath Mouse::shape() const +{ + QPainterPath path; + path.addRect(-10, -20, 20, 40); + return path; +} +//! [2] + +//! [3] +void Mouse::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + // Body + painter->setBrush(color); + painter->drawEllipse(-10, -20, 20, 40); + + // Eyes + painter->setBrush(Qt::white); + painter->drawEllipse(-10, -17, 8, 8); + painter->drawEllipse(2, -17, 8, 8); + + // Nose + painter->setBrush(Qt::black); + painter->drawEllipse(QRectF(-2, -22, 4, 4)); + + // Pupils + painter->drawEllipse(QRectF(-8.0 + mouseEyeDirection, -17, 4, 4)); + painter->drawEllipse(QRectF(4.0 + mouseEyeDirection, -17, 4, 4)); + + // Ears + painter->setBrush(scene()->collidingItems(this).isEmpty() ? Qt::darkYellow : Qt::red); + painter->drawEllipse(-17, -12, 16, 16); + painter->drawEllipse(1, -12, 16, 16); + + // Tail + QPainterPath path(QPointF(0, 20)); + path.cubicTo(-5, 22, -5, 22, 0, 25); + path.cubicTo(5, 27, 5, 32, 0, 30); + path.cubicTo(-5, 32, -5, 42, 0, 35); + painter->setBrush(Qt::NoBrush); + painter->drawPath(path); +} +//! [3] + +//! [4] +void Mouse::timerEvent(QTimerEvent *) +{ +//! [4] + // Don't move too far away +//! [5] + QLineF lineToCenter(QPointF(0, 0), mapFromScene(0, 0)); + if (lineToCenter.length() > 150) { + qreal angleToCenter = ::acos(lineToCenter.dx() / lineToCenter.length()); + if (lineToCenter.dy() < 0) + angleToCenter = TwoPi - angleToCenter; + angleToCenter = normalizeAngle((Pi - angleToCenter) + Pi / 2); + + if (angleToCenter < Pi && angleToCenter > Pi / 4) { + // Rotate left + angle += (angle < -Pi / 2) ? 0.25 : -0.25; + } else if (angleToCenter >= Pi && angleToCenter < (Pi + Pi / 2 + Pi / 4)) { + // Rotate right + angle += (angle < Pi / 2) ? 0.25 : -0.25; + } + } else if (::sin(angle) < 0) { + angle += 0.25; + } else if (::sin(angle) > 0) { + angle -= 0.25; +//! [5] //! [6] + } +//! [6] + + // Try not to crash with any other mice +//! [7] + QList dangerMice = scene()->items(QPolygonF() + << mapToScene(0, 0) + << mapToScene(-30, -50) + << mapToScene(30, -50)); + foreach (QGraphicsItem *item, dangerMice) { + if (item == this) + continue; + + QLineF lineToMouse(QPointF(0, 0), mapFromItem(item, 0, 0)); + qreal angleToMouse = ::acos(lineToMouse.dx() / lineToMouse.length()); + if (lineToMouse.dy() < 0) + angleToMouse = TwoPi - angleToMouse; + angleToMouse = normalizeAngle((Pi - angleToMouse) + Pi / 2); + + if (angleToMouse >= 0 && angleToMouse < Pi / 2) { + // Rotate right + angle += 0.5; + } else if (angleToMouse <= TwoPi && angleToMouse > (TwoPi - Pi / 2)) { + // Rotate left + angle -= 0.5; +//! [7] //! [8] + } +//! [8] //! [9] + } +//! [9] + + // Add some random movement +//! [10] + if (dangerMice.size() > 1 && (qrand() % 10) == 0) { + if (qrand() % 1) + angle += (qrand() % 100) / 500.0; + else + angle -= (qrand() % 100) / 500.0; + } +//! [10] + +//! [11] + speed += (-50 + qrand() % 100) / 100.0; + + qreal dx = ::sin(angle) * 10; + mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5; + + rotate(dx); + setPos(mapToParent(0, -(3 + sin(speed) * 3))); +} +//! [11] diff --git a/examples/gestures/collidingmice/mouse.h b/examples/gestures/collidingmice/mouse.h new file mode 100644 index 0000000..7545fe6 --- /dev/null +++ b/examples/gestures/collidingmice/mouse.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOUSE_H +#define MOUSE_H + +#include +#include + +//! [0] +class Mouse : public QObject, public QGraphicsItem +{ + Q_OBJECT + +public: + Mouse(); + + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget); + +protected: + void timerEvent(QTimerEvent *event); + +private: + qreal angle; + qreal speed; + qreal mouseEyeDirection; + QColor color; +}; +//! [0] + +#endif -- cgit v0.12 From 90ead023ec593bd3dba0ef8eb4e38f2299cfb783 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 6 Mar 2009 16:55:02 +0100 Subject: Implemented LinjaZax-like gesture in collidingmice example. --- examples/gestures/collidingmice/collidingmice.pro | 8 +- .../collidingmice/gesturerecognizerlinjazax.cpp | 189 +++++++++++++++++++++ .../collidingmice/gesturerecognizerlinjazax.h | 65 +++++++ examples/gestures/collidingmice/images/cheese.jpg | Bin 0 -> 3029 bytes examples/gestures/collidingmice/linjazaxgesture.h | 58 +++++++ examples/gestures/collidingmice/main.cpp | 19 ++- 6 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp create mode 100644 examples/gestures/collidingmice/gesturerecognizerlinjazax.h create mode 100644 examples/gestures/collidingmice/images/cheese.jpg create mode 100644 examples/gestures/collidingmice/linjazaxgesture.h diff --git a/examples/gestures/collidingmice/collidingmice.pro b/examples/gestures/collidingmice/collidingmice.pro index 5c7b42b..15164ce 100644 --- a/examples/gestures/collidingmice/collidingmice.pro +++ b/examples/gestures/collidingmice/collidingmice.pro @@ -1,8 +1,12 @@ HEADERS += \ - mouse.h + mouse.h \ + gesturerecognizerlinjazax.h \ + linjazaxgesture.h + SOURCES += \ main.cpp \ - mouse.cpp + mouse.cpp \ + gesturerecognizerlinjazax.cpp RESOURCES += \ mice.qrc diff --git a/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp b/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp new file mode 100644 index 0000000..4c57209 --- /dev/null +++ b/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp @@ -0,0 +1,189 @@ +#include "gesturerecognizerlinjazax.h" + +#include +#include + +static const int SIZE = 20; + +DirectionSimpleRecognizer::DirectionSimpleRecognizer() +{ +} + +Direction DirectionSimpleRecognizer::addPosition(const QPoint &pos) +{ + if (!directions.isEmpty()) { + const QPoint tmp = pos - directions.back().point; + if (tmp.manhattanLength() < 5) + return Direction(); + } + if (lastPoint.isNull()) { + lastPoint = pos; + return Direction(); + } + int dx = pos.x() - lastPoint.x(); + int dy = pos.y() - lastPoint.y(); + QString direction; + if (dx < 0) { + if (-1*dx >= SIZE/2) + direction = "4"; + } else { + if (dx >= SIZE/2) + direction = "6"; + } + if (dy < 0) { + if (-1*dy >= SIZE/2) + direction = "8"; + } else { + if (dy >= SIZE/2) + direction = "2"; + } + if (direction.isEmpty()) + return Direction(); + + lastPoint = pos; + directions.push_back(Direction(direction, pos)); + return Direction(direction, pos); +} + + +DirectionList DirectionSimpleRecognizer::getDirections() const +{ + return directions; +} + +void DirectionSimpleRecognizer::reset() +{ + directions.clear(); + lastPoint = QPoint(); +} + +/////////////////////////////////////////////////////////////////////////// + +GestureRecognizerLinjaZax::GestureRecognizerLinjaZax() + : QGestureRecognizer("LinjaZax"), mousePressed(false), gestureFinished(false), + zoomState(LinjaZaxGesture::NoZoom) +{ +} + +QGestureRecognizer::Result GestureRecognizerLinjaZax::recognize(const QList &inputEvents) +{ + // get all mouse events + QList events; + for(int i = 0; i < inputEvents.count(); ++i) { + QEvent *event = inputEvents.at(i); + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + events.push_back(static_cast(event)); + default: + break; + } + } + + if (zoomState != LinjaZaxGesture::NoZoom && !lastDirections.isEmpty()) { + lastDirections = lastDirections.right(1); + zoomState = LinjaZaxGesture::NoZoom; + } + + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + for(int i = 0; i < events.count(); ++i) { + QMouseEvent *event = events.at(i); + if (event->type() == QEvent::MouseButtonPress) { + if (!currentDirection.isEmpty()) { + result = QGestureRecognizer::NotGesture; + reset(); + break; + } + result = QGestureRecognizer::MaybeGesture; + mousePressed = true; + pressedPos = lastPos = currentPos = event->pos(); + } else if (event->type() == QEvent::MouseButtonRelease) { + if (mousePressed && !currentDirection.isEmpty()) { + result = QGestureRecognizer::GestureFinished; + gestureFinished = true; + currentPos = event->pos(); + internalReset(); + break; + } + result = QGestureRecognizer::NotGesture; + reset(); + break; + } else if (event->type() == QEvent::MouseMove) { + if (!mousePressed) + continue; + lastPos = currentPos; + currentPos = event->pos(); + QString direction = + simpleRecognizer.addPosition(event->pos()).direction; + if (currentDirection.isEmpty()) { + if (direction.isEmpty()) + result = QGestureRecognizer::MaybeGesture; + else + result = QGestureRecognizer::GestureStarted; + } else { + result = QGestureRecognizer::GestureStarted; + } + if (!direction.isEmpty()) { + if (currentDirection != direction) + lastDirections.append(direction); + currentDirection = direction; + if (lastDirections.length() > 5) + lastDirections.remove(0, 1); + if (lastDirections.contains("248")) + zoomState = LinjaZaxGesture::ZoomingIn; + else if (lastDirections.contains("268")) + zoomState = LinjaZaxGesture::ZoomingOut; + } + } + } + return result; +} + +static inline LinjaZaxGesture::DirectionType convertPanningDirection(const QString &direction) +{ + if (direction.length() == 1) { + if (direction == "4") + return LinjaZaxGesture::Left; + else if (direction == "6") + return LinjaZaxGesture::Right; + else if (direction == "8") + return LinjaZaxGesture::Up; + else if (direction == "2") + return LinjaZaxGesture::Down; + } + return LinjaZaxGesture::None; +} + +QGesture* GestureRecognizerLinjaZax::makeEvent() const +{ + LinjaZaxGesture::DirectionType dir = convertPanningDirection(currentDirection); + LinjaZaxGesture::DirectionType lastDir = convertPanningDirection(lastDirections.right(1)); + if (dir == LinjaZaxGesture::None) + return 0; + LinjaZaxGesture *g = + new LinjaZaxGesture("LinjaZax", pressedPos, lastPos, currentPos, + QRect(), pressedPos, QDateTime(), 0, + gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); + g->lastDirection_ = lastDir; + g->direction_ = dir; + g->zoomState_ = zoomState; + + return g; +} + +void GestureRecognizerLinjaZax::reset() +{ + mousePressed = false; + lastDirections.clear(); + currentDirection.clear(); + gestureFinished = false; + simpleRecognizer.reset(); + zoomState = LinjaZaxGesture::NoZoom; +} + +void GestureRecognizerLinjaZax::internalReset() +{ + mousePressed = false; + simpleRecognizer.reset(); +} diff --git a/examples/gestures/collidingmice/gesturerecognizerlinjazax.h b/examples/gestures/collidingmice/gesturerecognizerlinjazax.h new file mode 100644 index 0000000..6579059 --- /dev/null +++ b/examples/gestures/collidingmice/gesturerecognizerlinjazax.h @@ -0,0 +1,65 @@ +#ifndef GESTURERECOGNIZERLINJAZAX_H +#define GESTURERECOGNIZERLINJAZAX_H + +#include +#include +#include +#include +#include + +#include "linjazaxgesture.h" + +struct Direction +{ + QString direction; + QPoint point; + + Direction(QString dir, const QPoint &pt) + : direction(dir), point(pt) { } + Direction() + : direction() { } + + inline bool isEmpty() const { return direction.isEmpty(); } + inline bool isNull() const { return direction.isEmpty(); } +}; +typedef QList DirectionList; + +class DirectionSimpleRecognizer +{ +public: + DirectionSimpleRecognizer(); + Direction addPosition(const QPoint &pos); + DirectionList getDirections() const; + void reset(); + +private: + QPoint lastPoint; + DirectionList directions; +}; + +class GestureRecognizerLinjaZax : public QGestureRecognizer +{ + Q_OBJECT +public: + GestureRecognizerLinjaZax(); + + QGestureRecognizer::Result recognize(const QList &inputEvents); + QGesture* makeEvent() const; + + void reset(); + +private: + void internalReset(); + + QPoint pressedPos; + QPoint lastPos; + QPoint currentPos; + bool mousePressed; + bool gestureFinished; + QString lastDirections; + QString currentDirection; + DirectionSimpleRecognizer simpleRecognizer; + LinjaZaxGesture::ZoomState zoomState; +}; + +#endif diff --git a/examples/gestures/collidingmice/images/cheese.jpg b/examples/gestures/collidingmice/images/cheese.jpg new file mode 100644 index 0000000..dea5795 Binary files /dev/null and b/examples/gestures/collidingmice/images/cheese.jpg differ diff --git a/examples/gestures/collidingmice/linjazaxgesture.h b/examples/gestures/collidingmice/linjazaxgesture.h new file mode 100644 index 0000000..9601675 --- /dev/null +++ b/examples/gestures/collidingmice/linjazaxgesture.h @@ -0,0 +1,58 @@ +#ifndef LINJAZAXGESTURE_H +#define LINJAZAXGESTURE_H + +#include + +class Q_GUI_EXPORT LinjaZaxGesture : public QGesture +{ +public: + enum DirectionType + { + None = 0, + LeftDown = 1, + DownLeft = LeftDown, + Down = 2, + RightDown = 3, + DownRight = RightDown, + Left = 4, + Right = 6, + LeftUp = 7, + UpLeft = LeftUp, + Up = 8, + RightUp = 9, + UpRight = RightUp + }; + + enum ZoomState + { + NoZoom, + ZoomingIn, + ZoomingOut + }; + +public: + explicit LinjaZaxGesture(const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted) + : QGesture(type, state), lastDirection_(None), direction_(None), zoomState_(NoZoom) { } + LinjaZaxGesture(const Qt::GestureType &type, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state) + : QGesture(type, startPos, lastPos, pos, rect, hotSpot, startTime, duration, state) { } + ~LinjaZaxGesture() { } + + DirectionType lastDirection() const + { return lastDirection_; } + DirectionType direction() const + { return direction_; } + + ZoomState zoomState() const + { return zoomState_; } + +private: + DirectionType lastDirection_; + DirectionType direction_; + ZoomState zoomState_; + friend class GestureRecognizerLinjaZax; +}; + +#endif diff --git a/examples/gestures/collidingmice/main.cpp b/examples/gestures/collidingmice/main.cpp index 56e9f7f..9f50379 100644 --- a/examples/gestures/collidingmice/main.cpp +++ b/examples/gestures/collidingmice/main.cpp @@ -41,6 +41,9 @@ #include "mouse.h" +#include "gesturerecognizerlinjazax.h" +#include "linjazaxgesture.h" + #include #include @@ -53,14 +56,25 @@ public: PannableGraphicsView(QGraphicsScene *scene, QWidget *parent = 0) : QGraphicsView(scene, parent) { - grabGesture(Qt::Pan); + grabGesture("LinjaZax"); } protected: bool event(QEvent *event) { if (event->type() == QEvent::Gesture) { QGestureEvent *ge = static_cast(event); - if (const QPannableGesture *g = dynamic_cast(ge->gesture(Qt::Pan))) { + const LinjaZaxGesture *g = dynamic_cast(ge->gesture("LinjaZax")); + if (g) { + switch (g->zoomState()) { + case LinjaZaxGesture::ZoomingIn: + scale(1.5, 1.5); + break; + case LinjaZaxGesture::ZoomingOut: + scale(0.6, 0.6); + break; + default: + break; + }; QPoint pt = g->pos() - g->lastPos(); horizontalScrollBar()->setValue(horizontalScrollBar()->value() - pt.x()); verticalScrollBar()->setValue(verticalScrollBar()->value() - pt.y()); @@ -77,6 +91,7 @@ int main(int argc, char **argv) { QApplication app(argc, argv); QApplication::setAttribute(Qt::AA_EnableGestures); + app.addGestureRecognizer(new GestureRecognizerLinjaZax); qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); //! [0] -- cgit v0.12 From ed24f677a411c449a2a3f862906aff2a313b4425 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 6 Mar 2009 18:49:56 +0100 Subject: Added animation to zooming gesture in collidingmice example. --- .../collidingmice/gesturerecognizerlinjazax.cpp | 7 ++--- examples/gestures/collidingmice/main.cpp | 35 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp b/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp index 4c57209..3f90588 100644 --- a/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp +++ b/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp @@ -125,14 +125,13 @@ QGestureRecognizer::Result GestureRecognizerLinjaZax::recognize(const QList 5) lastDirections.remove(0, 1); - if (lastDirections.contains("248")) + if (lastDirections.contains("248") || lastDirections.contains("2448")) zoomState = LinjaZaxGesture::ZoomingIn; - else if (lastDirections.contains("268")) + else if (lastDirections.contains("268") || lastDirections.contains("2668")) zoomState = LinjaZaxGesture::ZoomingOut; } } diff --git a/examples/gestures/collidingmice/main.cpp b/examples/gestures/collidingmice/main.cpp index 9f50379..d6dbdb0 100644 --- a/examples/gestures/collidingmice/main.cpp +++ b/examples/gestures/collidingmice/main.cpp @@ -48,15 +48,26 @@ #include +#define ZOOMING_ANIMATION +#ifdef ZOOMING_ANIMATION +static const int AnimationSteps = 10; +#endif + static const int MouseCount = 7; class PannableGraphicsView : public QGraphicsView { + Q_OBJECT public: PannableGraphicsView(QGraphicsScene *scene, QWidget *parent = 0) : QGraphicsView(scene, parent) { grabGesture("LinjaZax"); +#ifdef ZOOMING_ANIMATION + timeline = new QTimeLine(700, this); + timeline->setFrameRange(0, AnimationSteps); + connect(timeline, SIGNAL(frameChanged(int)), this, SLOT(animationStep(int))); +#endif } protected: bool event(QEvent *event) @@ -67,10 +78,22 @@ protected: if (g) { switch (g->zoomState()) { case LinjaZaxGesture::ZoomingIn: +#ifdef ZOOMING_ANIMATION + scaleStep = 1. + 0.5/AnimationSteps; + timeline->stop(); + timeline->start(); +#else scale(1.5, 1.5); +#endif break; case LinjaZaxGesture::ZoomingOut: +#ifdef ZOOMING_ANIMATION + scaleStep = 1. - 0.5/AnimationSteps; + timeline->stop(); + timeline->start(); +#else scale(0.6, 0.6); +#endif break; default: break; @@ -84,6 +107,16 @@ protected: } return QGraphicsView::event(event); } +private slots: +#ifdef ZOOMING_ANIMATION + void animationStep(int step) + { + scale(scaleStep, scaleStep); + } +private: + qreal scaleStep; + QTimeLine *timeline; +#endif }; //! [0] @@ -127,3 +160,5 @@ int main(int argc, char **argv) return app.exec(); } //! [6] + +#include "main.moc" -- cgit v0.12 From 3e2cef8f6a58949b887add8ff1bb5e457e7be542 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 12 Mar 2009 16:07:36 +0100 Subject: Added QGraphicsSceneGestureEvent that extends plain QGestureEvent by providing some additional info (like a widget that received a gesture - for coordinates conversions). --- examples/gestures/graphicsview/main.cpp | 4 +- src/corelib/kernel/qcoreevent.h | 1 + src/gui/graphicsview/qgraphicsscene.cpp | 34 +++--- src/gui/graphicsview/qgraphicsscene.h | 3 +- src/gui/graphicsview/qgraphicsscene_p.h | 1 - src/gui/graphicsview/qgraphicssceneevent.cpp | 148 +++++++++++++++++++++++++++ src/gui/graphicsview/qgraphicssceneevent.h | 40 ++++++++ src/gui/graphicsview/qgraphicsview.cpp | 12 ++- src/gui/kernel/qapplication.cpp | 1 - src/gui/kernel/qevent.cpp | 26 +++-- src/gui/kernel/qevent.h | 8 +- src/gui/kernel/qgesturemanager.cpp | 8 +- 12 files changed, 236 insertions(+), 50 deletions(-) diff --git a/examples/gestures/graphicsview/main.cpp b/examples/gestures/graphicsview/main.cpp index 250da06..1b325ee 100644 --- a/examples/gestures/graphicsview/main.cpp +++ b/examples/gestures/graphicsview/main.cpp @@ -94,8 +94,8 @@ public: bool sceneEvent(QEvent *event) { - if (event->type() == QEvent::Gesture) { - QGestureEvent *gestureEvent = static_cast(event); + if (event->type() == QEvent::GraphicsSceneGesture) { + QGraphicsSceneGestureEvent *gestureEvent = static_cast(event); if (gestureEvent->gesture(Qt::DoubleTap)) { event->accept(); colored = !colored; diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 4ea3c70..cb24723 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -274,6 +274,7 @@ public: GraphicsSceneTouchEnd = 198, Gesture = 191, + GraphicsSceneGesture = 192, // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 21c7c81..7e23620 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3951,8 +3951,8 @@ bool QGraphicsScene::event(QEvent *event) // geometries that do not have an explicit style set. update(); break; - case QEvent::Gesture: - gestureEvent(static_cast(event)); + case QEvent::GraphicsSceneGesture: + gestureEvent(static_cast(event)); break; case QEvent::GraphicsSceneTouchBegin: d->touchBeginEvent(static_cast(event)); @@ -5604,36 +5604,32 @@ void QGraphicsScenePrivate::removeView(QGraphicsView *view) view->releaseGesture(gesture); } -void QGraphicsScenePrivate::sendGestureEvent(QGraphicsItem *item, QGestureEvent *event) -{ - //### TODO: position translation - sendEvent(item, event); -} - -void QGraphicsScene::gestureEvent(QGestureEvent *event) +void QGraphicsScene::gestureEvent(QGraphicsSceneGestureEvent *event) { Q_D(QGraphicsScene); QList gestureTypes = event->gestureTypes(); - QGraphicsView *view = qobject_cast(event->targetWidget()); + QGraphicsView *view = qobject_cast(event->widget()); if (!view) { - // something is wrong. - Q_ASSERT(view); + qWarning("QGraphicsScene::gestureEvent: gesture event was received without a view"); return; } + + // find graphics items that intersects with gestures hot spots. QPolygonF poly; - QMap hotSpots; + QMap sceneHotSpots; foreach(const Qt::GestureType &type, gestureTypes) { - QPointF pt = view->mapToScene(event->gesture(type)->hotSpot()); - hotSpots.insert(type, pt); + QPointF pt = event->mapToScene(event->gesture(type)->hotSpot()); + sceneHotSpots.insert(type, pt); poly << pt; } + QList itemsInGestureArea = items(poly, Qt::IntersectsItemBoundingRect); - foreach(QGraphicsItem *item, items(poly, Qt::IntersectsItemBoundingRect)) { - QMap::const_iterator it = hotSpots.begin(), - e = hotSpots.end(); + foreach(QGraphicsItem *item, itemsInGestureArea) { + QMap::const_iterator it = sceneHotSpots.begin(), + e = sceneHotSpots.end(); for(; it != e; ++it) { if (item->contains(item->mapFromScene(it.value())) && item->gestures().contains(it.key())) { - d->sendGestureEvent(item, event); + d->sendEvent(item, event); if (event->isAccepted()) break; } diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 4680455..45def44 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -80,6 +80,7 @@ class QGraphicsSceneHelpEvent; class QGraphicsSceneHoverEvent; class QGraphicsSceneMouseEvent; class QGraphicsSceneWheelEvent; +class QGraphicsSceneGestureEvent; class QGraphicsSimpleTextItem; class QGraphicsTextItem; class QGraphicsView; @@ -255,7 +256,7 @@ protected: virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); virtual void wheelEvent(QGraphicsSceneWheelEvent *event); virtual void inputMethodEvent(QInputMethodEvent *event); - virtual void gestureEvent(QGestureEvent *event); + virtual void gestureEvent(QGraphicsSceneGestureEvent *event); virtual void drawBackground(QPainter *painter, const QRectF &rect); virtual void drawForeground(QPainter *painter, const QRectF &rect); diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index cea0553..1cd1788 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -271,7 +271,6 @@ public: QSet grabbedGestures; void grabGesture(QGraphicsItem *item, const Qt::GestureType &type); void releaseGesture(QGraphicsItem *item, const Qt::GestureType &type); - void sendGestureEvent(QGraphicsItem *item, QGestureEvent *event); mutable QVector sceneTransformCache; mutable QBitArray validTransforms; diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 3c9799b..4d9e7bb 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -299,6 +299,8 @@ #include #include #include +#include "qgraphicsview.h" +#include "qgraphicsitem.h" QT_BEGIN_NAMESPACE @@ -1704,6 +1706,152 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) d->newPos = pos; } +/*! + \class QGraphicsSceneGestureEvent + \brief The QGraphicsSceneGestureEvent class provides gesture events for + the graphics view framework. + \since 4.6 + \ingroup graphicsview-api + + QGraphicsSceneGestureEvent extends information provided by + QGestureEvent by adding some convenience functions like + \l{QGraphicsSceneEvent::}{widget()} to get a widget that received + original gesture event, and convenience functions mapToScene(), + mapToItem() for converting positions of the gesture into + QGraphicsScene and QGraphicsItem coordinate system respectively. + + The scene sends the event to the first QGraphicsItem under the + mouse cursor that accepts gestures; a graphics item is set to accept + gestures with \l{QGraphicsItem::}{grabGesture()}. +*/ + +/*! \fn bool QGraphicsSceneGestureEvent::contains(const Qt::GestureType &type) const + + Checks if the gesture event contains gesture of specific \a type. +*/ + +/*! \fn QList QGraphicsSceneGestureEvent::gestureTypes() const + + Returns a list of gesture names that the event contains. +*/ + +/*! \fn const QGesture* QGraphicsSceneGestureEvent::gesture(const Qt::GestureType &type) const + + Returns extended information about a gesture of specific \a type. +*/ + +/*! \fn QList > QGraphicsSceneGestureEvent::gestures() const + + Returns extended information about all triggered gestures. +*/ + +/*! \fn QSet QGraphicsSceneGestureEvent::cancelledGestures() const + + Returns a set of gesture names that used to be executed, but got + cancelled (i.e. they were not finished properly). +*/ + +/*! \fn void QGraphicsSceneGestureEvent::setCancelledGestures(const QSet &) + + Returns a set of gesture names that used to be executed, but got + cancelled (i.e. they were not finished properly). +*/ + +QGraphicsSceneGestureEvent::QGraphicsSceneGestureEvent() + : QGraphicsSceneEvent(QEvent::GraphicsSceneGesture) +{ +} + +QGraphicsSceneGestureEvent::~QGraphicsSceneGestureEvent() +{ +} + +/*! + Maps the point \a point, which is in a view coordinate system, to + scene coordinate system, and returns the mapped coordinate. + + \a Point is in coordinate system of the widget that received + gesture event. + + \sa mapToScene(const QRect &rect), + mapToItem(const QPoint &point, QGraphicsItem *item), + mapToItem(const QRect &rect, QGraphicsItem *item), + {The Graphics View Coordinate System} +*/ +QPointF QGraphicsSceneGestureEvent::mapToScene(const QPoint &point) const +{ + if (QGraphicsView *view = qobject_cast(widget())) + return view->mapToScene(point); + return QPointF(); +} + +/*! + Maps the rectangular \a rect, which is in a view coordinate system, to + scene coordinate system, and returns the mapped coordinate. + + \a Point is in coordinate system of the widget that received + gesture event. + + \sa mapToScene(const QPoint &rect), + mapToItem(const QPoint &point, QGraphicsItem *item), + mapToItem(const QRect &rect, QGraphicsItem *item), + {The Graphics View Coordinate System} +*/ +QPolygonF QGraphicsSceneGestureEvent::mapToScene(const QRect &rect) const +{ + if (QGraphicsView *view = qobject_cast(widget())) + return view->mapToScene(rect); + return QPolygonF(); +} + +/*! + Maps the point \a point, which is in a view coordinate system, to + item's \a item coordinate system, and returns the mapped coordinate. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa mapToScene(const QPoint &rect), mapToScene(const QRect &rect), + mapToItem(const QRect &, QGraphicsItem *item), + {The Graphics View Coordinate System} +*/ +QPointF QGraphicsSceneGestureEvent::mapToItem(const QPoint &point, QGraphicsItem *item) const +{ + if (item) { + if (QGraphicsView *view = qobject_cast(widget())) + return item->mapFromScene(view->mapToScene(point)); + } else { + return mapToScene(point); + } + return QPointF(); +} + +/*! + Maps the point \a point, which is in a view coordinate system, to + item's \a item coordinate system, and returns the mapped coordinate. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa mapToScene(const QPoint &rect), mapToScene(const QRect &rect), + mapToItem(const QPoint &point, QGraphicsItem *item), + {The Graphics View Coordinate System} +*/ +QPolygonF QGraphicsSceneGestureEvent::mapToItem(const QRect &rect, QGraphicsItem *item) const +{ + if (item) { + if (QGraphicsView *view = qobject_cast(widget())) + return item->mapFromScene(view->mapToScene(rect)); + } else { + return mapToScene(rect); + } + return QPolygonF(); +} + +void QGraphicsSceneGestureEvent::setGestures(const QList > &gestures) +{ + foreach(const QSharedPointer &g, gestures) + m_gestures.insert(g->gestureType(), g); +} + class QGraphicsSceneTouchEventPrivate : public QGraphicsSceneEventPrivate { Q_DECLARE_PUBLIC(QGraphicsSceneTouchEvent) diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index 8ae3a99..d3899cd 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -44,6 +44,11 @@ #include #include +#include +#include +#include +#include +#include QT_BEGIN_HEADER @@ -302,6 +307,41 @@ public: void setNewPos(const QPointF &pos); }; +class QGesture; +class QGraphicsItem; +class QGraphicsSceneGestureEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneGestureEvent : public QGraphicsSceneEvent +{ + Q_DECLARE_PRIVATE(QGraphicsSceneGestureEvent) +public: + QGraphicsSceneGestureEvent(); + ~QGraphicsSceneGestureEvent(); + + inline bool contains(const Qt::GestureType &type) const + { return gesture(type) != 0; } + inline QList gestureTypes() const + { return m_gestures.keys(); } + inline const QGesture* gesture(const Qt::GestureType &type) const + { return m_gestures.value(type, QSharedPointer()).data(); } + inline QList > gestures() const + { return m_gestures.values(); } + void setGestures(const QList > &gestures); + + inline QSet cancelledGestures() const + { return m_cancelledGestures; } + void setCancelledGestures(const QSet &cancelledGestures) + { m_cancelledGestures = cancelledGestures; } + + QPointF mapToScene(const QPoint &point) const; + QPolygonF mapToScene(const QRect &rect) const; + QPointF mapToItem(const QPoint &point, QGraphicsItem *item) const; + QPolygonF mapToItem(const QRect &rect, QGraphicsItem *item) const; + +protected: + QHash > m_gestures; + QSet m_cancelledGestures; +}; + class QGraphicsSceneTouchEventPrivate; class QGraphicsSceneTouchEventTouchPointPrivate; class Q_GUI_EXPORT QGraphicsSceneTouchEvent : public QGraphicsSceneEvent diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 97903d3..925763c 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -2862,10 +2862,16 @@ bool QGraphicsView::event(QEvent *event) } } break; - case QEvent::Gesture: - QApplication::sendEvent(d->scene, event); - if (event->isAccepted()) + case QEvent::Gesture: { + QGraphicsSceneGestureEvent gestureEvent; + gestureEvent.setWidget(this); + QGestureEvent *ev = static_cast(event); + gestureEvent.setGestures(ev->gestures()); + gestureEvent.setCancelledGestures(ev->cancelledGestures()); + QApplication::sendEvent(d->scene, &gestureEvent); + if (gestureEvent.isAccepted()) return true; + } break; default: break; diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 104271d..5774ae0 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4037,7 +4037,6 @@ bool QApplication::notify(QObject *receiver, QEvent *e) // QGestureEvent ge(&qge, false); // ### TODO: fix widget-relative positions in gesture event. QGestureEvent ge = *g; - ge.m_targetWidget = w; ge.spont = g->spontaneous(); res = d->notify_helper(w, w == receiver ? g : &ge); g->spont = false; diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index d7d193c..6cdf781 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3524,27 +3524,27 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) \sa QGesture */ -/*! \fn QWidget *QGestureEvent::targetWidget() const - - Returns the widget the gesture event is send to. -*/ - -/*! \fn bool contains(const Qt::GestureType &type) const +/*! \fn bool QGestureEvent::contains(const Qt::GestureType &type) const Checks if the gesture event contains gesture of specific \a type. */ -/*! \fn QList gestureTypes() const +/*! \fn QList QGestureEvent::gestureTypes() const Returns a list of gesture names that the event contains. */ -/*! \fn const QGesture* gesture(const Qt::GestureType &type) const +/*! \fn const QGesture* QGestureEvent::gesture(const Qt::GestureType &type) const Returns extended information about a gesture of specific \a type. */ -/*! \fn QSet cancelledGestures() const +/*! \fn QList > QGestureEvent::gestures() const + + Returns extended information about all triggered gestures. +*/ + +/*! \fn QSet QGestureEvent::cancelledGestures() const Returns a set of gesture names that used to be executed, but got cancelled (i.e. they were not finished properly). @@ -3553,18 +3553,16 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) -QGestureEvent::QGestureEvent(QWidget *targetWidget, const QList &gestures, +QGestureEvent::QGestureEvent(const QList &gestures, const QSet &cancelledGestures) - : QEvent(QEvent::Gesture), m_targetWidget(targetWidget), - m_cancelledGestures(cancelledGestures) + : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) { foreach(QGesture *r, gestures) m_gestures.insert(r->gestureType(), QSharedPointer(r)); } QGestureEvent::QGestureEvent(const QGestureEvent &event, const QPoint &offset) - : QEvent(QEvent::Gesture), m_targetWidget(event.m_targetWidget), - m_gestures(event.m_gestures), + : QEvent(QEvent::Gesture), m_gestures(event.m_gestures), m_cancelledGestures(event.m_cancelledGestures) { //### use offset! diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index cde7cb2..603f358 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -717,27 +717,25 @@ private: class Q_GUI_EXPORT QGestureEvent : public QEvent { public: - QGestureEvent(QWidget *targetWidget, const QList &gestures, + QGestureEvent(const QList &gestures, const QSet &cancelledGestures = QSet()); // internal ctor QGestureEvent(const QGestureEvent &gestures, const QPoint &offset); ~QGestureEvent(); - QWidget *targetWidget() const - { return m_targetWidget; } - inline bool contains(const Qt::GestureType &type) const { return gesture(type) != 0; } inline QList gestureTypes() const { return m_gestures.keys(); } inline const QGesture* gesture(const Qt::GestureType &type) const { return m_gestures.value(type, QSharedPointer()).data(); } + inline QList > gestures() const + { return m_gestures.values(); } inline QSet cancelledGestures() const { return m_cancelledGestures; } protected: - QWidget *m_targetWidget; QHash > m_gestures; QSet m_cancelledGestures; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 7954ae9..9a287a0 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -178,7 +178,7 @@ bool QGestureManager::filterEvent(QEvent *event) gestures << gesture; } Q_ASSERT(!gestures.isEmpty()); - QGestureEvent event(targetWidget, gestures); + QGestureEvent event(gestures); ret = qt_sendGestureEvent(targetWidget, &event); ret = ret && event.isAccepted(); @@ -283,7 +283,7 @@ bool QGestureManager::filterEvent(QEvent *event) foreach(QGestureRecognizer *r, cancelledGestures) cancelledGestureNames << r->gestureType(); if(!gestures.isEmpty()) { - QGestureEvent event(targetWidget, gestures, cancelledGestureNames); + QGestureEvent event(gestures, cancelledGestureNames); ret = qt_sendGestureEvent(targetWidget, &event); ret = ret && event.isAccepted(); } @@ -375,7 +375,7 @@ void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) if (QGesture *gesture = recognizer->makeEvent()) gestures << gesture; if(!gestures.isEmpty()) { - QGestureEvent event(targetWidget, gestures); + QGestureEvent event(gestures); qt_sendGestureEvent(targetWidget, &event); } if (result == QGestureRecognizer::GestureFinished) @@ -385,7 +385,7 @@ void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) case QGestureRecognizer::MaybeGesture: { DEBUG() << "QGestureManager: maybe gesture: " << recognizer; if (activeGestures.contains(recognizer)) { - QGestureEvent event(targetWidget, QList(), + QGestureEvent event(QList(), QSet() << recognizer->gestureType()); qt_sendGestureEvent(targetWidget, &event); } -- cgit v0.12 From f8c5c2720b4a48dad0f68b83d9c494185e012cd1 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 12 Mar 2009 17:06:17 +0100 Subject: Moved the code that translated QGestureEvent to QGraphicsSceneGestureEvent to a viewport, so that the user could subscribe to gestures in a viewport as well. --- src/gui/graphicsview/qgraphicsview.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 925763c..6f142e3 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -2862,16 +2862,8 @@ bool QGraphicsView::event(QEvent *event) } } break; - case QEvent::Gesture: { - QGraphicsSceneGestureEvent gestureEvent; - gestureEvent.setWidget(this); - QGestureEvent *ev = static_cast(event); - gestureEvent.setGestures(ev->gestures()); - gestureEvent.setCancelledGestures(ev->cancelledGestures()); - QApplication::sendEvent(d->scene, &gestureEvent); - if (gestureEvent.isAccepted()) - return true; - } + case QEvent::Gesture: + viewportEvent(event); break; default: break; @@ -2953,6 +2945,17 @@ bool QGraphicsView::viewportEvent(QEvent *event) d->scene->d_func()->updateAll = false; } break; + case QEvent::Gesture: { + QGraphicsSceneGestureEvent gestureEvent; + gestureEvent.setWidget(this); + QGestureEvent *ev = static_cast(event); + gestureEvent.setGestures(ev->gestures()); + gestureEvent.setCancelledGestures(ev->cancelledGestures()); + QApplication::sendEvent(d->scene, &gestureEvent); + if (gestureEvent.isAccepted()) + return true; + } + break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: -- cgit v0.12 From 86a160b083f9d43c07d42bbd7d2ef8b676552e38 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 13 Mar 2009 18:08:18 +0100 Subject: Modifications after the api review by Brad. Gesture types are now separated to internal ones, which are listed as enums (though they might be converted to strings internally), and third party gestures which are referenced by strings. From now on QGesture objects derive from QObject, which means third party gesture recognizer developers can use QObjects property system to store custom data inside QGesture without need to subclass it. Some functions were renamed to show their purpose more clear. --- src/corelib/global/qnamespace.h | 18 +- src/gui/graphicsview/qgraphicsitem.cpp | 32 +- src/gui/graphicsview/qgraphicsitem.h | 6 +- src/gui/graphicsview/qgraphicsitem_p.h | 2 +- src/gui/graphicsview/qgraphicsscene.cpp | 33 +- src/gui/graphicsview/qgraphicsscene_p.h | 6 +- src/gui/graphicsview/qgraphicssceneevent.h | 14 +- src/gui/kernel/kernel.pri | 2 + src/gui/kernel/qapplication.cpp | 2 +- src/gui/kernel/qapplication_p.h | 4 +- src/gui/kernel/qevent.cpp | 2 +- src/gui/kernel/qevent.h | 15 +- src/gui/kernel/qgesture.cpp | 56 +-- src/gui/kernel/qgesture.h | 25 +- src/gui/kernel/qgesture_p.h | 7 +- src/gui/kernel/qgesturemanager.cpp | 69 ++-- src/gui/kernel/qgesturemanager_p.h | 4 +- src/gui/kernel/qgesturerecognizer.cpp | 71 ++++ src/gui/kernel/qgesturerecognizer.h | 35 +- src/gui/kernel/qgesturerecognizer_p.h | 72 ++++ src/gui/kernel/qgesturestandardrecognizers.cpp | 507 ++++++------------------- src/gui/kernel/qgesturestandardrecognizers_p.h | 60 +-- src/gui/kernel/qwidget.cpp | 66 ++-- src/gui/kernel/qwidget.h | 9 +- src/gui/kernel/qwidget_p.h | 3 +- 25 files changed, 501 insertions(+), 619 deletions(-) create mode 100644 src/gui/kernel/qgesturerecognizer.cpp create mode 100644 src/gui/kernel/qgesturerecognizer_p.h diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 5747c3c..56ee768 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1551,13 +1551,17 @@ public: TouchPointReleased }; - typedef QString GestureType; - static const char UnknownGesture[] = "???"; - static const char Tap[] = "Tap"; - static const char DoubleTap[] = "DoubleTap"; - static const char LongTap[] = "LongTap"; - static const char Pan[] = "Pan"; - static const char Pinch[] = "Pinch"; + enum GestureType + { + UnknownGesture, + TapGesture, + DoubleTapGesture, + TrippleTapGesture, + TapAndHoldGesture, + PanGesture, + PinchGesture + }; + enum GestureState { diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index a9ad1e1..cbd4834 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -561,6 +561,8 @@ public: }; Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore) +QString qt_getStandardGestureTypeName(Qt::GestureType type); + /*! \internal @@ -5788,23 +5790,27 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const return QVariant(); } -void QGraphicsItem::grabGesture(const Qt::GestureType &type) +int QGraphicsItem::grabGesture(Qt::GestureType type) { - d_ptr->gestures.insert(type); - if (d_ptr->scene) - d_ptr->scene->d_func()->grabGesture(this, type); + return grabGesture(qt_getStandardGestureTypeName(type)); } -void QGraphicsItem::releaseGesture(const Qt::GestureType &type) +int QGraphicsItem::grabGesture(const QString &type) { - d_ptr->gestures.remove(type); + int id = qHash(type); + QSet::iterator it = d_ptr->gestures.find(id); + if (it != d_ptr->gestures.end()) + return *it; + d_ptr->gestures << id; if (d_ptr->scene) - d_ptr->scene->d_func()->releaseGesture(this, type); + d_ptr->scene->d_func()->grabGesture(this, id); } -QSet QGraphicsItem::gestures() const +void QGraphicsItem::releaseGesture(int id) { - return d_ptr->gestures; + d_ptr->gestures.remove(id); + if (d_ptr->scene) + d_ptr->scene->d_func()->releaseGesture(this, id); } /*! @@ -5835,14 +5841,14 @@ QVariant QGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &va if (!qVariantValue(value)) { // the item has been removed from a scene, unsubscribe gestures. Q_ASSERT(d_ptr->scene); - foreach(const Qt::GestureType &gesture, d_ptr->gestures) - d_ptr->scene->d_func()->releaseGesture(this, gesture); + foreach(int id, d_ptr->gestures) + d_ptr->scene->d_func()->releaseGesture(this, id); } } else if (change == QGraphicsItem::ItemSceneHasChanged) { if (QGraphicsScene *scene = qVariantValue(value)) { // item has been added to the scene - foreach(const Qt::GestureType &gesture, d_ptr->gestures) - scene->d_func()->grabGesture(this, gesture); + foreach(int id, d_ptr->gestures) + scene->d_func()->grabGesture(this, id); } } return value; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 2b4cdf5..4e6e96d 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -338,9 +338,9 @@ public: QVariant data(int key) const; void setData(int key, const QVariant &value); - void grabGesture(const Qt::GestureType &type); - void releaseGesture(const Qt::GestureType &type); - QSet gestures() const; + int grabGesture(Qt::GestureType type); + int grabGesture(const QString &type); + void releaseGesture(int gestureId); enum { Type = 1, diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index a67a49a..f6daa49 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -298,7 +298,7 @@ public: int siblingIndex; int index; int depth; - QSet gestures; + QSet gestures; // Packed 32 bytes quint32 acceptedMouseButtons : 5; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 7e23620..0f70cfe 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -5593,21 +5593,21 @@ void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) void QGraphicsScenePrivate::addView(QGraphicsView *view) { views << view; - foreach(const Qt::GestureType &gesture, grabbedGestures) - view->grabGesture(gesture); + foreach(int gestureId, grabbedGestures) + view->d_func()->grabGesture(gestureId); } void QGraphicsScenePrivate::removeView(QGraphicsView *view) { views.removeAll(view); - foreach(const Qt::GestureType &gesture, grabbedGestures) - view->releaseGesture(gesture); + foreach(int gestureId, grabbedGestures) + view->releaseGesture(gestureId); } void QGraphicsScene::gestureEvent(QGraphicsSceneGestureEvent *event) { Q_D(QGraphicsScene); - QList gestureTypes = event->gestureTypes(); + QList gestureTypes = event->gestureTypes(); QGraphicsView *view = qobject_cast(event->widget()); if (!view) { qWarning("QGraphicsScene::gestureEvent: gesture event was received without a view"); @@ -5616,19 +5616,20 @@ void QGraphicsScene::gestureEvent(QGraphicsSceneGestureEvent *event) // find graphics items that intersects with gestures hot spots. QPolygonF poly; - QMap sceneHotSpots; - foreach(const Qt::GestureType &type, gestureTypes) { + QMap sceneHotSpots; + foreach(const QString &type, gestureTypes) { QPointF pt = event->mapToScene(event->gesture(type)->hotSpot()); - sceneHotSpots.insert(type, pt); + sceneHotSpots.insert(qHash(type), pt); poly << pt; } QList itemsInGestureArea = items(poly, Qt::IntersectsItemBoundingRect); foreach(QGraphicsItem *item, itemsInGestureArea) { - QMap::const_iterator it = sceneHotSpots.begin(), - e = sceneHotSpots.end(); + QMap::const_iterator it = sceneHotSpots.begin(), + e = sceneHotSpots.end(); for(; it != e; ++it) { - if (item->contains(item->mapFromScene(it.value())) && item->gestures().contains(it.key())) { + if (item->contains(item->mapFromScene(it.value())) && + item->d_ptr->gestures.contains(it.key())) { d->sendEvent(item, event); if (event->isAccepted()) break; @@ -5637,17 +5638,17 @@ void QGraphicsScene::gestureEvent(QGraphicsSceneGestureEvent *event) } } -void QGraphicsScenePrivate::grabGesture(QGraphicsItem *item, const Qt::GestureType &type) +void QGraphicsScenePrivate::grabGesture(QGraphicsItem *item, int gestureId) { - if (!grabbedGestures.contains(type)) { + if (!grabbedGestures.contains(gestureId)) { foreach(QGraphicsView *view, views) - view->grabGesture(type); + view->d_func()->grabGesture(gestureId); } itemsWithGestures << item; - grabbedGestures << type; + grabbedGestures << gestureId; } -void QGraphicsScenePrivate::releaseGesture(QGraphicsItem *item, const Qt::GestureType &type) +void QGraphicsScenePrivate::releaseGesture(QGraphicsItem *item, int gestureId) { //### } diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 1cd1788..074e95c 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -268,9 +268,9 @@ public: void updatePalette(const QPalette &palette); QSet itemsWithGestures; - QSet grabbedGestures; - void grabGesture(QGraphicsItem *item, const Qt::GestureType &type); - void releaseGesture(QGraphicsItem *item, const Qt::GestureType &type); + QSet grabbedGestures; + void grabGesture(QGraphicsItem *item, int gestureId); + void releaseGesture(QGraphicsItem *item, int gestureId); mutable QVector sceneTransformCache; mutable QBitArray validTransforms; diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index d3899cd..4d88cda 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -317,19 +317,19 @@ public: QGraphicsSceneGestureEvent(); ~QGraphicsSceneGestureEvent(); - inline bool contains(const Qt::GestureType &type) const + inline bool contains(const QString &type) const { return gesture(type) != 0; } - inline QList gestureTypes() const + inline QList gestureTypes() const { return m_gestures.keys(); } - inline const QGesture* gesture(const Qt::GestureType &type) const + inline const QGesture* gesture(const QString &type) const { return m_gestures.value(type, QSharedPointer()).data(); } inline QList > gestures() const { return m_gestures.values(); } void setGestures(const QList > &gestures); - inline QSet cancelledGestures() const + inline QSet cancelledGestures() const { return m_cancelledGestures; } - void setCancelledGestures(const QSet &cancelledGestures) + void setCancelledGestures(const QSet &cancelledGestures) { m_cancelledGestures = cancelledGestures; } QPointF mapToScene(const QPoint &point) const; @@ -338,8 +338,8 @@ public: QPolygonF mapToItem(const QRect &rect, QGraphicsItem *item) const; protected: - QHash > m_gestures; - QSet m_cancelledGestures; + QHash > m_gestures; + QSet m_cancelledGestures; }; class QGraphicsSceneTouchEventPrivate; diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index b6ef6b2..adc2b10 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -44,6 +44,7 @@ HEADERS += \ kernel/qkeymapper_p.h \ kernel/qgesture.h \ kernel/qgesturemanager_p.h \ + kernel/qgesturerecognizer_p.h \ kernel/qgesturerecognizer.h \ kernel/qgesturestandardrecognizers_p.h \ kernel/qdirectionrecognizer_p.h \ @@ -79,6 +80,7 @@ SOURCES += \ kernel/qkeymapper.cpp \ kernel/qgesture.cpp \ kernel/qgesturemanager.cpp \ + kernel/qgesturerecognizer.cpp \ kernel/qgesturestandardrecognizers.cpp \ kernel/qdirectionrecognizer.cpp diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 5774ae0..efe1b13 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -3720,7 +3720,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) if (w && (mouse->type() != QEvent::MouseMove || mouse->buttons() != 0)) { // find the gesture target widget QWidget *target = w; - while (target && target->gestures().isEmpty()) + while (target && target->d_func()->gestures.isEmpty()) target = target->parentWidget(); if (target) { gm->setGestureTargetWidget(target); diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 40e928d..40cd4ae 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -428,7 +428,9 @@ public: void sendSyntheticEnterLeave(QWidget *widget); #endif - QMap grabbedGestures; + // map number of grabbed widgets (something like refcount) + QMap grabbedGestures; + static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); #if defined(Q_WS_WIN) diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 6cdf781..225d8b1 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3554,7 +3554,7 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) QGestureEvent::QGestureEvent(const QList &gestures, - const QSet &cancelledGestures) + const QSet &cancelledGestures) : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) { foreach(QGesture *r, gestures) diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 603f358..db3d03f 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -718,26 +718,27 @@ class Q_GUI_EXPORT QGestureEvent : public QEvent { public: QGestureEvent(const QList &gestures, - const QSet &cancelledGestures = QSet()); + const QSet &cancelledGestures = QSet()); // internal ctor QGestureEvent(const QGestureEvent &gestures, const QPoint &offset); ~QGestureEvent(); - inline bool contains(const Qt::GestureType &type) const + inline bool contains(const QString &type) const { return gesture(type) != 0; } - inline QList gestureTypes() const + inline QList gestureTypes() const { return m_gestures.keys(); } - inline const QGesture* gesture(const Qt::GestureType &type) const + + inline const QGesture* gesture(const QString &type) const { return m_gestures.value(type, QSharedPointer()).data(); } inline QList > gestures() const { return m_gestures.values(); } - inline QSet cancelledGestures() const + inline QSet cancelledGestures() const { return m_cancelledGestures; } protected: - QHash > m_gestures; - QSet m_cancelledGestures; + QHash > m_gestures; + QSet m_cancelledGestures; friend class QApplication; }; diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index fbabb8f..b2d357c 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -42,80 +42,80 @@ #include "qgesture.h" #include -QGesture::QGesture(const Qt::GestureType &type, Qt::GestureState state) - : d(new QGesturePrivate), gestureType_(type), gestureState_(state) +QT_BEGIN_NAMESPACE + +QString qt_getStandardGestureTypeName(Qt::GestureType type); + +QGesture::QGesture(QObject *parent, const QString &type, Qt::GestureState state) + : QObject(*new QGesturePrivate, parent), gestureType_(type), gestureState_(state) { } -QGesture::QGesture(const Qt::GestureType &type, const QPoint &startPos, +QGesture::QGesture(QObject *parent, const QString &type, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, uint duration, Qt::GestureState state) - : d(new QGesturePrivate), gestureType_(type), gestureState_(state) + : QObject(*new QGesturePrivate, parent), gestureType_(type), gestureState_(state) { - d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); + d_func()->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); } -QGesture::QGesture(QGesturePrivate &dd, const Qt::GestureType &type, +QGesture::QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, Qt::GestureState state) - : d(&dd), gestureType_(type), gestureState_(state) + : QObject(dd, parent), gestureType_(type), gestureState_(state) { } QGesture::~QGesture() { - delete d; d = 0; } QRect QGesture::rect() const { - return d->rect; + return d_func()->rect; } QPoint QGesture::hotSpot() const { - return d->hotSpot; + return d_func()->hotSpot; } QDateTime QGesture::startTime() const { - return d->startTime; + return d_func()->startTime; } uint QGesture::duration() const { - return d->duration; + return d_func()->duration; } QPoint QGesture::startPos() const { - return d->startPos; + return d_func()->startPos; } QPoint QGesture::lastPos() const { - return d->lastPos; + return d_func()->lastPos; } QPoint QGesture::pos() const { - return d->pos; + return d_func()->pos; } -QPannableGesture::QPannableGesture(const Qt::GestureType &type, Qt::GestureState state) - : QGesture(*new QPannableGesturePrivate, type, state) -{ -} - -QPannableGesture::QPannableGesture(const Qt::GestureType &type, const QPoint &startPos, +QPannableGesture::QPannableGesture(QObject *parent, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, uint duration, Qt::GestureState state) - : QGesture(*new QPannableGesturePrivate, type, state) + : QGesture(*new QPannableGesturePrivate, parent, + qt_getStandardGestureTypeName(Qt::PanGesture), state) { + Q_D(QPannableGesture); d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); - ((QPannableGesturePrivate*)d)->lastDirection = QPannableGesture::None; - ((QPannableGesturePrivate*)d)->direction = QPannableGesture::None; + d->lastDirection = QPannableGesture::None; + d->direction = QPannableGesture::None; } QPannableGesture::~QPannableGesture() @@ -124,10 +124,14 @@ QPannableGesture::~QPannableGesture() QPannableGesture::DirectionType QPannableGesture::lastDirection() const { - return ((QPannableGesturePrivate*)d)->lastDirection; + Q_D(const QPannableGesture); + return d->lastDirection; } QPannableGesture::DirectionType QPannableGesture::direction() const { - return ((QPannableGesturePrivate*)d)->direction; + Q_D(const QPannableGesture); + return d->direction; } + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 16a5704..f20a63c 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -56,17 +56,21 @@ QT_BEGIN_NAMESPACE QT_MODULE(Gui) class QGesturePrivate; -class Q_GUI_EXPORT QGesture +class Q_GUI_EXPORT QGesture : public QObject { + Q_OBJECT + Q_DECLARE_PRIVATE(QGesture) public: - explicit QGesture(const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted); - QGesture(const Qt::GestureType &type, const QPoint &startPos, + explicit QGesture(QObject *parent, const QString &type, + Qt::GestureState state = Qt::GestureStarted); + QGesture(QObject *parent, + const QString &type, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, uint duration, Qt::GestureState state); virtual ~QGesture(); - inline Qt::GestureType gestureType() const { return gestureType_; } + inline QString gestureType() const { return gestureType_; } inline Qt::GestureState state() const { return gestureState_; } QRect rect() const; @@ -79,16 +83,20 @@ public: QPoint pos() const; protected: - QGesture(QGesturePrivate &dd, const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted); - QGesturePrivate *d; + QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, Qt::GestureState state); + //### virtual void translateCoordinates(const QPoint &offset); private: - Qt::GestureType gestureType_; + QString gestureType_; Qt::GestureState gestureState_; }; +class QPannableGesturePrivate; class Q_GUI_EXPORT QPannableGesture : public QGesture { + Q_OBJECT + Q_DECLARE_PRIVATE(QPannableGesture) + public: enum DirectionType { @@ -108,8 +116,7 @@ public: }; public: - explicit QPannableGesture(const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted); - QPannableGesture(const Qt::GestureType &type, const QPoint &startPos, + QPannableGesture(QObject *parent, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, uint duration, Qt::GestureState state); diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 745590b..8a76186 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -56,11 +56,14 @@ #include "qrect.h" #include "qpoint.h" #include "qdatetime.h" +#include "private/qobject_p.h" QT_BEGIN_NAMESPACE -class QGesturePrivate +class QGesturePrivate : public QObjectPrivate { + Q_DECLARE_PUBLIC(QGesture) + public: QGesturePrivate() : duration(0) { } @@ -90,6 +93,8 @@ public: class QPannableGesturePrivate : public QGesturePrivate { + Q_DECLARE_PUBLIC(QPannableGesture) + public: QPannableGesture::DirectionType lastDirection; QPannableGesture::DirectionType direction; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 9a287a0..0e2cea8 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -44,9 +44,10 @@ #include "qevent.h" #include "qapplication.h" -#include "private/qapplication_p.h" +#include "qapplication_p.h" +#include "qwidget_p.h" -#include "private/qgesturestandardrecognizers_p.h" +#include "qgesturestandardrecognizers_p.h" #include "qdebug.h" @@ -60,13 +61,6 @@ QT_BEGIN_NAMESPACE bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); -static bool qt_sendGestureEvent(QWidget *receiver, QGestureEvent *event) -{ - QSet eventGestures = event->gestureTypes().toSet(); - while (receiver && (receiver->gestures() & eventGestures).isEmpty()) - receiver = receiver->parentWidget(); - return receiver ? qt_sendSpontaneousEvent(receiver, event) : false; -} static const unsigned int maximumGestureRecognitionTimeout = 2000; @@ -74,13 +68,12 @@ QGestureManager::QGestureManager() : targetWidget(0), state(NotGesture) { recognizers << new QDoubleTapGestureRecognizer(); - recognizers << new QLongTapGestureRecognizer(); + recognizers << new QTapAndHoldGestureRecognizer(); recognizers << new QGestureRecognizerPan(); - // recognizers << new QMultiTouchGestureRecognizer(); foreach(QGestureRecognizer *r, recognizers) - connect(r, SIGNAL(triggered(QGestureRecognizer::Result)), - this, SLOT(recognizerTriggered(QGestureRecognizer::Result))); + connect(r, SIGNAL(stateChanged(QGestureRecognizer::Result)), + this, SLOT(recognizerStateChanged(QGestureRecognizer::Result))); } void QGestureManager::addRecognizer(QGestureRecognizer *recognizer) @@ -98,9 +91,6 @@ bool QGestureManager::filterEvent(QEvent *event) if (!QApplication::testAttribute(Qt::AA_EnableGestures)) return false; - QList events; - events << event; - QPoint currentPos; switch (event->type()) { case QEvent::MouseButtonPress: @@ -111,7 +101,7 @@ bool QGestureManager::filterEvent(QEvent *event) default: break; } - const QMap &grabbedGestures = qApp->d_func()->grabbedGestures; + const QMap &grabbedGestures = qApp->d_func()->grabbedGestures; bool ret = false; QSet startedGestures; @@ -127,9 +117,9 @@ bool QGestureManager::filterEvent(QEvent *event) QSet stillMaybeGestures; // try other recognizers. foreach(QGestureRecognizer *r, recognizers) { - if (grabbedGestures.value(r->gestureType(), 0) <= 0) + if (grabbedGestures.value(qHash(r->gestureType()), 0) <= 0) continue; - QGestureRecognizer::Result result = r->recognize(events); + QGestureRecognizer::Result result = r->filterEvent(event); if (result == QGestureRecognizer::GestureStarted) { DEBUG() << "QGestureManager: gesture started: " << r; startedGestures << r; @@ -170,16 +160,16 @@ bool QGestureManager::filterEvent(QEvent *event) QList gestures; foreach(QGestureRecognizer *r, finishedGestures) { - if (QGesture *gesture = r->makeEvent()) + if (QGesture *gesture = r->getGesture()) gestures << gesture; } foreach(QGestureRecognizer *r, activeGestures) { - if (QGesture *gesture = r->makeEvent()) + if (QGesture *gesture = r->getGesture()) gestures << gesture; } Q_ASSERT(!gestures.isEmpty()); QGestureEvent event(gestures); - ret = qt_sendGestureEvent(targetWidget, &event); + ret = sendGestureEvent(targetWidget, &event); ret = ret && event.isAccepted(); if (!activeGestures.isEmpty()) { @@ -220,9 +210,9 @@ bool QGestureManager::filterEvent(QEvent *event) Q_ASSERT(!activeGestures.isEmpty()); foreach(QGestureRecognizer *r, recognizers) { - if (grabbedGestures.value(r->gestureType(), 0) <= 0) + if (grabbedGestures.value(qHash(r->gestureType()), 0) <= 0) continue; - QGestureRecognizer::Result result = r->recognize(events); + QGestureRecognizer::Result result = r->filterEvent(event); if (result == QGestureRecognizer::GestureStarted) { DEBUG() << "QGestureManager: gesture started: " << r; startedGestures << r; @@ -271,20 +261,20 @@ bool QGestureManager::filterEvent(QEvent *event) << activeGestures << " and " << finishedGestures; foreach(QGestureRecognizer *r, finishedGestures) { - if (QGesture *gesture = r->makeEvent()) + if (QGesture *gesture = r->getGesture()) gestures << gesture; } foreach(QGestureRecognizer *r, activeGestures) { - if (QGesture *gesture = r->makeEvent()) + if (QGesture *gesture = r->getGesture()) gestures << gesture; } } - QSet cancelledGestureNames; + QSet cancelledGestureNames; foreach(QGestureRecognizer *r, cancelledGestures) cancelledGestureNames << r->gestureType(); if(!gestures.isEmpty()) { QGestureEvent event(gestures, cancelledGestureNames); - ret = qt_sendGestureEvent(targetWidget, &event); + ret = sendGestureEvent(targetWidget, &event); ret = ret && event.isAccepted(); } @@ -343,7 +333,7 @@ void QGestureManager::setGestureTargetWidget(QWidget *widget) targetWidget = widget; } -void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) +void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) { if (!QApplication::testAttribute(Qt::AA_EnableGestures)) return; @@ -351,7 +341,7 @@ void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) QGestureRecognizer *recognizer = qobject_cast(sender()); if (!recognizer) return; - if (qApp->d_func()->grabbedGestures.value(recognizer->gestureType(), 0) <= 0) { + if (qApp->d_func()->grabbedGestures.value(qHash(recognizer->gestureType()), 0) <= 0) { recognizer->reset(); return; } @@ -372,11 +362,11 @@ void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) maybeGestures.remove(recognizer); } QList gestures; - if (QGesture *gesture = recognizer->makeEvent()) + if (QGesture *gesture = recognizer->getGesture()) gestures << gesture; if(!gestures.isEmpty()) { QGestureEvent event(gestures); - qt_sendGestureEvent(targetWidget, &event); + sendGestureEvent(targetWidget, &event); } if (result == QGestureRecognizer::GestureFinished) recognizer->reset(); @@ -386,8 +376,8 @@ void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) DEBUG() << "QGestureManager: maybe gesture: " << recognizer; if (activeGestures.contains(recognizer)) { QGestureEvent event(QList(), - QSet() << recognizer->gestureType()); - qt_sendGestureEvent(targetWidget, &event); + QSet() << recognizer->gestureType()); + sendGestureEvent(targetWidget, &event); } if (!maybeGestures.contains(recognizer)) { int timerId = startTimer(maximumGestureRecognitionTimeout); @@ -410,6 +400,17 @@ void QGestureManager::recognizerTriggered(QGestureRecognizer::Result result) } } +bool QGestureManager::sendGestureEvent(QWidget *receiver, QGestureEvent *event) +{ + QSet eventGestures; + foreach(const QString &gesture, event->gestureTypes()) + eventGestures << qHash(gesture); + + while (receiver && (receiver->d_func()->gestures & eventGestures).isEmpty()) + receiver = receiver->parentWidget(); + return receiver ? qt_sendSpontaneousEvent(receiver, event) : false; +} + QT_END_NAMESPACE #include "moc_qgesturemanager_p.cpp" diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 762b70d..18eed71 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -82,9 +82,11 @@ protected: void timerEvent(QTimerEvent *event); private slots: - void recognizerTriggered(QGestureRecognizer::Result); + void recognizerStateChanged(QGestureRecognizer::Result); private: + bool sendGestureEvent(QWidget *receiver, QGestureEvent *event); + QSet activeGestures; QMap maybeGestures; QSet recognizers; diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp new file mode 100644 index 0000000..9a00138 --- /dev/null +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesturerecognizer.h" +#include "qgesture.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); + +QGestureRecognizerPrivate::QGestureRecognizerPrivate() + : gestureType(Qt::UnknownGesture) +{ +} + +QGestureRecognizer::QGestureRecognizer(const QString &type) + : QObject(*new QGestureRecognizerPrivate, 0) +{ + d_func()->customGestureType = type; +} + +QString QGestureRecognizer::gestureType() const +{ + Q_D(const QGestureRecognizer); + if (d->gestureType == Qt::UnknownGesture) + return d->customGestureType; + return qt_getStandardGestureTypeName(d->gestureType); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h index ca8972b..c0d4f5e 100644 --- a/src/gui/kernel/qgesturerecognizer.h +++ b/src/gui/kernel/qgesturerecognizer.h @@ -42,7 +42,6 @@ #ifndef QGESTURERECOGNIZER_H #define QGESTURERECOGNIZER_H -#include "qgesture.h" #include "qevent.h" #include "qlist.h" #include "qset.h" @@ -59,18 +58,18 @@ QT_BEGIN_NAMESPACE Usually gesture recognizer implements state machine, storing its state internally in the recognizer object. The recognizer receives - input events through the QGestureRecognizer::recognize() virtual + input events through the QGestureRecognizer::filterEvent() virtual function and decides whether the parsed event should change the state of the recognizer - i.e. if the event starts or ends a gesture or if it isn't related to gesture at all. */ -/*! \fn Qt::GestureType gestureType() const +/*! \fn QString gestureType() const Returns the name of the gesture that is handled by the recognizer. */ -/*! \fn Result recognize(const QList &events) +/*! \fn Result filterEvent(const QEvent *event) This is a pure virtual function that need to be implemented in subclasses. @@ -79,7 +78,7 @@ QT_BEGIN_NAMESPACE sequence is a gesture or not. */ -/*! \fn QGesture* makeEvent() const +/*! \fn QGesture* getGesture() Creates a new gesture object that will be send to the widget. This function is called when the gesture recognizer returned a @@ -94,16 +93,20 @@ QT_BEGIN_NAMESPACE Resets the internal state of the gesture recognizer. */ -/*! \fn void triggered(QGestureRecognizer::Result result) +/*! \fn void stateChanged(QGestureRecognizer::Result result) The gesture recognizer might emit the signal when the gesture state changes asynchronously, i.e. without any event being received. */ +class QGesture; +class QGestureRecognizerPrivate; class Q_GUI_EXPORT QGestureRecognizer : public QObject { Q_OBJECT + Q_DECLARE_PRIVATE(QGestureRecognizer) + public: enum Result { @@ -113,21 +116,21 @@ public: MaybeGesture }; - inline QGestureRecognizer() { } - //### remove this ctor - inline QGestureRecognizer(const char *type) : type_(QLatin1String(type)) { } - inline QGestureRecognizer(const Qt::GestureType &type) : type_(type) { } - inline Qt::GestureType gestureType() const { return type_; } + explicit QGestureRecognizer(const QString &type); + + QString gestureType() const; - virtual Result recognize(const QList &events) = 0; - virtual QGesture* makeEvent() const = 0; + virtual Result filterEvent(const QEvent* event) = 0; + virtual QGesture* getGesture() = 0; virtual void reset() = 0; signals: - void triggered(QGestureRecognizer::Result result); + void stateChanged(QGestureRecognizer::Result result); -protected: - Qt::GestureType type_; +private: + friend class QDoubleTapGestureRecognizer; + friend class QTapAndHoldGestureRecognizer; + friend class QGestureRecognizerPan; }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturerecognizer_p.h b/src/gui/kernel/qgesturerecognizer_p.h new file mode 100644 index 0000000..4d80b99 --- /dev/null +++ b/src/gui/kernel/qgesturerecognizer_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURERECOGNIZER_P_H +#define QGESTURERECOGNIZER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QGestureRecognizerPrivate : public QObjectPrivate +{ +public: + QGestureRecognizerPrivate(); + +public: + Qt::GestureType gestureType; + QString customGestureType; +}; + +QT_END_NAMESPACE + +#endif // QGESTURERECOGNIZER_P_H diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index 504786a..718bdfd 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -41,6 +41,7 @@ #include "qgesturestandardrecognizers_p.h" #include "qgesture_p.h" +#include "qgesturerecognizer_p.h" // #define GESTURE_RECOGNIZER_DEBUG #ifndef GESTURE_RECOGNIZER_DEBUG @@ -50,205 +51,92 @@ #endif QT_BEGIN_NAMESPACE -/* -QGestureRecognizerMouseTwoButtons::QGestureRecognizerMouseTwoButtons() - : QGestureRecognizer(Qt::MouseTwoButtonClick) -{ - clear(); -} -QGestureRecognizer::Result QGestureRecognizerMouseTwoButtons::recognize(const QList &inputEvents) +QString qt_getStandardGestureTypeName(Qt::GestureType gestureType) { - // get all mouse events - QList events; - for(int i = 0; i < inputEvents.count(); ++i) { - QEvent *event = inputEvents.at(i); - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - events.push_back(static_cast(event)); - default: - break; - } - } - - QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; - for(int i = 0; i < events.count(); ++i) { - QMouseEvent *event = events.at(i); - if (event->type() == QEvent::MouseButtonPress) { - if (userEvents[3]) { - // something wrong, we already has a gesture. - clear(); - } else if (userEvents[2]) { - // 1d, 2d, 1u, and user press another button. not gesture. - DEBUG() << "1"; - result = QGestureRecognizer::NotGesture; - clear(); - break; - } else if (userEvents[1]) { - // 1d, 2d, and user pressed third button. not gesture. - DEBUG() << "2"; - result = QGestureRecognizer::NotGesture; - clear(); - break; - } else if (userEvents[0]) { - // second button press. - DEBUG() << "3"; - userEvents[1] = event; - result = QGestureRecognizer::MaybeGesture; - } else { - // first button press. - DEBUG() << "4"; - userEvents[0] = event; - result = QGestureRecognizer::MaybeGesture; - } - } else if (event->type() == QEvent::MouseButtonRelease) { - if (userEvents[3]) { - // something wrong, we already has a gesture. - clear(); - } else if (userEvents[2]) { - // 1d, 2d, 1u, and button release - DEBUG() << "5"; - if (userEvents[1]->button() != event->button()) { - // got weird buttonreleased event. doesn't look like gesture. - result = QGestureRecognizer::NotGesture; - clear(); - break; - } - // gesture! - userEvents[3] = event; - result = QGestureRecognizer::GestureFinished; - break; - } else if (userEvents[1]) { - // 1d, 2d, and button release - DEBUG() << "6"; - if (userEvents[0]->button() != event->button()) { - // user released the wrong button. not gesture. - result = QGestureRecognizer::NotGesture; - clear(); - break; - } - // user released the right button! looks like gesture - userEvents[2] = event; - result = QGestureRecognizer::MaybeGesture; - } else if (userEvents[0]) { - // 1d, and some button was released. not a gesture. - DEBUG() << "7"; - result = QGestureRecognizer::NotGesture; - clear(); - break; - } - } + switch (gestureType) { + case Qt::TapGesture: + return QLatin1String("__QTapGesture"); + case Qt::DoubleTapGesture: + return QLatin1String("__QDoubleTapGesture"); + case Qt::TrippleTapGesture: + return QLatin1String("__QTrippleTapGesture"); + case Qt::TapAndHoldGesture: + return QLatin1String("__QTapAndHoldGesture"); + case Qt::PanGesture: + return QLatin1String("__QPanGesture"); + case Qt::PinchGesture: + return QLatin1String("__QPinchGesture"); + case Qt::UnknownGesture: + break; } - return result; -} - -QGesture* QGestureRecognizerMouseTwoButtons::makeEvent() const -{ - if (!userEvents[0] || !userEvents[1] || !userEvents[2] || !userEvents[3]) - return 0; - QGesture::Direction direction = - (userEvents[0]->button() < userEvents[1]->button() ? - QGesture::Left : QGesture::Right); - QList points; - points.push_back(userEvents[0]->globalPos()); - points.push_back(userEvents[1]->globalPos()); - QGesture *gesture = - new QGesture(Qt::MouseTwoButtonClick, points.at(0), points.at(1), points.at(1), - direction, - QRect(points.at(0), points.at(1)), - points.at(0)); - return gesture; + qFatal("QGestureRecognizer::gestureType: got an unhandled gesture type."); + return QLatin1String("__unknown_gesture"); } -void QGestureRecognizerMouseTwoButtons::reset() -{ - clear(); -} - -void QGestureRecognizerMouseTwoButtons::clear() -{ - userEvents[0] = userEvents[1] = userEvents[2] = userEvents[3] = 0; -} -*/ - // // QGestureRecognizerPan // QGestureRecognizerPan::QGestureRecognizerPan() - : QGestureRecognizer(Qt::Pan), mousePressed(false), gestureFinished(false), + : QGestureRecognizer(QString()), mousePressed(false), gestureFinished(false), lastDirection(Direction::None), currentDirection(Direction::None) { + Q_D(QGestureRecognizer); + d->gestureType = Qt::PanGesture; } -QGestureRecognizer::Result QGestureRecognizerPan::recognize(const QList &inputEvents) +QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *event) { - // get all mouse events - QList events; - for(int i = 0; i < inputEvents.count(); ++i) { - QEvent *event = inputEvents.at(i); - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - events.push_back(static_cast(event)); - default: - break; - } - } - - QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; - for(int i = 0; i < events.count(); ++i) { - QMouseEvent *event = events.at(i); - if (event->type() == QEvent::MouseButtonPress) { - if (currentDirection != Direction::None) { - DEBUG() << "Pan: MouseButtonPress: fail. another press during pan"; - result = QGestureRecognizer::NotGesture; - reset(); - break; - } - DEBUG() << "Pan: MouseButtonPress: maybe gesture started"; - result = QGestureRecognizer::MaybeGesture; - mousePressed = true; - pressedPos = lastPos = currentPos = event->pos(); - } else if (event->type() == QEvent::MouseButtonRelease) { - if (mousePressed && currentDirection != Direction::None) { - DEBUG() << "Pan: MouseButtonRelease: pan detected"; - result = QGestureRecognizer::GestureFinished; - gestureFinished = true; - currentPos = event->pos(); - internalReset(); - break; - } - DEBUG() << "Pan: MouseButtonRelease: some weird release detected, ignoring"; - result = QGestureRecognizer::NotGesture; + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *ev = static_cast(event); + if (currentDirection != Direction::None) { + DEBUG() << "Pan: MouseButtonPress: fail. another press during pan"; reset(); - break; - } else if (event->type() == QEvent::MouseMove) { - if (!mousePressed) - continue; - lastPos = currentPos; - currentPos = event->pos(); - Direction::DirectionType direction = - simpleRecognizer.addPosition(event->pos()).direction; - DEBUG() << "Pan: MouseMove: simplerecognizer result = " << direction; - if (currentDirection == Direction::None) { - if (direction == Direction::None) - result = QGestureRecognizer::MaybeGesture; - else - result = QGestureRecognizer::GestureStarted; - } else { + return QGestureRecognizer::NotGesture; + } + DEBUG() << "Pan: MouseButtonPress: maybe gesture started"; + mousePressed = true; + pressedPos = lastPos = currentPos = ev->pos(); + return QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseButtonRelease) { + if (mousePressed && currentDirection != Direction::None) { + DEBUG() << "Pan: MouseButtonRelease: pan detected"; + gestureFinished = true; + const QMouseEvent *ev = static_cast(event); + currentPos = ev->pos(); + internalReset(); + return QGestureRecognizer::GestureFinished; + } + DEBUG() << "Pan: MouseButtonRelease: some weird release detected, ignoring"; + reset(); + return QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseMove) { + if (!mousePressed) + return QGestureRecognizer::NotGesture; + const QMouseEvent *ev = static_cast(event); + lastPos = currentPos; + currentPos = ev->pos(); + Direction::DirectionType direction = + simpleRecognizer.addPosition(ev->pos()).direction; + DEBUG() << "Pan: MouseMove: simplerecognizer result = " << direction; + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + if (currentDirection == Direction::None) { + if (direction == Direction::None) + result = QGestureRecognizer::MaybeGesture; + else result = QGestureRecognizer::GestureStarted; - } - if (direction != Direction::None) { - if (currentDirection != direction) - lastDirection = currentDirection; - currentDirection = direction; - } + } else { + result = QGestureRecognizer::GestureStarted; } + if (direction != Direction::None) { + if (currentDirection != direction) + lastDirection = currentDirection; + currentDirection = direction; + } + return result; } - return result; + return QGestureRecognizer::NotGesture; } static inline QPannableGesture::DirectionType convertPanningDirection(const Direction::DirectionType direction) @@ -268,17 +156,18 @@ static inline QPannableGesture::DirectionType convertPanningDirection(const Dire return QPannableGesture::None; } -QGesture* QGestureRecognizerPan::makeEvent() const +QGesture* QGestureRecognizerPan::getGesture() { QPannableGesture::DirectionType dir = convertPanningDirection(currentDirection); QPannableGesture::DirectionType lastDir = convertPanningDirection(lastDirection); if (dir == QPannableGesture::None) return 0; QPannableGesture *g = - new QPannableGesture(Qt::Pan, pressedPos, lastPos, currentPos, + new QPannableGesture(this, + pressedPos, lastPos, currentPos, QRect(), pressedPos, QDateTime(), 0, gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); - QPannableGesturePrivate *d = (QPannableGesturePrivate*)g->d; + QPannableGesturePrivate *d = g->d_func(); d->lastDirection = lastDir; d->direction = dir; @@ -307,52 +196,37 @@ void QGestureRecognizerPan::internalReset() // QDoubleTapGestureRecognizer // QDoubleTapGestureRecognizer::QDoubleTapGestureRecognizer() - : QGestureRecognizer(Qt::DoubleTap) + : QGestureRecognizer(QString()) { + Q_D(QGestureRecognizer); + d->gestureType = Qt::DoubleTapGesture; } -QGestureRecognizer::Result QDoubleTapGestureRecognizer::recognize(const QList &inputEvents) +QGestureRecognizer::Result QDoubleTapGestureRecognizer::filterEvent(const QEvent *event) { - // get all mouse events - QList events; - for(int i = 0; i < inputEvents.count(); ++i) { - QEvent *event = inputEvents.at(i); - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - events.push_back(static_cast(event)); - default: - break; - } - } - - QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; - for(int i = 0; i < events.count(); ++i) { - QMouseEvent *event = events.at(i); - if (event->type() == QEvent::MouseButtonPress) { - if (pressedPosition.isNull()) { - result = QGestureRecognizer::MaybeGesture; - pressedPosition = event->pos(); - } else if ((pressedPosition - event->pos()).manhattanLength() < 10) { - result = QGestureRecognizer::GestureFinished; - } - } else if (event->type() == QEvent::MouseButtonRelease) { - if (!pressedPosition.isNull() && (pressedPosition - event->pos()).manhattanLength() < 10) - result = QGestureRecognizer::MaybeGesture; - } else if (event->type() == QEvent::MouseButtonDblClick) { - result = QGestureRecognizer::GestureFinished; - pressedPosition = event->pos(); - break; + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *ev = static_cast(event); + if (pressedPosition.isNull()) { + pressedPosition = ev->pos(); + return QGestureRecognizer::MaybeGesture; + } else if ((pressedPosition - ev->pos()).manhattanLength() < 10) { + return QGestureRecognizer::GestureFinished; } + } else if (event->type() == QEvent::MouseButtonRelease) { + const QMouseEvent *ev = static_cast(event); + if (!pressedPosition.isNull() && (pressedPosition - ev->pos()).manhattanLength() < 10) + return QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseButtonDblClick) { + const QMouseEvent *ev = static_cast(event); + pressedPosition = ev->pos(); + return QGestureRecognizer::GestureFinished; } - return result; + return QGestureRecognizer::NotGesture; } -QGesture* QDoubleTapGestureRecognizer::makeEvent() const +QGesture* QDoubleTapGestureRecognizer::getGesture() { - return new QGesture(Qt::DoubleTap, + return new QGesture(this, qt_getStandardGestureTypeName(Qt::DoubleTapGesture), pressedPosition, pressedPosition, pressedPosition, QRect(), pressedPosition, QDateTime(), 0, Qt::GestureFinished); } @@ -363,195 +237,66 @@ void QDoubleTapGestureRecognizer::reset() } // -// QLongTapGestureRecognizer +// QTapAndHoldGestureRecognizer // -const int QLongTapGestureRecognizer::iterationCount = 40; -const int QLongTapGestureRecognizer::iterationTimeout = 50; +const int QTapAndHoldGestureRecognizer::iterationCount = 40; +const int QTapAndHoldGestureRecognizer::iterationTimeout = 50; -QLongTapGestureRecognizer::QLongTapGestureRecognizer() - : QGestureRecognizer(Qt::LongTap), iteration(0) +QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer() + : QGestureRecognizer(QString()), iteration(0) { + Q_D(QGestureRecognizer); + d->gestureType = Qt::TapAndHoldGesture; } -QGestureRecognizer::Result QLongTapGestureRecognizer::recognize(const QList &inputEvents) +QGestureRecognizer::Result QTapAndHoldGestureRecognizer::filterEvent(const QEvent *event) { - // get all mouse events - QList events; - for(int i = 0; i < inputEvents.count(); ++i) { - QEvent *event = inputEvents.at(i); - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - events.push_back(static_cast(event)); - default: - break; - } - } - - QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; - for(int i = 0; i < events.count(); ++i) { - QMouseEvent *event = events.at(i); - if (event->type() == QEvent::MouseButtonPress) { - if (timer.isActive()) - timer.stop(); - timer.start(QLongTapGestureRecognizer::iterationTimeout, this); - pressedPosition = event->pos(); - result = QGestureRecognizer::MaybeGesture; - } else if (event->type() == QEvent::MouseMove) { - if ((pressedPosition - event->pos()).manhattanLength() < 15) - result = QGestureRecognizer::GestureStarted; - else - result = QGestureRecognizer::NotGesture; - } else if (event->type() == QEvent::MouseButtonRelease) { - result = QGestureRecognizer::NotGesture; + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *ev = static_cast(event); + if (timer.isActive()) timer.stop(); - break; - } + timer.start(QTapAndHoldGestureRecognizer::iterationTimeout, this); + pressedPosition = ev->pos(); + return QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseMove) { + const QMouseEvent *ev = static_cast(event); + if ((pressedPosition - ev->pos()).manhattanLength() < 15) + return QGestureRecognizer::GestureStarted; + else + return QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseButtonRelease) { + timer.stop(); + return QGestureRecognizer::NotGesture; } - return result; + return QGestureRecognizer::NotGesture; } -void QLongTapGestureRecognizer::timerEvent(QTimerEvent *event) +void QTapAndHoldGestureRecognizer::timerEvent(QTimerEvent *event) { if (event->timerId() != timer.timerId()) return; - if (++iteration == QLongTapGestureRecognizer::iterationCount) { - emit triggered(QGestureRecognizer::GestureFinished); + if (++iteration == QTapAndHoldGestureRecognizer::iterationCount) { + emit stateChanged(QGestureRecognizer::GestureFinished); timer.stop(); } else { - emit triggered(QGestureRecognizer::GestureStarted); + emit stateChanged(QGestureRecognizer::GestureStarted); } } -QGesture* QLongTapGestureRecognizer::makeEvent() const +QGesture* QTapAndHoldGestureRecognizer::getGesture() { - return new QGesture(Qt::LongTap, + return new QGesture(this, qt_getStandardGestureTypeName(Qt::TapAndHoldGesture), pressedPosition, pressedPosition, pressedPosition, QRect(), pressedPosition, QDateTime(), 0, - iteration >= QLongTapGestureRecognizer::iterationCount ? + iteration >= QTapAndHoldGestureRecognizer::iterationCount ? Qt::GestureFinished : Qt::GestureStarted); } -void QLongTapGestureRecognizer::reset() +void QTapAndHoldGestureRecognizer::reset() { pressedPosition = QPoint(); timer.stop(); iteration = 0; } -// -// QMultiTouchGestureRecognizer -// - -/* -QMultiTouchGestureRecognizer::QMultiTouchGestureRecognizer() - : QGestureRecognizer(Qt::Pinch) -{ -} - -QGestureRecognizer::Result QMultiTouchGestureRecognizer::recognize(const QList &inputEvents) -{ - QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; - for(int i = 0; i < inputEvents.count(); ++i) { - QEvent *inputEvent = inputEvents.at(i); - if (inputEvent->type() != QEvent::Pointer) { - if (touches.size() == 2) - result = QGestureRecognizer::GestureStarted; - else if (touches.size() == 1) - result = QGestureRecognizer::MaybeGesture; - continue; - } - QPointerEvent *event = static_cast(inputEvent); - Q_ASSERT(event->pointerCount() > 0); - for (int idx = 0; idx < event->pointerCount(); ++idx) { - switch (event->state(idx)) { - case QPointerEvent::Begin: - qDebug() << "Begin" << idx; - if (touches.size() == 2) { - // too many touches - qDebug() << "too many touches"; - result = QGestureRecognizer::MaybeGesture; - continue; - } - touches[event->pointerId(idx)].startPosition = event->pos(idx); - if (touches.size() == 1) { - result = QGestureRecognizer::MaybeGesture; - } else if (touches.size() == 2) { - result = QGestureRecognizer::GestureStarted; - } else { - result = QGestureRecognizer::NotGesture; - } - break; - - case QPointerEvent::Move: - qDebug() << "Move" << idx; - touches[event->pointerId(idx)].lastPosition = touches[event->pointerId(idx)].currentPosition; - touches[event->pointerId(idx)].currentPosition = event->pos(idx); - if (touches.size() == 1) - result = QGestureRecognizer::MaybeGesture; - else if (touches.size() == 2) - result = QGestureRecognizer::GestureStarted; - else - result = QGestureRecognizer::NotGesture; - break; - - case QPointerEvent::End: - qDebug() << "End" << idx; - touches.remove(event->pointerId(idx)); - if (touches.size() == 1) { - result = QGestureRecognizer::MaybeGesture; - } else if (touches.size() == 0) { - result = QGestureRecognizer::NotGesture; - } else { - qDebug() << "unexpected touch end event"; - return QGestureRecognizer::NotGesture; - } - break; - - case QPointerEvent::Stationary: - qDebug() << "Stationary" << idx; - if (touches.size() == 2) - result = QGestureRecognizer::GestureStarted; - else if (touches.size() == 1) - result = QGestureRecognizer::MaybeGesture; - break; - - default: - break; - } - } - } - return result; -} - -QGesture* QMultiTouchGestureRecognizer::makeEvent() const -{ - if (touches.size() != 2) - return 0; - - QGesture *g = new QGesture(Qt::Pinch); - QGesture::Data data; - - TapMap::const_iterator it = touches.begin(); - data.startPos = it->startPosition.toPoint(); - data.lastPos = it->lastPosition.toPoint(); - data.currentPos = it->currentPosition.toPoint(); - g->datas.push_back(data); - - ++it; - data.startPos = it->startPosition.toPoint(); - data.lastPos = it->lastPosition.toPoint(); - data.currentPos = it->currentPosition.toPoint(); - g->datas.push_back(data); - - return g; -} - -void QMultiTouchGestureRecognizer::reset() -{ - touches.clear(); -} -*/ - QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h index 567cf65..75a206a 100644 --- a/src/gui/kernel/qgesturestandardrecognizers_p.h +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -62,34 +62,14 @@ QT_BEGIN_NAMESPACE -/* -class QGestureRecognizerMouseTwoButtons : public QGestureRecognizer -{ - Q_OBJECT -public: - QGestureRecognizerMouseTwoButtons(); - QGestureRecognizer::Result recognize(const QList &inputEvents); - - QGesture* makeEvent() const; - void reset(); - -private: - void clear(); - - // find the last two button click events - QMouseEvent* userEvents[4]; -}; -*/ - class QGestureRecognizerPan : public QGestureRecognizer { Q_OBJECT public: QGestureRecognizerPan(); - QGestureRecognizer::Result recognize(const QList &inputEvents); - QGesture* makeEvent() const; - + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture(); void reset(); private: @@ -112,22 +92,22 @@ class QDoubleTapGestureRecognizer : public QGestureRecognizer public: QDoubleTapGestureRecognizer(); - QGestureRecognizer::Result recognize(const QList &inputEvents); - QGesture* makeEvent() const; + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture(); void reset(); private: QPoint pressedPosition; }; -class QLongTapGestureRecognizer : public QGestureRecognizer +class QTapAndHoldGestureRecognizer : public QGestureRecognizer { Q_OBJECT public: - QLongTapGestureRecognizer(); + QTapAndHoldGestureRecognizer(); - QGestureRecognizer::Result recognize(const QList &inputEvents); - QGesture* makeEvent() const; + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture(); void reset(); protected: @@ -141,30 +121,6 @@ private: static const int iterationTimeout; }; -/* -class QMultiTouchGestureRecognizer : public QGestureRecognizer -{ - Q_OBJECT -public: - QMultiTouchGestureRecognizer(); - - QMap maybeGestureCompletion(); - QGestureRecognizer::Result recognize(const QList &inputEvents); - QGesture* makeEvent() const; - void reset(); - -private: - struct Tap { - //### should I use QPointF everywhere internally ?? - QPointF startPosition; - QPointF lastPosition; - QPointF currentPosition; - }; - typedef QMap TapMap; - TapMap touches; -}; -*/ - QT_END_NAMESPACE #endif // QGESTURESTANDARDRECOGNIZERS_P_H diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index aea0003..2f794f4 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -127,6 +127,8 @@ Q_GUI_EXPORT void qt_x11_set_global_double_buffer(bool enable) } #endif +QString qt_getStandardGestureTypeName(Qt::GestureType); + static inline bool qRectIntersects(const QRect &r1, const QRect &r2) { return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) && @@ -11026,60 +11028,56 @@ QWindowSurface *QWidget::windowSurface() const } /*! - \fn void QWidget::grabGesture(const Qt::GestureType &gesture) - Subscribes the widget to the specified \a gesture type. + + Returns the id of the gesture. */ -void QWidget::grabGesture(const Qt::GestureType &gesture) +int QWidget::grabGesture(const QString &gesture) { Q_D(QWidget); - if (d->gestures.contains(gesture)) - return; - d->gestures << gesture; - ++qApp->d_func()->grabbedGestures[gesture]; + return d->grabGesture(qHash(gesture)); +} + +int QWidgetPrivate::grabGesture(int id) +{ + QSet::iterator it = gestures.find(id); + if (it != gestures.end()) + return *it; + gestures << id; + ++qApp->d_func()->grabbedGestures[id]; + return id; } /*! - \fn void QWidget::grabGestures(const QSet &gestures) + Subscribes the widget to the specified \a gesture type. - Subscribes the widget to the specified list of \a gestures. + Returns the id of the gesture. */ -void QWidget::grabGestures(const QSet &gestures) + +int QWidget::grabGesture(Qt::GestureType gesture) { - Q_D(QWidget); - foreach(const Qt::GestureType &gesture, gestures) { - if (!d->gestures.contains(gesture)) - ++qApp->d_func()->grabbedGestures[gesture]; - } - d->gestures.unite(gestures); + return grabGesture(qt_getStandardGestureTypeName(gesture)); } /*! - \fn void QWidget::releaseGesture(const Qt::GestureType &gesture) - - Unsubscribes the widget from the specified \a gesture type. + Unsubscribes the widget from a gesture, which is specified by its + \a id. */ -void QWidget::releaseGesture(const Qt::GestureType &gesture) +void QWidget::releaseGesture(int gestureId) { Q_D(QWidget); - QMap::iterator it = - qApp->d_func()->grabbedGestures.find(gesture); - if (it != qApp->d_func()->grabbedGestures.end() && - d->gestures.contains(gesture)) - ++it.value(); - d->gestures.remove(gesture); + QSet::iterator it = d->gestures.find(gestureId); + if (it != d->gestures.end()) { + Q_ASSERT(qApp->d_func()->grabbedGestures[gestureId] > 0); + --qApp->d_func()->grabbedGestures[gestureId]; + d->gestures.erase(it); + } } -/*! - \fn QSet QWidget::gestures() const - - Returns a list of gestures that the widget is subscribed to. -*/ -QSet QWidget::gestures() const +void setGestureEnabled(int gestureId, bool enable) { - Q_D(const QWidget); - return d->gestures; + //### } void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index e36b966..a76c50a 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -613,10 +613,10 @@ public: void setWindowSurface(QWindowSurface *surface); QWindowSurface *windowSurface() const; - void grabGesture(const Qt::GestureType &gesture); - void grabGestures(const QSet &gestures); - void releaseGesture(const Qt::GestureType &gesture); - QSet gestures() const; + int grabGesture(const QString &gesture); + int grabGesture(Qt::GestureType gesture); + void releaseGesture(int gestureId); + void setGestureEnabled(int gestureId, bool enable); Q_SIGNALS: void customContextMenuRequested(const QPoint &pos); @@ -755,6 +755,7 @@ private: friend bool isWidgetOpaque(const QWidget *); friend class QGLWidgetPrivate; #endif + friend class QGestureManager; #ifdef Q_WS_X11 friend void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp); friend void qt_net_remove_user_time(QWidget *tlw); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 178f7ba..abd7b8a 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -593,7 +593,8 @@ public: uint isGLWidget : 1; #endif - QSet gestures; + QSet gestures; + int grabGesture(int gestureId); #if defined(Q_WS_X11) || defined (Q_WS_WIN) || defined(Q_WS_MAC) #ifdef Q_WS_MAC -- cgit v0.12 From 32164d09d8647995e6177efec967ace799b7c528 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 13 Mar 2009 19:15:35 +0100 Subject: De-inlined functions in gesture events. --- src/gui/graphicsview/qgraphicssceneevent.cpp | 69 +++++++++++++++++++++------- src/gui/graphicsview/qgraphicssceneevent.h | 22 ++++----- src/gui/kernel/qevent.cpp | 37 +++++++++++++++ src/gui/kernel/qevent.h | 22 ++++----- 4 files changed, 110 insertions(+), 40 deletions(-) diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 4d9e7bb..4f24a74 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -304,6 +304,8 @@ QT_BEGIN_NAMESPACE +QString qt_getStandardGestureTypeName(Qt::GestureType type); + class QGraphicsSceneEventPrivate { public: @@ -1725,45 +1727,80 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) gestures with \l{QGraphicsItem::}{grabGesture()}. */ -/*! \fn bool QGraphicsSceneGestureEvent::contains(const Qt::GestureType &type) const +QGraphicsSceneGestureEvent::QGraphicsSceneGestureEvent() + : QGraphicsSceneEvent(QEvent::GraphicsSceneGesture) +{ +} + +QGraphicsSceneGestureEvent::~QGraphicsSceneGestureEvent() +{ +} + +/*! Checks if the gesture event contains gesture of specific \a type. */ +bool QGraphicsSceneGestureEvent::contains(const QString &type) const +{ + return gesture(type) != 0; +} -/*! \fn QList QGraphicsSceneGestureEvent::gestureTypes() const +/*! + Checks if the gesture event contains gesture of specific \a type. +*/ +bool QGraphicsSceneGestureEvent::contains(Qt::GestureType type) const +{ + return contains(qt_getStandardGestureTypeName(type)); +} +/*! Returns a list of gesture names that the event contains. */ +QList QGraphicsSceneGestureEvent::gestureTypes() const +{ + return m_gestures.keys(); +} -/*! \fn const QGesture* QGraphicsSceneGestureEvent::gesture(const Qt::GestureType &type) const - +/*! Returns extended information about a gesture of specific \a type. */ +const QGesture* QGraphicsSceneGestureEvent::gesture(const QString &type) const +{ + return m_gestures.value(type, QSharedPointer()).data(); +} -/*! \fn QList > QGraphicsSceneGestureEvent::gestures() const +/*! + Returns extended information about a gesture of specific \a type. +*/ +const QGesture* QGraphicsSceneGestureEvent::gesture(Qt::GestureType type) const +{ + return gesture(qt_getStandardGestureTypeName(type)); +} +/*! Returns extended information about all triggered gestures. */ +QList > QGraphicsSceneGestureEvent::gestures() const +{ + return m_gestures.values(); +} -/*! \fn QSet QGraphicsSceneGestureEvent::cancelledGestures() const - +/*! Returns a set of gesture names that used to be executed, but got cancelled (i.e. they were not finished properly). */ +QSet QGraphicsSceneGestureEvent::cancelledGestures() const +{ + return m_cancelledGestures; +} -/*! \fn void QGraphicsSceneGestureEvent::setCancelledGestures(const QSet &) - +/*! Returns a set of gesture names that used to be executed, but got cancelled (i.e. they were not finished properly). */ - -QGraphicsSceneGestureEvent::QGraphicsSceneGestureEvent() - : QGraphicsSceneEvent(QEvent::GraphicsSceneGesture) -{ -} - -QGraphicsSceneGestureEvent::~QGraphicsSceneGestureEvent() +void QGraphicsSceneGestureEvent::setCancelledGestures(const QSet &cancelledGestures) { + m_cancelledGestures = cancelledGestures; } /*! diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index 4d88cda..d1a301e 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -317,20 +317,18 @@ public: QGraphicsSceneGestureEvent(); ~QGraphicsSceneGestureEvent(); - inline bool contains(const QString &type) const - { return gesture(type) != 0; } - inline QList gestureTypes() const - { return m_gestures.keys(); } - inline const QGesture* gesture(const QString &type) const - { return m_gestures.value(type, QSharedPointer()).data(); } - inline QList > gestures() const - { return m_gestures.values(); } + bool contains(const QString &type) const; + bool contains(Qt::GestureType type) const; + + QList gestureTypes() const; + + const QGesture* gesture(Qt::GestureType type) const; + const QGesture* gesture(const QString &type) const; + QList > gestures() const; void setGestures(const QList > &gestures); - inline QSet cancelledGestures() const - { return m_cancelledGestures; } - void setCancelledGestures(const QSet &cancelledGestures) - { m_cancelledGestures = cancelledGestures; } + QSet cancelledGestures() const; + void setCancelledGestures(const QSet &cancelledGestures); QPointF mapToScene(const QPoint &point) const; QPolygonF mapToScene(const QRect &rect) const; diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 225d8b1..5761939 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -52,6 +52,8 @@ QT_BEGIN_NAMESPACE +QString qt_getStandardGestureTypeName(Qt::GestureType type); + /*! \class QInputEvent \ingroup events @@ -3572,6 +3574,41 @@ QGestureEvent::~QGestureEvent() { } +bool QGestureEvent::contains(Qt::GestureType type) const +{ + return contains(qt_getStandardGestureTypeName(type)); +} + +bool QGestureEvent::contains(const QString &type) const +{ + return gesture(type) != 0; +} + +QList QGestureEvent::gestureTypes() const +{ + return m_gestures.keys(); +} + +const QGesture* QGestureEvent::gesture(Qt::GestureType type) const +{ + return gesture(qt_getStandardGestureTypeName(type)); +} + +const QGesture* QGestureEvent::gesture(const QString &type) const +{ + return m_gestures.value(type, QSharedPointer()).data(); +} + +QList > QGestureEvent::gestures() const +{ + return m_gestures.values(); +} + +QSet QGestureEvent::cancelledGestures() const +{ + return m_cancelledGestures; +} + /*! \class QTouchEvent \brief The QTouchEvent class contains parameters that describe a touch event . diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index db3d03f..48e420a 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -723,18 +723,16 @@ public: QGestureEvent(const QGestureEvent &gestures, const QPoint &offset); ~QGestureEvent(); - inline bool contains(const QString &type) const - { return gesture(type) != 0; } - inline QList gestureTypes() const - { return m_gestures.keys(); } - - inline const QGesture* gesture(const QString &type) const - { return m_gestures.value(type, QSharedPointer()).data(); } - inline QList > gestures() const - { return m_gestures.values(); } - - inline QSet cancelledGestures() const - { return m_cancelledGestures; } + bool contains(Qt::GestureType type) const; + bool contains(const QString &type) const; + + QList gestureTypes() const; + + const QGesture* gesture(Qt::GestureType type) const; + const QGesture* gesture(const QString &type) const; + QList > gestures() const; + + QSet cancelledGestures() const; protected: QHash > m_gestures; -- cgit v0.12 From 04bb61d39f56fb7a980bda26241b86a8c41724de Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 13 Mar 2009 19:29:05 +0100 Subject: Gesture examples compile now. --- .../collidingmice/gesturerecognizerlinjazax.cpp | 114 +++++++++------------ .../collidingmice/gesturerecognizerlinjazax.h | 4 +- examples/gestures/collidingmice/linjazaxgesture.h | 17 +-- examples/gestures/collidingmice/main.cpp | 4 +- examples/gestures/graphicsview/main.cpp | 10 +- examples/gestures/imageviewer/imagewidget.cpp | 20 ++-- 6 files changed, 77 insertions(+), 92 deletions(-) diff --git a/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp b/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp index 3f90588..41bb113 100644 --- a/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp +++ b/examples/gestures/collidingmice/gesturerecognizerlinjazax.cpp @@ -60,83 +60,67 @@ void DirectionSimpleRecognizer::reset() /////////////////////////////////////////////////////////////////////////// GestureRecognizerLinjaZax::GestureRecognizerLinjaZax() - : QGestureRecognizer("LinjaZax"), mousePressed(false), gestureFinished(false), + : QGestureRecognizer(QLatin1String("LinjaZax")), mousePressed(false), gestureFinished(false), zoomState(LinjaZaxGesture::NoZoom) { } -QGestureRecognizer::Result GestureRecognizerLinjaZax::recognize(const QList &inputEvents) +QGestureRecognizer::Result GestureRecognizerLinjaZax::filterEvent(const QEvent *event) { - // get all mouse events - QList events; - for(int i = 0; i < inputEvents.count(); ++i) { - QEvent *event = inputEvents.at(i); - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - events.push_back(static_cast(event)); - default: - break; - } - } - if (zoomState != LinjaZaxGesture::NoZoom && !lastDirections.isEmpty()) { lastDirections = lastDirections.right(1); zoomState = LinjaZaxGesture::NoZoom; } - QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; - for(int i = 0; i < events.count(); ++i) { - QMouseEvent *event = events.at(i); - if (event->type() == QEvent::MouseButtonPress) { - if (!currentDirection.isEmpty()) { - result = QGestureRecognizer::NotGesture; - reset(); - break; - } - result = QGestureRecognizer::MaybeGesture; - mousePressed = true; - pressedPos = lastPos = currentPos = event->pos(); - } else if (event->type() == QEvent::MouseButtonRelease) { - if (mousePressed && !currentDirection.isEmpty()) { - result = QGestureRecognizer::GestureFinished; - gestureFinished = true; - currentPos = event->pos(); - internalReset(); - break; - } - result = QGestureRecognizer::NotGesture; + if (event->type() == QEvent::MouseButtonPress) { + if (!currentDirection.isEmpty()) { reset(); - break; - } else if (event->type() == QEvent::MouseMove) { - if (!mousePressed) - continue; - lastPos = currentPos; - currentPos = event->pos(); - QString direction = - simpleRecognizer.addPosition(event->pos()).direction; - if (currentDirection.isEmpty()) { - if (direction.isEmpty()) - result = QGestureRecognizer::MaybeGesture; - else - result = QGestureRecognizer::GestureStarted; - } else { + return QGestureRecognizer::NotGesture; + } + mousePressed = true; + const QMouseEvent *ev = static_cast(event); + pressedPos = lastPos = currentPos = ev->pos(); + return QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseButtonRelease) { + const QMouseEvent *ev = static_cast(event); + if (mousePressed && !currentDirection.isEmpty()) { + gestureFinished = true; + currentPos = ev->pos(); + internalReset(); + return QGestureRecognizer::GestureFinished; + } + reset(); + return QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseMove) { + if (!mousePressed) + return QGestureRecognizer::NotGesture; + lastPos = currentPos; + const QMouseEvent *ev = static_cast(event); + currentPos = ev->pos(); + QString direction = + simpleRecognizer.addPosition(ev->pos()).direction; + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + if (currentDirection.isEmpty()) { + if (direction.isEmpty()) + result = QGestureRecognizer::MaybeGesture; + else result = QGestureRecognizer::GestureStarted; - } - if (!direction.isEmpty()) { - lastDirections.append(direction); - currentDirection = direction; - if (lastDirections.length() > 5) - lastDirections.remove(0, 1); - if (lastDirections.contains("248") || lastDirections.contains("2448")) - zoomState = LinjaZaxGesture::ZoomingIn; - else if (lastDirections.contains("268") || lastDirections.contains("2668")) - zoomState = LinjaZaxGesture::ZoomingOut; - } + } else { + result = QGestureRecognizer::GestureStarted; + } + if (!direction.isEmpty()) { + lastDirections.append(direction); + currentDirection = direction; + if (lastDirections.length() > 5) + lastDirections.remove(0, 1); + if (lastDirections.contains("248") || lastDirections.contains("2448")) + zoomState = LinjaZaxGesture::ZoomingIn; + else if (lastDirections.contains("268") || lastDirections.contains("2668")) + zoomState = LinjaZaxGesture::ZoomingOut; } + return result; } - return result; + return QGestureRecognizer::NotGesture; } static inline LinjaZaxGesture::DirectionType convertPanningDirection(const QString &direction) @@ -154,14 +138,14 @@ static inline LinjaZaxGesture::DirectionType convertPanningDirection(const QStri return LinjaZaxGesture::None; } -QGesture* GestureRecognizerLinjaZax::makeEvent() const +QGesture* GestureRecognizerLinjaZax::getGesture() { LinjaZaxGesture::DirectionType dir = convertPanningDirection(currentDirection); LinjaZaxGesture::DirectionType lastDir = convertPanningDirection(lastDirections.right(1)); if (dir == LinjaZaxGesture::None) return 0; LinjaZaxGesture *g = - new LinjaZaxGesture("LinjaZax", pressedPos, lastPos, currentPos, + new LinjaZaxGesture(this, pressedPos, lastPos, currentPos, QRect(), pressedPos, QDateTime(), 0, gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); g->lastDirection_ = lastDir; diff --git a/examples/gestures/collidingmice/gesturerecognizerlinjazax.h b/examples/gestures/collidingmice/gesturerecognizerlinjazax.h index 6579059..07172f1 100644 --- a/examples/gestures/collidingmice/gesturerecognizerlinjazax.h +++ b/examples/gestures/collidingmice/gesturerecognizerlinjazax.h @@ -43,8 +43,8 @@ class GestureRecognizerLinjaZax : public QGestureRecognizer public: GestureRecognizerLinjaZax(); - QGestureRecognizer::Result recognize(const QList &inputEvents); - QGesture* makeEvent() const; + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture(); void reset(); diff --git a/examples/gestures/collidingmice/linjazaxgesture.h b/examples/gestures/collidingmice/linjazaxgesture.h index 9601675..8a7cb23 100644 --- a/examples/gestures/collidingmice/linjazaxgesture.h +++ b/examples/gestures/collidingmice/linjazaxgesture.h @@ -31,13 +31,16 @@ public: }; public: - explicit LinjaZaxGesture(const Qt::GestureType &type, Qt::GestureState state = Qt::GestureStarted) - : QGesture(type, state), lastDirection_(None), direction_(None), zoomState_(NoZoom) { } - LinjaZaxGesture(const Qt::GestureType &type, const QPoint &startPos, - const QPoint &lastPos, const QPoint &pos, const QRect &rect, - const QPoint &hotSpot, const QDateTime &startTime, - uint duration, Qt::GestureState state) - : QGesture(type, startPos, lastPos, pos, rect, hotSpot, startTime, duration, state) { } + explicit LinjaZaxGesture(QObject *parent, + Qt::GestureState state = Qt::GestureStarted) + : QGesture(parent, QLatin1String("LinjaZax"), state), lastDirection_(None), + direction_(None), zoomState_(NoZoom) { } + LinjaZaxGesture(QObject *parent, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state) + : QGesture(parent, QLatin1String("LinjaZax"), startPos, lastPos, + pos, rect, hotSpot, startTime, duration, state) { } ~LinjaZaxGesture() { } DirectionType lastDirection() const diff --git a/examples/gestures/collidingmice/main.cpp b/examples/gestures/collidingmice/main.cpp index d6dbdb0..e8ef13f 100644 --- a/examples/gestures/collidingmice/main.cpp +++ b/examples/gestures/collidingmice/main.cpp @@ -62,7 +62,7 @@ public: PannableGraphicsView(QGraphicsScene *scene, QWidget *parent = 0) : QGraphicsView(scene, parent) { - grabGesture("LinjaZax"); + grabGesture(QLatin1String("LinjaZax")); #ifdef ZOOMING_ANIMATION timeline = new QTimeLine(700, this); timeline->setFrameRange(0, AnimationSteps); @@ -74,7 +74,7 @@ protected: { if (event->type() == QEvent::Gesture) { QGestureEvent *ge = static_cast(event); - const LinjaZaxGesture *g = dynamic_cast(ge->gesture("LinjaZax")); + const LinjaZaxGesture *g = static_cast(ge->gesture("LinjaZax")); if (g) { switch (g->zoomState()) { case LinjaZaxGesture::ZoomingIn: diff --git a/examples/gestures/graphicsview/main.cpp b/examples/gestures/graphicsview/main.cpp index 1b325ee..1a40805 100644 --- a/examples/gestures/graphicsview/main.cpp +++ b/examples/gestures/graphicsview/main.cpp @@ -46,14 +46,14 @@ class PannableGraphicsView : public QGraphicsView public: PannableGraphicsView() { - grabGesture(Qt::Pan); + grabGesture(Qt::PanGesture); } protected: bool event(QEvent *event) { if (event->type() == QEvent::Gesture) { - QGestureEvent *ge = static_cast(event); - if (const QPannableGesture *g = dynamic_cast(ge->gesture(Qt::Pan))) { + QGestureEvent *gestureEvent = static_cast(event); + if (const QGesture *g = gestureEvent->gesture(Qt::PanGesture)) { QPoint pt = g->pos() - g->lastPos(); horizontalScrollBar()->setValue(horizontalScrollBar()->value() - pt.x()); verticalScrollBar()->setValue(verticalScrollBar()->value() - pt.y()); @@ -71,7 +71,7 @@ public: ImageItem() : colored(false) { - grabGesture(Qt::DoubleTap); + grabGesture(Qt::DoubleTapGesture); } QRectF boundingRect() const @@ -96,7 +96,7 @@ public: { if (event->type() == QEvent::GraphicsSceneGesture) { QGraphicsSceneGestureEvent *gestureEvent = static_cast(event); - if (gestureEvent->gesture(Qt::DoubleTap)) { + if (gestureEvent->gesture(Qt::DoubleTapGesture)) { event->accept(); colored = !colored; update(); diff --git a/examples/gestures/imageviewer/imagewidget.cpp b/examples/gestures/imageviewer/imagewidget.cpp index 0db62bb..1a341a5 100644 --- a/examples/gestures/imageviewer/imagewidget.cpp +++ b/examples/gestures/imageviewer/imagewidget.cpp @@ -61,9 +61,9 @@ ImageWidget::ImageWidget(QWidget *parent) horizontalOffset = 0; verticalOffset = 0; - grabGesture(Qt::DoubleTap); - grabGesture(Qt::Pan); - grabGesture(Qt::LongTap); + grabGesture(Qt::DoubleTapGesture); + grabGesture(Qt::PanGesture); + grabGesture(Qt::TapAndHoldGesture); } void ImageWidget::paintEvent(QPaintEvent*) @@ -136,16 +136,16 @@ void ImageWidget::gestureEvent(QGestureEvent *event) touchFeedback.doubleTapped = false; Q_ASSERT(event); - if (event->contains(Qt::Tap)) { + if (event->contains(Qt::TapGesture)) { // - } else if (const QGesture *g = event->gesture(Qt::DoubleTap)) { + } else if (const QGesture *g = event->gesture(Qt::DoubleTapGesture)) { touchFeedback.doubleTapped = true; horizontalOffset = g->hotSpot().x() - currentImage.width()*1.0*g->hotSpot().x()/width(); verticalOffset = g->hotSpot().y() - currentImage.height()*1.0*g->hotSpot().y()/height(); setZoomedIn(!zoomedIn); zoomed = rotated = false; updateImage(); - } else if (const QGesture *g = event->gesture(Qt::Pan)) { + } else if (const QGesture *g = event->gesture(Qt::PanGesture)) { if (zoomedIn) { // usual panning #ifndef QT_NO_CURSOR @@ -161,8 +161,8 @@ void ImageWidget::gestureEvent(QGestureEvent *event) update(); } else { // only slide gesture should be accepted - const QPannableGesture *pg = dynamic_cast(g); - if (pg && pg->direction() != pg->lastDirection()) { + const QPannableGesture *pg = static_cast(g); + if (pg->direction() != pg->lastDirection()) { // ###: event->cancel(); } if (g->state() == Qt::GestureFinished) { @@ -178,7 +178,7 @@ void ImageWidget::gestureEvent(QGestureEvent *event) updateImage(); } } - } else if (const QGesture *g = event->gesture(Qt::LongTap)) { + } else if (const QGesture *g = event->gesture(Qt::TapAndHoldGesture)) { if (g->state() == Qt::GestureFinished) { qDebug() << "tap and hold detected"; touchFeedback.reset(); @@ -190,8 +190,6 @@ void ImageWidget::gestureEvent(QGestureEvent *event) menu.addAction("Action 3"); menu.exec(mapToGlobal(g->hotSpot())); } - } else if (const QGesture *g = event->gesture(Qt::LongTap)) { - qDebug() << "long tap: " << (g->state() == Qt::GestureStarted ? "started" : "finished"); } else { qDebug() << "unknown gesture"; } -- cgit v0.12 From d8e4d20f3d225a3c29168d7a9440f2efc129ea8e Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 11:54:28 +0100 Subject: Added meta-object properties to QGesture object. --- src/gui/kernel/qgesture.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index f20a63c..8bd312b 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -60,6 +60,15 @@ class Q_GUI_EXPORT QGesture : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QGesture) + + Q_PROPERTY(QRect rect READ rect) + Q_PROPERTY(QPoint hotSpot READ hotSpot) + Q_PROPERTY(QDateTime startTime READ startTime) + Q_PROPERTY(uint duration READ duration) + Q_PROPERTY(QPoint startPos READ startPos) + Q_PROPERTY(QPoint lastPos READ lastPos) + Q_PROPERTY(QPoint pos READ pos) + public: explicit QGesture(QObject *parent, const QString &type, Qt::GestureState state = Qt::GestureStarted); -- cgit v0.12 From d392c3073c10d053ffcd5d64d4f9d89a971fcb2d Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 14:56:24 +0100 Subject: Extended the gesture documentation. Also made some small fixes that noticed while was writing a doc. --- src/corelib/global/qnamespace.h | 17 ++++ src/corelib/kernel/qcoreevent.cpp | 2 + src/gui/kernel/qapplication.cpp | 10 ++ src/gui/kernel/qdirectionrecognizer.cpp | 34 +++---- src/gui/kernel/qdirectionrecognizer_p.h | 26 +----- src/gui/kernel/qevent.cpp | 64 ++++++------- src/gui/kernel/qgesture.cpp | 124 +++++++++++++++++++++++-- src/gui/kernel/qgesture.h | 29 ++---- src/gui/kernel/qgesture_p.h | 4 - src/gui/kernel/qgesturerecognizer.cpp | 79 ++++++++++++++++ src/gui/kernel/qgesturerecognizer.h | 54 +---------- src/gui/kernel/qgesturestandardrecognizers.cpp | 55 ++++------- src/gui/kernel/qgesturestandardrecognizers_p.h | 4 +- src/gui/kernel/qwidget.cpp | 20 +++- src/gui/kernel/qwidget.h | 2 +- 15 files changed, 323 insertions(+), 201 deletions(-) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 56ee768..7875fba 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1569,6 +1569,23 @@ public: GestureFinished = 1 }; + enum DirectionType + { + NoDirection = 0, + LeftDownDirection = 1, + DownLeftDirection = LeftDownDirection, + DownDirection = 2, + RightDownDirection = 3, + DownRightDirection = RightDownDirection, + LeftDirection = 4, + RightDirection = 6, + LeftUpDirection = 7, + UpLeftDirection = LeftUpDirection, + UpDirection = 8, + RightUpDirection = 9, + UpRightDirection = RightUpDirection + }; + } #ifdef Q_MOC_RUN ; diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index ecc8796..7e25c60 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -228,6 +228,8 @@ QT_BEGIN_NAMESPACE \value GraphicsSceneTouchBegin Beginning of a sequence of touch-screen and/or track-pad events in a graphics scene (QGraphicsSceneTouchEvent) \value GraphicsSceneTouchUpdate Touch-screen event in a graphics scene (QGraphicsSceneTouchEvent) \value GraphicsSceneTouchEnd End of touch-event sequence in a graphics scene (QGraphicsSceneTouchEvent) + \value Gesture A gesture has occured. + \value GraphicsSceneGesture A gesture has occured on a graphics scene. User events should have values between \c User and \c{MaxUser}: diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index efe1b13..bcc25b9 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -5088,11 +5088,21 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) return true; } +/*! + Adds custom gesture \a recognizer object. + + \sa Qt::AA_EnableGestures, QGestureEvent +*/ void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) { gestureManager()->addRecognizer(recognizer); } +/*! + Removes custom gesture \a recognizer object. + + \sa Qt::AA_EnableGestures, QGestureEvent +*/ void QApplication::removeGestureRecognizer(QGestureRecognizer *recognizer) { gestureManager()->removeRecognizer(recognizer); diff --git a/src/gui/kernel/qdirectionrecognizer.cpp b/src/gui/kernel/qdirectionrecognizer.cpp index 37b64e7..2896523 100644 --- a/src/gui/kernel/qdirectionrecognizer.cpp +++ b/src/gui/kernel/qdirectionrecognizer.cpp @@ -64,22 +64,22 @@ Direction QDirectionSimpleRecognizer::addPosition(const QPoint &pos) } int dx = pos.x() - lastPoint.x(); int dy = pos.y() - lastPoint.y(); - Direction::DirectionType direction = Direction::None; + Qt::DirectionType direction = Qt::NoDirection; if (dx < 0) { if (-1*dx >= SIZE/2) - direction = Direction::Left; + direction = Qt::LeftDirection; } else { if (dx >= SIZE/2) - direction = Direction::Right; + direction = Qt::RightDirection; } if (dy < 0) { if (-1*dy >= SIZE/2) - direction = Direction::Up; + direction = Qt::UpDirection; } else { if (dy >= SIZE/2) - direction = Direction::Down; + direction = Qt::DownDirection; } - if (direction == Direction::None) + if (direction == Qt::NoDirection) return Direction(); lastPoint = pos; @@ -123,7 +123,7 @@ Direction QDirectionDiagonalRecognizer::addPosition(const QPoint &pos) if (distance < SIZE/2) return Direction(); - Direction::DirectionType direction = Direction::None; + Qt::DirectionType direction = Qt::NoDirection; double angle = atan(1.0*qAbs(lastPoint.y() - pos.y())/qAbs(pos.x() - lastPoint.x())) * 180. / M_PI; if (dx < 0 && dy <= 0) { angle = 180 - angle; @@ -135,25 +135,25 @@ Direction QDirectionDiagonalRecognizer::addPosition(const QPoint &pos) if (angle < 0) angle += 360; if (angle <= 20) - direction = Direction::Right; + direction = Qt::RightDirection; else if (angle <= 65) - direction = Direction::RightUp; + direction = Qt::RightUpDirection; else if (angle <= 110) - direction = Direction::Up; + direction = Qt::UpDirection; else if (angle <= 155) - direction = Direction::LeftUp; + direction = Qt::LeftUpDirection; else if (angle <= 200) - direction = Direction::Left; + direction = Qt::LeftDirection; else if (angle <= 245) - direction = Direction::LeftDown; + direction = Qt::LeftDownDirection; else if (angle <= 290) - direction = Direction::Down; + direction = Qt::DownDirection; else if (angle <= 335) - direction = Direction::RightDown; + direction = Qt::RightDownDirection; else - direction = Direction::Right; + direction = Qt::RightDirection; - if (direction == Direction::None) + if (direction == Qt::NoDirection) return Direction(); lastPoint = pos; diff --git a/src/gui/kernel/qdirectionrecognizer_p.h b/src/gui/kernel/qdirectionrecognizer_p.h index 6390990..ca3d1c1 100644 --- a/src/gui/kernel/qdirectionrecognizer_p.h +++ b/src/gui/kernel/qdirectionrecognizer_p.h @@ -60,32 +60,16 @@ QT_BEGIN_NAMESPACE struct Direction { - enum DirectionType - { - None = 0, - LeftDown = 1, - DownLeft = LeftDown, - Down = 2, - RightDown = 3, - DownRight = RightDown, - Left = 4, - Right = 6, - LeftUp = 7, - UpLeft = LeftUp, - Up = 8, - RightUp = 9, - UpRight = RightUp - }; - DirectionType direction; + Qt::DirectionType direction; QPoint point; - Direction(DirectionType dir, const QPoint &pt) + Direction(Qt::DirectionType dir, const QPoint &pt) : direction(dir), point(pt) { } Direction() - : direction(None) { } + : direction(Qt::NoDirection) { } - inline bool isEmpty() const { return direction == None; } - inline bool isNull() const { return direction == None; } + inline bool isEmpty() const { return direction == Qt::NoDirection; } + inline bool isNull() const { return direction == Qt::NoDirection; } }; typedef QList DirectionList; diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 5761939..3ce2517 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3519,42 +3519,19 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) gesture recognition. The QGestureEvent class contains a list of gestures that are being - executed right now (QGestureEvent::gestureTypes()) and a list of - gestures that are cancelled (the gesture might be cancelled - because the window lost focus, or because of timeout, etc). + executed right now (\l{QGestureEvent::}{gestureTypes()}) and a + list of gestures that are cancelled (the gesture might be + cancelled because the window lost focus, or because of timeout, + etc). \sa QGesture */ -/*! \fn bool QGestureEvent::contains(const Qt::GestureType &type) const - - Checks if the gesture event contains gesture of specific \a type. -*/ - -/*! \fn QList QGestureEvent::gestureTypes() const - - Returns a list of gesture names that the event contains. -*/ - -/*! \fn const QGesture* QGestureEvent::gesture(const Qt::GestureType &type) const - - Returns extended information about a gesture of specific \a type. -*/ - -/*! \fn QList > QGestureEvent::gestures() const - - Returns extended information about all triggered gestures. -*/ - -/*! \fn QSet QGestureEvent::cancelledGestures() const - - Returns a set of gesture names that used to be executed, but got - cancelled (i.e. they were not finished properly). +/*! + Creates new QGestureEvent containing a list of \a gestures that + are being executed and a list of gesture that were cancelled (\a + cancelledGestures). */ - - - - QGestureEvent::QGestureEvent(const QList &gestures, const QSet &cancelledGestures) : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) @@ -3570,40 +3547,65 @@ QGestureEvent::QGestureEvent(const QGestureEvent &event, const QPoint &offset) //### use offset! } +/*! + Destroys the QGestureEvent object. +*/ QGestureEvent::~QGestureEvent() { } +/*! + Checks if the gesture event contains gesture of specific \a type. +*/ bool QGestureEvent::contains(Qt::GestureType type) const { return contains(qt_getStandardGestureTypeName(type)); } +/*! + Checks if the gesture event contains gesture of specific \a type. +*/ bool QGestureEvent::contains(const QString &type) const { return gesture(type) != 0; } +/*! + Returns a list of gesture names that this event contains. +*/ QList QGestureEvent::gestureTypes() const { return m_gestures.keys(); } +/*! + Returns extended information about a gesture of specific \a type. +*/ const QGesture* QGestureEvent::gesture(Qt::GestureType type) const { return gesture(qt_getStandardGestureTypeName(type)); } +/*! + Returns extended information about a gesture of specific \a type. +*/ const QGesture* QGestureEvent::gesture(const QString &type) const { return m_gestures.value(type, QSharedPointer()).data(); } +/*! + Returns extended information about all gestures in the event. +*/ QList > QGestureEvent::gestures() const { return m_gestures.values(); } +/*! + Returns a set of gesture names that used to be executed, but were + cancelled (i.e. they were not finished properly). +*/ QSet QGestureEvent::cancelledGestures() const { return m_cancelledGestures; diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index b2d357c..b2bb859 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -46,11 +46,58 @@ QT_BEGIN_NAMESPACE QString qt_getStandardGestureTypeName(Qt::GestureType type); +/*! + \class QGesture + + \brief The QGesture class represents a gesture, containing all + properties that describe a gesture. + + The widget receives a QGestureEvent with a list of QGesture + objects that represent gestures that are occuring on it. The class + has a list of properties that can be queried by the user to get + some gesture-specific arguments (i.e. position of the tap in the + DoubleTap gesture). + + When creating custom gesture recognizers, they might add new + properties to the gesture object, or custom gesture developers + might subclass the QGesture objects to provide some extended + information. However, if the gesture developer wants to add a new + property to the gesture object that describe coordinate (like a + QPoint or QRect), it is required to subclass the QGesture and + re-implement the \l{QGesture::}{translate} function to make sure + the coordinates are translated properly when the gesture event is + propagated to parent widgets. + + \sa QGestureEvent, QGestureRecognizer +*/ + +/*! + Creates a new gesture object of type \a type in a \a state and + marks it as a child of \a parent. + + Usually QGesture objects should only be contructed by the + QGestureRecognizer classes. +*/ QGesture::QGesture(QObject *parent, const QString &type, Qt::GestureState state) : QObject(*new QGesturePrivate, parent), gestureType_(type), gestureState_(state) { } +/*! + Creates a new gesture object of type \a type in a \a state and + marks it as a child of \a parent. + + This constructor also fills some basic information about the + gesture like a \a startPos which describes the start point of the + gesture, \a lastPos - last point where the gesture happened, \a + pos - a current point, \a rect - a bounding rect of the gesture, + \a hotSpot - a center point of the gesture, \a startTime - a time + when the gesture has started, \a duration - how long the gesture + is going on. + + Usually QGesture objects should only be contructed by the + QGestureRecognizer classes. +*/ QGesture::QGesture(QObject *parent, const QString &type, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, @@ -60,51 +107,105 @@ QGesture::QGesture(QObject *parent, const QString &type, const QPoint &startPos, d_func()->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); } +/*! \internal +*/ QGesture::QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, Qt::GestureState state) : QObject(dd, parent), gestureType_(type), gestureState_(state) { } +/*! + Destroys the gesture object. +*/ QGesture::~QGesture() { } +/*! + Translates the internal gesture properties that represent + coordinates by \a offset. + + Custom gesture recognizer developer have to re-implement this + function if they want to store custom properties that represent + coordinates. +*/ +void QGesture::translate(const QPoint &offset) +{ + Q_D(QGesture); + d->rect.translate(offset); + d->hotSpot += offset; + d->startPos += offset; + d->lastPos += offset; + d->pos += offset; +} + +/*! + Returns a bounding rect of a gesture. +*/ QRect QGesture::rect() const { return d_func()->rect; } +/*! + Returns a center point of a gesture. +*/ QPoint QGesture::hotSpot() const { return d_func()->hotSpot; } +/*! + Returns a time when the gesture has started. +*/ QDateTime QGesture::startTime() const { return d_func()->startTime; } +/*! + Returns a duration time of a gesture. +*/ uint QGesture::duration() const { return d_func()->duration; } +/*! + Returns the start position of the pointer. +*/ QPoint QGesture::startPos() const { return d_func()->startPos; } +/*! + Returns the last recorded position of the pointer. +*/ QPoint QGesture::lastPos() const { return d_func()->lastPos; } +/*! + Returns the position of the pointer. +*/ QPoint QGesture::pos() const { return d_func()->pos; } +/*! + \class QPannableGesture + + \brief The QPannableGesture class represents a Pan gesture, + providing additional information related to panning. + + This class is provided for convenience, panning direction + information is also contained in the QGesture object in it's + properties. +*/ QPannableGesture::QPannableGesture(QObject *parent, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, @@ -114,24 +215,31 @@ QPannableGesture::QPannableGesture(QObject *parent, const QPoint &startPos, { Q_D(QPannableGesture); d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); - d->lastDirection = QPannableGesture::None; - d->direction = QPannableGesture::None; + setProperty("lastDirection", QVariant::fromValue(Qt::NoDirection)); + setProperty("direction", QVariant::fromValue(Qt::NoDirection)); } +/*! + Destroys the QPannableGesture object. +*/ QPannableGesture::~QPannableGesture() { } -QPannableGesture::DirectionType QPannableGesture::lastDirection() const +/*! + Returns the last recorded direction of panning. +*/ +Qt::DirectionType QPannableGesture::lastDirection() const { - Q_D(const QPannableGesture); - return d->lastDirection; + return qVariantValue(property("lastDirection")); } -QPannableGesture::DirectionType QPannableGesture::direction() const +/*! + Returns the current direction of panning. +*/ +Qt::DirectionType QPannableGesture::direction() const { - Q_D(const QPannableGesture); - return d->direction; + return qVariantValue(property("direction")); } QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 8bd312b..b2999d7 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -48,6 +48,7 @@ #include "qpoint.h" #include "qrect.h" #include "qsharedpointer.h" +#include "qmetatype.h" QT_BEGIN_HEADER @@ -93,7 +94,7 @@ public: protected: QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, Qt::GestureState state); - //### virtual void translateCoordinates(const QPoint &offset); + virtual void translate(const QPoint &offset); private: QString gestureType_; @@ -107,36 +108,18 @@ class Q_GUI_EXPORT QPannableGesture : public QGesture Q_DECLARE_PRIVATE(QPannableGesture) public: - enum DirectionType - { - None = 0, - LeftDown = 1, - DownLeft = LeftDown, - Down = 2, - RightDown = 3, - DownRight = RightDown, - Left = 4, - Right = 6, - LeftUp = 7, - UpLeft = LeftUp, - Up = 8, - RightUp = 9, - UpRight = RightUp - }; - -public: QPannableGesture(QObject *parent, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, uint duration, Qt::GestureState state); ~QPannableGesture(); - DirectionType lastDirection() const; - DirectionType direction() const; - - friend class QGestureRecognizerPan; + Qt::DirectionType lastDirection() const; + Qt::DirectionType direction() const; }; +Q_DECLARE_METATYPE(Qt::DirectionType) + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 8a76186..028a622 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -94,10 +94,6 @@ public: class QPannableGesturePrivate : public QGesturePrivate { Q_DECLARE_PUBLIC(QPannableGesture) - -public: - QPannableGesture::DirectionType lastDirection; - QPannableGesture::DirectionType direction; }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp index 9a00138..7791ad7 100644 --- a/src/gui/kernel/qgesturerecognizer.cpp +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -49,17 +49,96 @@ QT_BEGIN_NAMESPACE QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); +/*! + \class QGestureRecognizer + + \brief The QGestureRecognizer class is the base class for + implementing custom gestures. + + This is a base class, to create a custom gesture type, you should + subclass it and implement its pure virtual functions. + + Usually gesture recognizer implements state machine, storing its + state internally in the recognizer object. The recognizer receives + input events through the \l{QGestureRecognizer::}{filterEvent()} + virtual function and decides whether the parsed event should + change the state of the recognizer - i.e. if the event starts or + ends a gesture or if it isn't related to gesture at all. +*/ + +/*! + \enum QGestureRecognizer::Result + + This enum type defines the state of the gesture recognizer. + + \value NotGesture Not a gesture. + + \value GestureStarted The long-term gesture has started. When the + recognizer is in started state, a gesture event containing + QGesture objects returned by the + \l{QGestureRecognizer::}{getGesture()} will be sent to a widget. + + \value GestureFinished A gesture has ended. The gesture event will + be sent to a widget. + + \value MaybeGesture Gesture hasn't started yet, but it might start + soon after next events are received by the recognizer. That means + that gesture manager shouldn't reset() the internal state of the + gesture recognizer. +*/ + +/*! \fn QGestureRecognizer::Result QGestureRecognizer::filterEvent(const QEvent *event) + + This is a pure virtual function that needs to be implemented in + subclasses. + + Parses input \a event and returns the result, which specifies if + the event sequence is a gesture or not. +*/ + +/*! \fn QGesture* QGestureRecognizer::getGesture() + + Returns a gesture object that will be send to the widget. This + function is called when the gesture recognizer changed its state + to QGestureRecognizer::GestureStarted or + QGestureRecognizer::GestureFinished. + + The gesture object is owned by the recognizer itself. +*/ + +/*! \fn void QGestureRecognizer::reset() + + Resets the internal state of the gesture recognizer. +*/ + +/*! \fn void QGestureRecognizer::stateChanged(QGestureRecognizer::Result result) + + The gesture recognizer might emit the stateChanged() signal when + the gesture state changes asynchronously, i.e. without any event + being filtered through filterEvent(). +*/ + QGestureRecognizerPrivate::QGestureRecognizerPrivate() : gestureType(Qt::UnknownGesture) { } +/*! + Creates a new gesture recognizer object that handles gestures of + the specific \a type. + + \sa QApplication::addGestureRecognizer(), + QApplication::removeGestureRecognizer(), +*/ QGestureRecognizer::QGestureRecognizer(const QString &type) : QObject(*new QGestureRecognizerPrivate, 0) { d_func()->customGestureType = type; } +/*! + Returns the name of the gesture that is handled by the recognizer. +*/ QString QGestureRecognizer::gestureType() const { Q_D(const QGestureRecognizer); diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h index c0d4f5e..9ffdb0d 100644 --- a/src/gui/kernel/qgesturerecognizer.h +++ b/src/gui/kernel/qgesturerecognizer.h @@ -48,58 +48,6 @@ QT_BEGIN_NAMESPACE -/*! - \class QGestureRecognizer - - \brief The base class for implementing custom gestures. - - This is a base class, to create a custom gesture type, you should - subclass it and implement pure virtual functions. - - Usually gesture recognizer implements state machine, storing its - state internally in the recognizer object. The recognizer receives - input events through the QGestureRecognizer::filterEvent() virtual - function and decides whether the parsed event should change the - state of the recognizer - i.e. if the event starts or ends a - gesture or if it isn't related to gesture at all. -*/ - -/*! \fn QString gestureType() const - - Returns the name of the gesture that is handled by the recognizer. -*/ - -/*! \fn Result filterEvent(const QEvent *event) - - This is a pure virtual function that need to be implemented in - subclasses. - - Parses input \a events and returns the result, saying if the event - sequence is a gesture or not. -*/ - -/*! \fn QGesture* getGesture() - - Creates a new gesture object that will be send to the widget. This - function is called when the gesture recognizer returned a - QGestureRecognizer::GestureStarted or - QGestureRecognizer::GestureFinished state. - - Created gesture object is owned by the caller. - */ - -/*! \fn void reset() - - Resets the internal state of the gesture recognizer. -*/ - -/*! \fn void stateChanged(QGestureRecognizer::Result result) - - The gesture recognizer might emit the signal when the gesture - state changes asynchronously, i.e. without any event being - received. -*/ - class QGesture; class QGestureRecognizerPrivate; class Q_GUI_EXPORT QGestureRecognizer : public QObject @@ -120,7 +68,7 @@ public: QString gestureType() const; - virtual Result filterEvent(const QEvent* event) = 0; + virtual QGestureRecognizer::Result filterEvent(const QEvent* event) = 0; virtual QGesture* getGesture() = 0; virtual void reset() = 0; diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index 718bdfd..82a0a6f 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -80,17 +80,19 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType) QGestureRecognizerPan::QGestureRecognizerPan() : QGestureRecognizer(QString()), mousePressed(false), gestureFinished(false), - lastDirection(Direction::None), currentDirection(Direction::None) + lastDirection(Qt::NoDirection), currentDirection(Qt::NoDirection) { Q_D(QGestureRecognizer); d->gestureType = Qt::PanGesture; + + qRegisterMetaType(); } QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { const QMouseEvent *ev = static_cast(event); - if (currentDirection != Direction::None) { + if (currentDirection != Qt::NoDirection) { DEBUG() << "Pan: MouseButtonPress: fail. another press during pan"; reset(); return QGestureRecognizer::NotGesture; @@ -100,7 +102,7 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even pressedPos = lastPos = currentPos = ev->pos(); return QGestureRecognizer::MaybeGesture; } else if (event->type() == QEvent::MouseButtonRelease) { - if (mousePressed && currentDirection != Direction::None) { + if (mousePressed && currentDirection != Qt::NoDirection) { DEBUG() << "Pan: MouseButtonRelease: pan detected"; gestureFinished = true; const QMouseEvent *ev = static_cast(event); @@ -117,19 +119,19 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even const QMouseEvent *ev = static_cast(event); lastPos = currentPos; currentPos = ev->pos(); - Direction::DirectionType direction = + Qt::DirectionType direction = simpleRecognizer.addPosition(ev->pos()).direction; DEBUG() << "Pan: MouseMove: simplerecognizer result = " << direction; QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; - if (currentDirection == Direction::None) { - if (direction == Direction::None) + if (currentDirection == Qt::NoDirection) { + if (direction == Qt::NoDirection) result = QGestureRecognizer::MaybeGesture; else result = QGestureRecognizer::GestureStarted; } else { result = QGestureRecognizer::GestureStarted; } - if (direction != Direction::None) { + if (direction != Qt::NoDirection) { if (currentDirection != direction) lastDirection = currentDirection; currentDirection = direction; @@ -139,37 +141,16 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even return QGestureRecognizer::NotGesture; } -static inline QPannableGesture::DirectionType convertPanningDirection(const Direction::DirectionType direction) -{ - switch (direction) { - case Direction::Left: - return QPannableGesture::Left; - case Direction::Right: - return QPannableGesture::Right; - case Direction::Up: - return QPannableGesture::Up; - case Direction::Down: - return QPannableGesture::Down; - default: - break; - } - return QPannableGesture::None; -} - QGesture* QGestureRecognizerPan::getGesture() { - QPannableGesture::DirectionType dir = convertPanningDirection(currentDirection); - QPannableGesture::DirectionType lastDir = convertPanningDirection(lastDirection); - if (dir == QPannableGesture::None) + if (currentDirection == Qt::NoDirection) return 0; - QPannableGesture *g = - new QPannableGesture(this, - pressedPos, lastPos, currentPos, - QRect(), pressedPos, QDateTime(), 0, - gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); - QPannableGesturePrivate *d = g->d_func(); - d->lastDirection = lastDir; - d->direction = dir; + QGesture *g = new QGesture(this, qt_getStandardGestureTypeName(Qt::PanGesture), + pressedPos, lastPos, currentPos, + QRect(), pressedPos, QDateTime(), 0, + gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); + g->setProperty("lastDirection", QVariant::fromValue(lastDirection)); + g->setProperty("direction", QVariant::fromValue(currentDirection)); return g; } @@ -177,8 +158,8 @@ QGesture* QGestureRecognizerPan::getGesture() void QGestureRecognizerPan::reset() { mousePressed = false; - lastDirection = Direction::None; - currentDirection = Direction::None; + lastDirection = Qt::NoDirection; + currentDirection = Qt::NoDirection; gestureFinished = false; diagonalRecognizer.reset(); simpleRecognizer.reset(); diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h index 75a206a..8f1361f 100644 --- a/src/gui/kernel/qgesturestandardrecognizers_p.h +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -80,8 +80,8 @@ private: QPoint currentPos; bool mousePressed; bool gestureFinished; - Direction::DirectionType lastDirection; - Direction::DirectionType currentDirection; + Qt::DirectionType lastDirection; + Qt::DirectionType currentDirection; QDirectionDiagonalRecognizer diagonalRecognizer; QDirectionSimpleRecognizer simpleRecognizer; }; diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 2f794f4..d37f360 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -11031,8 +11031,9 @@ QWindowSurface *QWidget::windowSurface() const Subscribes the widget to the specified \a gesture type. Returns the id of the gesture. -*/ + \sa grabGesture(), releaseGesture(), setGestureEnabled() +*/ int QWidget::grabGesture(const QString &gesture) { Q_D(QWidget); @@ -11053,16 +11054,19 @@ int QWidgetPrivate::grabGesture(int id) Subscribes the widget to the specified \a gesture type. Returns the id of the gesture. -*/ + \sa grabGesture(), releaseGesture(), setGestureEnabled() +*/ int QWidget::grabGesture(Qt::GestureType gesture) { return grabGesture(qt_getStandardGestureTypeName(gesture)); } /*! - Unsubscribes the widget from a gesture, which is specified by its - \a id. + Unsubscribes the widget from a gesture, which is specified by the + \a gestureId. + + \sa grabGesture(),setGestureEnabled() */ void QWidget::releaseGesture(int gestureId) { @@ -11075,6 +11079,14 @@ void QWidget::releaseGesture(int gestureId) } } +/*! + If \a enable is true, the gesture with the given \a gestureId is + enabled; otherwise the gesture is disabled. + + The id of the gesture is returned by the grabGesture(). + + \sa grabGesture(), releaseGesture() +*/ void setGestureEnabled(int gestureId, bool enable) { //### diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index a76c50a..6324782 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -616,7 +616,7 @@ public: int grabGesture(const QString &gesture); int grabGesture(Qt::GestureType gesture); void releaseGesture(int gestureId); - void setGestureEnabled(int gestureId, bool enable); + void setGestureEnabled(int gestureId, bool enable = true); Q_SIGNALS: void customContextMenuRequested(const QPoint &pos); -- cgit v0.12 From 415039d9c9dab219505cc082981e2175a8977b03 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 15:55:30 +0100 Subject: More documentation fixes. --- doc/src/qnamespace.qdoc | 33 +++++++++++++- src/gui/graphicsview/qgraphicsitem.cpp | 48 +++++++++++++++++--- src/gui/graphicsview/qgraphicsitem.h | 5 ++- src/gui/graphicsview/qgraphicssceneevent.cpp | 51 +++++++++++----------- src/gui/kernel/qevent.cpp | 13 ++---- src/gui/kernel/qevent.h | 2 - src/gui/kernel/qgesture.cpp | 65 ++++++++++++++++------------ src/gui/kernel/qgesture.h | 9 ++-- src/gui/kernel/qwidget.cpp | 8 ++-- 9 files changed, 149 insertions(+), 85 deletions(-) diff --git a/doc/src/qnamespace.qdoc b/doc/src/qnamespace.qdoc index c44b41e..c30dc88 100644 --- a/doc/src/qnamespace.qdoc +++ b/doc/src/qnamespace.qdoc @@ -2676,9 +2676,17 @@ \sa QPixmapBorders, qDrawBorderPixmap() */ -/*! \typedef Qt::GestureType +/*! \enum Qt::GestureType - A string representing a type of a gesture. + This enum lists standard gestures. + + \value UnknownGesture An unknown gesture. This enum value shouldn't be used. + \value TapGesture A single tap gesture. + \value DoubleTapGesture A double tap gesture. + \value TrippleTapGesture A tripple tap gesture. + \value TapAndHoldGesture A tap-and-hold (long tap) gesture. + \value PanGesture A pan gesture. + \value PinchGesture A pinch gesture. */ /*! @@ -2691,3 +2699,24 @@ \sa QGesture */ + +/*! + \enum Qt::DirectionType + + This enum type describes directions. This could be used by the + gesture recognizers. + + \value NoDirection Non-specific direction. + \value LeftDownDirection + \value DownLeftDirection + \value DownDirection + \value RightDownDirection + \value DownRightDirection + \value LeftDirection + \value RightDirection + \value LeftUpDirection + \value UpLeftDirection + \value UpDirection + \value RightUpDirection + \value UpRightDirection +*/ diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index cbd4834..0dc227a 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -5790,27 +5790,61 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const return QVariant(); } -int QGraphicsItem::grabGesture(Qt::GestureType type) +/*! + Subscribes the graphics item to the specified \a gesture type. + + Returns the id of the gesture. + + \sa releaseGesture(), setGestureEnabled() +*/ +int QGraphicsItem::grabGesture(Qt::GestureType gesture) { - return grabGesture(qt_getStandardGestureTypeName(type)); + return grabGesture(qt_getStandardGestureTypeName(gesture)); } -int QGraphicsItem::grabGesture(const QString &type) +/*! + Subscribes the graphics item to the specified \a gesture type. + + Returns the id of the gesture. + + \sa releaseGesture(), setGestureEnabled() +*/ +int QGraphicsItem::grabGesture(const QString &gesture) { - int id = qHash(type); + int id = qHash(gesture); QSet::iterator it = d_ptr->gestures.find(id); if (it != d_ptr->gestures.end()) return *it; d_ptr->gestures << id; if (d_ptr->scene) d_ptr->scene->d_func()->grabGesture(this, id); + return id; } -void QGraphicsItem::releaseGesture(int id) +/*! + Unsubscribes the graphics item from a gesture, which is specified + by the \a gestureId. + + \sa grabGesture(), setGestureEnabled() +*/ +void QGraphicsItem::releaseGesture(int gestureId) { - d_ptr->gestures.remove(id); + d_ptr->gestures.remove(gestureId); if (d_ptr->scene) - d_ptr->scene->d_func()->releaseGesture(this, id); + d_ptr->scene->d_func()->releaseGesture(this, gestureId); +} + +/*! + If \a enable is true, the gesture with the given \a gestureId is + enabled; otherwise the gesture is disabled. + + The id of the gesture is returned by the grabGesture(). + + \sa grabGesture(), releaseGesture() +*/ +void QGraphicsItem::setGestureEnabled(int gestureId, bool enable) +{ + //### } /*! diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 4e6e96d..37d8f78 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -338,9 +338,10 @@ public: QVariant data(int key) const; void setData(int key, const QVariant &value); - int grabGesture(Qt::GestureType type); - int grabGesture(const QString &type); + int grabGesture(Qt::GestureType gesture); + int grabGesture(const QString &gesture); void releaseGesture(int gestureId); + void setGestureEnabled(int gestureId, bool enable = true); enum { Type = 1, diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 4f24a74..c79e30b 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1727,18 +1727,24 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) gestures with \l{QGraphicsItem::}{grabGesture()}. */ - +/*! + Constructs a QGraphicsSceneGestureEvent. +*/ QGraphicsSceneGestureEvent::QGraphicsSceneGestureEvent() : QGraphicsSceneEvent(QEvent::GraphicsSceneGesture) { } +/*! + Destroys a QGraphicsSceneGestureEvent. +*/ QGraphicsSceneGestureEvent::~QGraphicsSceneGestureEvent() { } /*! - Checks if the gesture event contains gesture of specific \a type. + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. */ bool QGraphicsSceneGestureEvent::contains(const QString &type) const { @@ -1746,7 +1752,8 @@ bool QGraphicsSceneGestureEvent::contains(const QString &type) const } /*! - Checks if the gesture event contains gesture of specific \a type. + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. */ bool QGraphicsSceneGestureEvent::contains(Qt::GestureType type) const { @@ -1754,7 +1761,7 @@ bool QGraphicsSceneGestureEvent::contains(Qt::GestureType type) const } /*! - Returns a list of gesture names that the event contains. + Returns a list of gesture names that this event contains. */ QList QGraphicsSceneGestureEvent::gestureTypes() const { @@ -1778,7 +1785,7 @@ const QGesture* QGraphicsSceneGestureEvent::gesture(Qt::GestureType type) const } /*! - Returns extended information about all triggered gestures. + Returns extended information about all gestures in the event. */ QList > QGraphicsSceneGestureEvent::gestures() const { @@ -1786,7 +1793,7 @@ QList > QGraphicsSceneGestureEvent::gestures() const } /*! - Returns a set of gesture names that used to be executed, but got + Returns a set of gesture names that used to be executed, but were cancelled (i.e. they were not finished properly). */ QSet QGraphicsSceneGestureEvent::cancelledGestures() const @@ -1795,8 +1802,9 @@ QSet QGraphicsSceneGestureEvent::cancelledGestures() const } /*! - Returns a set of gesture names that used to be executed, but got - cancelled (i.e. they were not finished properly). + Sets a list of gesture names \a cancelledGestures that used to be + executed, but were cancelled (i.e. they were not finished + properly). */ void QGraphicsSceneGestureEvent::setCancelledGestures(const QSet &cancelledGestures) { @@ -1807,13 +1815,10 @@ void QGraphicsSceneGestureEvent::setCancelledGestures(const QSet &cance Maps the point \a point, which is in a view coordinate system, to scene coordinate system, and returns the mapped coordinate. - \a Point is in coordinate system of the widget that received + A \a point is in coordinate system of the widget that received gesture event. - \sa mapToScene(const QRect &rect), - mapToItem(const QPoint &point, QGraphicsItem *item), - mapToItem(const QRect &rect, QGraphicsItem *item), - {The Graphics View Coordinate System} + \sa mapToItem(), {The Graphics View Coordinate System} */ QPointF QGraphicsSceneGestureEvent::mapToScene(const QPoint &point) const { @@ -1826,13 +1831,10 @@ QPointF QGraphicsSceneGestureEvent::mapToScene(const QPoint &point) const Maps the rectangular \a rect, which is in a view coordinate system, to scene coordinate system, and returns the mapped coordinate. - \a Point is in coordinate system of the widget that received + A \a rect is in coordinate system of the widget that received gesture event. - \sa mapToScene(const QPoint &rect), - mapToItem(const QPoint &point, QGraphicsItem *item), - mapToItem(const QRect &rect, QGraphicsItem *item), - {The Graphics View Coordinate System} + \sa mapToItem(), {The Graphics View Coordinate System} */ QPolygonF QGraphicsSceneGestureEvent::mapToScene(const QRect &rect) const { @@ -1847,9 +1849,7 @@ QPolygonF QGraphicsSceneGestureEvent::mapToScene(const QRect &rect) const If \a item is 0, this function returns the same as mapToScene(). - \sa mapToScene(const QPoint &rect), mapToScene(const QRect &rect), - mapToItem(const QRect &, QGraphicsItem *item), - {The Graphics View Coordinate System} + \sa mapToScene(), {The Graphics View Coordinate System} */ QPointF QGraphicsSceneGestureEvent::mapToItem(const QPoint &point, QGraphicsItem *item) const { @@ -1863,14 +1863,12 @@ QPointF QGraphicsSceneGestureEvent::mapToItem(const QPoint &point, QGraphicsItem } /*! - Maps the point \a point, which is in a view coordinate system, to + Maps the rectangualar \a rect, which is in a view coordinate system, to item's \a item coordinate system, and returns the mapped coordinate. If \a item is 0, this function returns the same as mapToScene(). - \sa mapToScene(const QPoint &rect), mapToScene(const QRect &rect), - mapToItem(const QPoint &point, QGraphicsItem *item), - {The Graphics View Coordinate System} + \sa mapToScene(), {The Graphics View Coordinate System} */ QPolygonF QGraphicsSceneGestureEvent::mapToItem(const QRect &rect, QGraphicsItem *item) const { @@ -1883,6 +1881,9 @@ QPolygonF QGraphicsSceneGestureEvent::mapToItem(const QRect &rect, QGraphicsItem return QPolygonF(); } +/*! + Set a list of gesture objects containing extended information about \a gestures. +*/ void QGraphicsSceneGestureEvent::setGestures(const QList > &gestures) { foreach(const QSharedPointer &g, gestures) diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 3ce2517..dbfe528 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3540,13 +3540,6 @@ QGestureEvent::QGestureEvent(const QList &gestures, m_gestures.insert(r->gestureType(), QSharedPointer(r)); } -QGestureEvent::QGestureEvent(const QGestureEvent &event, const QPoint &offset) - : QEvent(QEvent::Gesture), m_gestures(event.m_gestures), - m_cancelledGestures(event.m_cancelledGestures) -{ - //### use offset! -} - /*! Destroys the QGestureEvent object. */ @@ -3555,7 +3548,8 @@ QGestureEvent::~QGestureEvent() } /*! - Checks if the gesture event contains gesture of specific \a type. + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. */ bool QGestureEvent::contains(Qt::GestureType type) const { @@ -3563,7 +3557,8 @@ bool QGestureEvent::contains(Qt::GestureType type) const } /*! - Checks if the gesture event contains gesture of specific \a type. + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. */ bool QGestureEvent::contains(const QString &type) const { diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 48e420a..41005d8 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -719,8 +719,6 @@ class Q_GUI_EXPORT QGestureEvent : public QEvent public: QGestureEvent(const QList &gestures, const QSet &cancelledGestures = QSet()); - // internal ctor - QGestureEvent(const QGestureEvent &gestures, const QPoint &offset); ~QGestureEvent(); bool contains(Qt::GestureType type) const; diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index b2bb859..35a3e2a 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -64,13 +64,23 @@ QString qt_getStandardGestureTypeName(Qt::GestureType type); information. However, if the gesture developer wants to add a new property to the gesture object that describe coordinate (like a QPoint or QRect), it is required to subclass the QGesture and - re-implement the \l{QGesture::}{translate} function to make sure + re-implement the \l{QGesture::}{translate()} function to make sure the coordinates are translated properly when the gesture event is propagated to parent widgets. \sa QGestureEvent, QGestureRecognizer */ +/*! \fn QString QGesture::gestureType() const + + Returns the type of the gesture. +*/ + +/*! \fn Qt::GestureState QGesture::state() const + + Returns the current state of the gesture. +*/ + /*! Creates a new gesture object of type \a type in a \a state and marks it as a child of \a parent. @@ -141,7 +151,9 @@ void QGesture::translate(const QPoint &offset) } /*! - Returns a bounding rect of a gesture. + \property QGesture::rect + + \brief The bounding rect of a gesture. */ QRect QGesture::rect() const { @@ -149,7 +161,9 @@ QRect QGesture::rect() const } /*! - Returns a center point of a gesture. + \property QGesture::hotSpot + + \brief The center point of a gesture. */ QPoint QGesture::hotSpot() const { @@ -157,7 +171,9 @@ QPoint QGesture::hotSpot() const } /*! - Returns a time when the gesture has started. + \property QGesture::startTime + + \brief The time when the gesture has started. */ QDateTime QGesture::startTime() const { @@ -165,7 +181,9 @@ QDateTime QGesture::startTime() const } /*! - Returns a duration time of a gesture. + \property QGesture::duration + + \brief The duration time of a gesture. */ uint QGesture::duration() const { @@ -173,7 +191,9 @@ uint QGesture::duration() const } /*! - Returns the start position of the pointer. + \property QGesture::startPos + + \brief The start position of the pointer. */ QPoint QGesture::startPos() const { @@ -181,7 +201,9 @@ QPoint QGesture::startPos() const } /*! - Returns the last recorded position of the pointer. + \property QGesture::lastPos + + \brief The last recorded position of the pointer. */ QPoint QGesture::lastPos() const { @@ -189,7 +211,9 @@ QPoint QGesture::lastPos() const } /*! - Returns the position of the pointer. + \property QGesture::pos + + \brief The current position of the pointer. */ QPoint QGesture::pos() const { @@ -206,28 +230,11 @@ QPoint QGesture::pos() const information is also contained in the QGesture object in it's properties. */ -QPannableGesture::QPannableGesture(QObject *parent, const QPoint &startPos, - const QPoint &lastPos, const QPoint &pos, const QRect &rect, - const QPoint &hotSpot, const QDateTime &startTime, - uint duration, Qt::GestureState state) - : QGesture(*new QPannableGesturePrivate, parent, - qt_getStandardGestureTypeName(Qt::PanGesture), state) -{ - Q_D(QPannableGesture); - d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); - setProperty("lastDirection", QVariant::fromValue(Qt::NoDirection)); - setProperty("direction", QVariant::fromValue(Qt::NoDirection)); -} /*! - Destroys the QPannableGesture object. -*/ -QPannableGesture::~QPannableGesture() -{ -} + \property QPannableGesture::lastDirection -/*! - Returns the last recorded direction of panning. + \brief The last recorded direction of panning. */ Qt::DirectionType QPannableGesture::lastDirection() const { @@ -235,7 +242,9 @@ Qt::DirectionType QPannableGesture::lastDirection() const } /*! - Returns the current direction of panning. + \property QPannableGesture::direction + + \brief The current direction of panning. */ Qt::DirectionType QPannableGesture::direction() const { diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index b2999d7..4e692be 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -107,13 +107,10 @@ class Q_GUI_EXPORT QPannableGesture : public QGesture Q_OBJECT Q_DECLARE_PRIVATE(QPannableGesture) -public: - QPannableGesture(QObject *parent, const QPoint &startPos, - const QPoint &lastPos, const QPoint &pos, const QRect &rect, - const QPoint &hotSpot, const QDateTime &startTime, - uint duration, Qt::GestureState state); - ~QPannableGesture(); + Q_PROPERTY(Qt::DirectionType lastDirection READ lastDirection) + Q_PROPERTY(Qt::DirectionType direction READ direction) +public: Qt::DirectionType lastDirection() const; Qt::DirectionType direction() const; }; diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index d37f360..9c0c404 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -11032,7 +11032,7 @@ QWindowSurface *QWidget::windowSurface() const Returns the id of the gesture. - \sa grabGesture(), releaseGesture(), setGestureEnabled() + \sa releaseGesture(), setGestureEnabled() */ int QWidget::grabGesture(const QString &gesture) { @@ -11055,7 +11055,7 @@ int QWidgetPrivate::grabGesture(int id) Returns the id of the gesture. - \sa grabGesture(), releaseGesture(), setGestureEnabled() + \sa releaseGesture(), setGestureEnabled() */ int QWidget::grabGesture(Qt::GestureType gesture) { @@ -11066,7 +11066,7 @@ int QWidget::grabGesture(Qt::GestureType gesture) Unsubscribes the widget from a gesture, which is specified by the \a gestureId. - \sa grabGesture(),setGestureEnabled() + \sa grabGesture(), setGestureEnabled() */ void QWidget::releaseGesture(int gestureId) { @@ -11087,7 +11087,7 @@ void QWidget::releaseGesture(int gestureId) \sa grabGesture(), releaseGesture() */ -void setGestureEnabled(int gestureId, bool enable) +void QWidget::setGestureEnabled(int gestureId, bool enable) { //### } -- cgit v0.12 From 2e91de4a0046c17ff41f4ed533542a5fc3858087 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 15:57:58 +0100 Subject: Renamed QPannableGesture to QPanningGesture. --- src/gui/kernel/qgesture.cpp | 12 ++++++------ src/gui/kernel/qgesture.h | 6 +++--- src/gui/kernel/qgesture_p.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index 35a3e2a..76101f2 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -221,9 +221,9 @@ QPoint QGesture::pos() const } /*! - \class QPannableGesture + \class QPanningGesture - \brief The QPannableGesture class represents a Pan gesture, + \brief The QPanningGesture class represents a Pan gesture, providing additional information related to panning. This class is provided for convenience, panning direction @@ -232,21 +232,21 @@ QPoint QGesture::pos() const */ /*! - \property QPannableGesture::lastDirection + \property QPanningGesture::lastDirection \brief The last recorded direction of panning. */ -Qt::DirectionType QPannableGesture::lastDirection() const +Qt::DirectionType QPanningGesture::lastDirection() const { return qVariantValue(property("lastDirection")); } /*! - \property QPannableGesture::direction + \property QPanningGesture::direction \brief The current direction of panning. */ -Qt::DirectionType QPannableGesture::direction() const +Qt::DirectionType QPanningGesture::direction() const { return qVariantValue(property("direction")); } diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 4e692be..b9e86a3 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -101,11 +101,11 @@ private: Qt::GestureState gestureState_; }; -class QPannableGesturePrivate; -class Q_GUI_EXPORT QPannableGesture : public QGesture +class QPanningGesturePrivate; +class Q_GUI_EXPORT QPanningGesture : public QGesture { Q_OBJECT - Q_DECLARE_PRIVATE(QPannableGesture) + Q_DECLARE_PRIVATE(QPanningGesture) Q_PROPERTY(Qt::DirectionType lastDirection READ lastDirection) Q_PROPERTY(Qt::DirectionType direction READ direction) diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 028a622..24350d4 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -91,9 +91,9 @@ public: QPoint pos; }; -class QPannableGesturePrivate : public QGesturePrivate +class QPanningGesturePrivate : public QGesturePrivate { - Q_DECLARE_PUBLIC(QPannableGesture) + Q_DECLARE_PUBLIC(QPanningGesture) }; QT_END_NAMESPACE -- cgit v0.12 From a72e6d351f136e0cd5e0f9c178e5f09eb70ca2db Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 16:04:13 +0100 Subject: Removed the use of QSharedPointer from gesture api - since QGesture are QObject now everything is much simplier. --- src/gui/graphicsview/qgraphicssceneevent.cpp | 8 ++++---- src/gui/graphicsview/qgraphicssceneevent.h | 7 +++---- src/gui/kernel/qevent.cpp | 6 +++--- src/gui/kernel/qevent.h | 5 ++--- src/gui/kernel/qgesture.h | 1 - 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index c79e30b..b26f8cf 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1773,7 +1773,7 @@ QList QGraphicsSceneGestureEvent::gestureTypes() const */ const QGesture* QGraphicsSceneGestureEvent::gesture(const QString &type) const { - return m_gestures.value(type, QSharedPointer()).data(); + return m_gestures.value(type, 0); } /*! @@ -1787,7 +1787,7 @@ const QGesture* QGraphicsSceneGestureEvent::gesture(Qt::GestureType type) const /*! Returns extended information about all gestures in the event. */ -QList > QGraphicsSceneGestureEvent::gestures() const +QList QGraphicsSceneGestureEvent::gestures() const { return m_gestures.values(); } @@ -1884,9 +1884,9 @@ QPolygonF QGraphicsSceneGestureEvent::mapToItem(const QRect &rect, QGraphicsItem /*! Set a list of gesture objects containing extended information about \a gestures. */ -void QGraphicsSceneGestureEvent::setGestures(const QList > &gestures) +void QGraphicsSceneGestureEvent::setGestures(const QList &gestures) { - foreach(const QSharedPointer &g, gestures) + foreach(QGesture *g, gestures) m_gestures.insert(g->gestureType(), g); } diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index d1a301e..244c25e 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -48,7 +48,6 @@ #include #include #include -#include QT_BEGIN_HEADER @@ -324,8 +323,8 @@ public: const QGesture* gesture(Qt::GestureType type) const; const QGesture* gesture(const QString &type) const; - QList > gestures() const; - void setGestures(const QList > &gestures); + QList gestures() const; + void setGestures(const QList &gestures); QSet cancelledGestures() const; void setCancelledGestures(const QSet &cancelledGestures); @@ -336,7 +335,7 @@ public: QPolygonF mapToItem(const QRect &rect, QGraphicsItem *item) const; protected: - QHash > m_gestures; + QHash m_gestures; QSet m_cancelledGestures; }; diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index dbfe528..23a45b5 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3537,7 +3537,7 @@ QGestureEvent::QGestureEvent(const QList &gestures, : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) { foreach(QGesture *r, gestures) - m_gestures.insert(r->gestureType(), QSharedPointer(r)); + m_gestures.insert(r->gestureType(), r); } /*! @@ -3586,13 +3586,13 @@ const QGesture* QGestureEvent::gesture(Qt::GestureType type) const */ const QGesture* QGestureEvent::gesture(const QString &type) const { - return m_gestures.value(type, QSharedPointer()).data(); + return m_gestures.value(type, 0); } /*! Returns extended information about all gestures in the event. */ -QList > QGestureEvent::gestures() const +QList QGestureEvent::gestures() const { return m_gestures.values(); } diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 41005d8..280ca79 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -54,7 +54,6 @@ #include #include #include -#include #include QT_BEGIN_HEADER @@ -728,12 +727,12 @@ public: const QGesture* gesture(Qt::GestureType type) const; const QGesture* gesture(const QString &type) const; - QList > gestures() const; + QList gestures() const; QSet cancelledGestures() const; protected: - QHash > m_gestures; + QHash m_gestures; QSet m_cancelledGestures; friend class QApplication; diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index b9e86a3..d4b2165 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -47,7 +47,6 @@ #include "qdatetime.h" #include "qpoint.h" #include "qrect.h" -#include "qsharedpointer.h" #include "qmetatype.h" QT_BEGIN_HEADER -- cgit v0.12 From 31d90932bd52d2557a9f1b164632e879105ce67b Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 16:09:46 +0100 Subject: Gestures/imageviewer example compiles --- examples/gestures/imageviewer/imagewidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gestures/imageviewer/imagewidget.cpp b/examples/gestures/imageviewer/imagewidget.cpp index 1a341a5..0a279ca 100644 --- a/examples/gestures/imageviewer/imagewidget.cpp +++ b/examples/gestures/imageviewer/imagewidget.cpp @@ -161,17 +161,17 @@ void ImageWidget::gestureEvent(QGestureEvent *event) update(); } else { // only slide gesture should be accepted - const QPannableGesture *pg = static_cast(g); + const QPanningGesture *pg = static_cast(g); if (pg->direction() != pg->lastDirection()) { // ###: event->cancel(); } if (g->state() == Qt::GestureFinished) { touchFeedback.sliding = false; zoomed = rotated = false; - if (pg->direction() == QPannableGesture::Right) { + if (pg->direction() == Qt::RightDirection) { qDebug() << "slide right"; goNextImage(); - } else if (pg->direction() == QPannableGesture::Left) { + } else if (pg->direction() == Qt::LeftDirection) { qDebug() << "slide left"; goPrevImage(); } -- cgit v0.12 From 841b10d10943d912fe75674045a740cff29e266c Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 16:31:05 +0100 Subject: Removed QWidget::gestureEvent() functions since adding a new virtual function breaks binary compatibility. --- examples/gestures/imageviewer/imagewidget.cpp | 9 ++++ examples/gestures/imageviewer/imagewidget.h | 1 + src/gui/graphicsview/qgraphicsscene.cpp | 68 +++++++++++++-------------- src/gui/graphicsview/qgraphicsscene.h | 1 - src/gui/kernel/qwidget.cpp | 7 +-- src/gui/kernel/qwidget.h | 2 - 6 files changed, 43 insertions(+), 45 deletions(-) diff --git a/examples/gestures/imageviewer/imagewidget.cpp b/examples/gestures/imageviewer/imagewidget.cpp index 0a279ca..b3c92bc 100644 --- a/examples/gestures/imageviewer/imagewidget.cpp +++ b/examples/gestures/imageviewer/imagewidget.cpp @@ -131,6 +131,15 @@ void ImageWidget::paintEvent(QPaintEvent*) p.restore(); } +bool ImageWidget::event(QEvent *event) +{ + if (event->type() == QEvent::Gesture) { + gestureEvent(static_cast(event)); + return true; + } + return QWidget::event(event); +} + void ImageWidget::gestureEvent(QGestureEvent *event) { touchFeedback.doubleTapped = false; diff --git a/examples/gestures/imageviewer/imagewidget.h b/examples/gestures/imageviewer/imagewidget.h index 56fcb40..3f3ff96 100644 --- a/examples/gestures/imageviewer/imagewidget.h +++ b/examples/gestures/imageviewer/imagewidget.h @@ -56,6 +56,7 @@ public: void openDirectory(const QString &path); protected: + bool event(QEvent *event); void paintEvent(QPaintEvent*); void gestureEvent(QGestureEvent *event); void resizeEvent(QResizeEvent*); diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 0f70cfe..337b6ee 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3951,8 +3951,38 @@ bool QGraphicsScene::event(QEvent *event) // geometries that do not have an explicit style set. update(); break; - case QEvent::GraphicsSceneGesture: - gestureEvent(static_cast(event)); + case QEvent::GraphicsSceneGesture: { + QGraphicsSceneGestureEvent *ev = static_cast(event); + QList gestureTypes = ev->gestureTypes(); + QGraphicsView *view = qobject_cast(ev->widget()); + if (!view) { + qWarning("QGraphicsScene::event: gesture event was received without a view"); + break; + } + + // find graphics items that intersects with gestures hot spots. + QPolygonF poly; + QMap sceneHotSpots; + foreach(const QString &type, gestureTypes) { + QPointF pt = ev->mapToScene(ev->gesture(type)->hotSpot()); + sceneHotSpots.insert(qHash(type), pt); + poly << pt; + } + QList itemsInGestureArea = items(poly, Qt::IntersectsItemBoundingRect); + + foreach(QGraphicsItem *item, itemsInGestureArea) { + QMap::const_iterator it = sceneHotSpots.begin(), + e = sceneHotSpots.end(); + for(; it != e; ++it) { + if (item->contains(item->mapFromScene(it.value())) && + item->d_ptr->gestures.contains(it.key())) { + d->sendEvent(item, ev); + if (ev->isAccepted()) + break; + } + } + } + } break; case QEvent::GraphicsSceneTouchBegin: d->touchBeginEvent(static_cast(event)); @@ -5604,40 +5634,6 @@ void QGraphicsScenePrivate::removeView(QGraphicsView *view) view->releaseGesture(gestureId); } -void QGraphicsScene::gestureEvent(QGraphicsSceneGestureEvent *event) -{ - Q_D(QGraphicsScene); - QList gestureTypes = event->gestureTypes(); - QGraphicsView *view = qobject_cast(event->widget()); - if (!view) { - qWarning("QGraphicsScene::gestureEvent: gesture event was received without a view"); - return; - } - - // find graphics items that intersects with gestures hot spots. - QPolygonF poly; - QMap sceneHotSpots; - foreach(const QString &type, gestureTypes) { - QPointF pt = event->mapToScene(event->gesture(type)->hotSpot()); - sceneHotSpots.insert(qHash(type), pt); - poly << pt; - } - QList itemsInGestureArea = items(poly, Qt::IntersectsItemBoundingRect); - - foreach(QGraphicsItem *item, itemsInGestureArea) { - QMap::const_iterator it = sceneHotSpots.begin(), - e = sceneHotSpots.end(); - for(; it != e; ++it) { - if (item->contains(item->mapFromScene(it.value())) && - item->d_ptr->gestures.contains(it.key())) { - d->sendEvent(item, event); - if (event->isAccepted()) - break; - } - } - } -} - void QGraphicsScenePrivate::grabGesture(QGraphicsItem *item, int gestureId) { if (!grabbedGestures.contains(gestureId)) { diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 45def44..a76b348 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -256,7 +256,6 @@ protected: virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); virtual void wheelEvent(QGraphicsSceneWheelEvent *event); virtual void inputMethodEvent(QInputMethodEvent *event); - virtual void gestureEvent(QGraphicsSceneGestureEvent *event); virtual void drawBackground(QPainter *painter, const QRectF &rect); virtual void drawForeground(QPainter *painter, const QRectF &rect); diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 9c0c404..0029809 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -7931,7 +7931,7 @@ bool QWidget::event(QEvent *event) break; #endif case QEvent::Gesture: - gestureEvent((QGestureEvent*)event); + event->ignore(); break; #ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { @@ -8738,11 +8738,6 @@ bool QWidget::qwsEvent(QWSEvent *) #endif -void QWidget::gestureEvent(QGestureEvent *event) -{ - event->ignore(); -} - /*! Ensures that the widget has been polished by QStyle (i.e., has a diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index 6324782..80d4f3e 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -677,8 +677,6 @@ protected: // Misc. protected functions virtual void changeEvent(QEvent *); - virtual void gestureEvent(QGestureEvent *); - int metric(PaintDeviceMetric) const; virtual void inputMethodEvent(QInputMethodEvent *); -- cgit v0.12 From 9467195595d8672c3bb1a58e1f0882ba9223f8d3 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 16 Mar 2009 16:32:04 +0100 Subject: Make sure gestures/collidingmice example is build with all gesture examples. --- examples/gestures/gestures.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/gestures/gestures.pro b/examples/gestures/gestures.pro index 1e0f1a7..35164ac 100644 --- a/examples/gestures/gestures.pro +++ b/examples/gestures/gestures.pro @@ -2,7 +2,8 @@ TEMPLATE = \ subdirs SUBDIRS = \ imageviewer \ - graphicsview + graphicsview \ + collidingmice # install target.path = $$[QT_INSTALL_EXAMPLES]/gestures -- cgit v0.12 From 05767badad6408438c6dcffa29318f681522a55a Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 19 Mar 2009 15:29:51 +0100 Subject: Store the QGestureManager in QApplicationPrivate ... since we don't want these to persist after QApplication has been destroyed --- src/gui/kernel/qapplication.cpp | 26 ++++++++++++++++---------- src/gui/kernel/qapplication_p.h | 2 ++ src/gui/kernel/qgesturemanager.cpp | 4 ++-- src/gui/kernel/qgesturemanager_p.h | 2 +- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index bcc25b9..d9a39c7 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -137,8 +137,6 @@ int QApplicationPrivate::autoMaximizeThreshold = -1; bool QApplicationPrivate::autoSipEnabled = false; #endif -Q_GLOBAL_STATIC(QGestureManager, gestureManager); - QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type) : QCoreApplicationPrivate(argc, argv) { @@ -162,6 +160,8 @@ QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::T directPainters = 0; #endif + gestureManager = 0; + if (!self) self = this; } @@ -3709,22 +3709,22 @@ bool QApplication::notify(QObject *receiver, QEvent *e) if (e->spontaneous()) { if (QApplication::testAttribute(Qt::AA_EnableGestures)) { QWidget *w = static_cast(receiver); - QGestureManager *gm = gestureManager(); // if we are in gesture mode, we send all mouse events // directly to gesture recognizer. - if (gm->inGestureMode()) { - if (gm->filterEvent(e)) + if (!d->gestureManager) + d->gestureManager = new QGestureManager(this); + if (d->gestureManager->inGestureMode()) { + if (d->gestureManager->filterEvent(e)) return true; } else { - QMouseEvent* mouse = static_cast(e); if (w && (mouse->type() != QEvent::MouseMove || mouse->buttons() != 0)) { // find the gesture target widget QWidget *target = w; while (target && target->d_func()->gestures.isEmpty()) target = target->parentWidget(); if (target) { - gm->setGestureTargetWidget(target); - if (gm->filterEvent(e)) + d->gestureManager->setGestureTargetWidget(target); + if (d->gestureManager->filterEvent(e)) return true; } } @@ -5095,7 +5095,10 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) */ void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) { - gestureManager()->addRecognizer(recognizer); + Q_D(QApplication); + if (!d->gestureManager) + d->gestureManager = new QGestureManager(this); + d->gestureManager->addRecognizer(recognizer); } /*! @@ -5105,7 +5108,10 @@ void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) */ void QApplication::removeGestureRecognizer(QGestureRecognizer *recognizer) { - gestureManager()->removeRecognizer(recognizer); + Q_D(QApplication); + if (!d->gestureManager) + return; + d->gestureManager->removeRecognizer(recognizer); } /*! \fn QDecoration &QApplication::qwsDecoration() diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 40cd4ae..a6cb927 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -80,6 +80,7 @@ class QGraphicsSystem; class QInputContext; class QObject; class QWidget; +class QGestureManager; extern bool qt_is_gui_used; #ifndef QT_NO_CLIPBOARD @@ -429,6 +430,7 @@ public: #endif // map number of grabbed widgets (something like refcount) + QGestureManager *gestureManager; QMap grabbedGestures; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 0e2cea8..753d712 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -64,8 +64,8 @@ bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); static const unsigned int maximumGestureRecognitionTimeout = 2000; -QGestureManager::QGestureManager() - : targetWidget(0), state(NotGesture) +QGestureManager::QGestureManager(QObject *parent) + : QObject(parent), targetWidget(0), state(NotGesture) { recognizers << new QDoubleTapGestureRecognizer(); recognizers << new QTapAndHoldGestureRecognizer(); diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 18eed71..2500ecf 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -67,7 +67,7 @@ class Q_GUI_EXPORT QGestureManager : public QObject { Q_OBJECT public: - QGestureManager(); + QGestureManager(QObject *parent); // should be internal void setGestureTargetWidget(QWidget *widget); -- cgit v0.12 From c24887401cf7e22825263e8b50d37ef704a2f90d Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 19 Mar 2009 17:02:15 +0100 Subject: Gesture recognizer don;t create new QGesture object every time anymore, but just fill data on a single qgesture object. --- src/gui/kernel/qgesture.cpp | 61 +++++++++++++++++++------- src/gui/kernel/qgesture.h | 19 ++++++-- src/gui/kernel/qgesture_p.h | 7 +++ src/gui/kernel/qgesturemanager.cpp | 3 ++ src/gui/kernel/qgesturestandardrecognizers.cpp | 52 +++++++++++++--------- src/gui/kernel/qgesturestandardrecognizers_p.h | 4 ++ 6 files changed, 106 insertions(+), 40 deletions(-) diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index 76101f2..ccda805 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -71,16 +71,6 @@ QString qt_getStandardGestureTypeName(Qt::GestureType type); \sa QGestureEvent, QGestureRecognizer */ -/*! \fn QString QGesture::gestureType() const - - Returns the type of the gesture. -*/ - -/*! \fn Qt::GestureState QGesture::state() const - - Returns the current state of the gesture. -*/ - /*! Creates a new gesture object of type \a type in a \a state and marks it as a child of \a parent. @@ -89,8 +79,11 @@ QString qt_getStandardGestureTypeName(Qt::GestureType type); QGestureRecognizer classes. */ QGesture::QGesture(QObject *parent, const QString &type, Qt::GestureState state) - : QObject(*new QGesturePrivate, parent), gestureType_(type), gestureState_(state) + : QObject(*new QGesturePrivate, parent) { + Q_D(QGesture); + d->type = type; + d->state = state; } /*! @@ -112,17 +105,23 @@ QGesture::QGesture(QObject *parent, const QString &type, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, const QPoint &hotSpot, const QDateTime &startTime, uint duration, Qt::GestureState state) - : QObject(*new QGesturePrivate, parent), gestureType_(type), gestureState_(state) + : QObject(*new QGesturePrivate, parent) { - d_func()->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); + Q_D(QGesture); + d->type = type; + d->state = state; + d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); } /*! \internal */ QGesture::QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, Qt::GestureState state) - : QObject(dd, parent), gestureType_(type), gestureState_(state) + : QObject(dd, parent) { + Q_D(QGesture); + d->type = type; + d->state = state; } /*! @@ -133,6 +132,23 @@ QGesture::~QGesture() } /*! + Returns the type of the gesture. +*/ +QString QGesture::gestureType() const +{ + return d_func()->type; +} + + +/*! + Returns the current state of the gesture. +*/ +Qt::GestureState QGesture::state() const +{ + return d_func()->state; +} + +/*! Translates the internal gesture properties that represent coordinates by \a offset. @@ -231,6 +247,19 @@ QPoint QGesture::pos() const properties. */ +/*! \internal +*/ +QPanningGesture::QPanningGesture(QObject *parent) + : QGesture(parent, qt_getStandardGestureTypeName(Qt::PanGesture)) +{ +} + +/*! \internal +*/ +QPanningGesture::~QPanningGesture() +{ +} + /*! \property QPanningGesture::lastDirection @@ -238,7 +267,7 @@ QPoint QGesture::pos() const */ Qt::DirectionType QPanningGesture::lastDirection() const { - return qVariantValue(property("lastDirection")); + return d_func()->lastDirection; } /*! @@ -248,7 +277,7 @@ Qt::DirectionType QPanningGesture::lastDirection() const */ Qt::DirectionType QPanningGesture::direction() const { - return qVariantValue(property("direction")); + return d_func()->direction; } QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index d4b2165..9f58f7a 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -61,6 +61,9 @@ class Q_GUI_EXPORT QGesture : public QObject Q_OBJECT Q_DECLARE_PRIVATE(QGesture) + Q_PROPERTY(QString gestureType READ gestureType) + Q_PROPERTY(Qt::GestureState state READ state) + Q_PROPERTY(QRect rect READ rect) Q_PROPERTY(QPoint hotSpot READ hotSpot) Q_PROPERTY(QDateTime startTime READ startTime) @@ -79,8 +82,8 @@ public: uint duration, Qt::GestureState state); virtual ~QGesture(); - inline QString gestureType() const { return gestureType_; } - inline Qt::GestureState state() const { return gestureState_; } + QString gestureType() const; + Qt::GestureState state() const; QRect rect() const; QPoint hotSpot() const; @@ -96,8 +99,9 @@ protected: virtual void translate(const QPoint &offset); private: - QString gestureType_; - Qt::GestureState gestureState_; + friend class QGestureRecognizerPan; + friend class QDoubleTapGestureRecognizer; + friend class QTapAndHoldGestureRecognizer; }; class QPanningGesturePrivate; @@ -112,9 +116,16 @@ class Q_GUI_EXPORT QPanningGesture : public QGesture public: Qt::DirectionType lastDirection() const; Qt::DirectionType direction() const; + +private: + QPanningGesture(QObject *parent = 0); + ~QPanningGesture(); + + friend class QGestureRecognizerPan; }; Q_DECLARE_METATYPE(Qt::DirectionType) +Q_DECLARE_METATYPE(Qt::GestureState) QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 24350d4..cf4e760 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -82,6 +82,9 @@ public: this->pos = pos; } + QString type; + Qt::GestureState state; + QRect rect; QPoint hotSpot; QDateTime startTime; @@ -94,6 +97,10 @@ public: class QPanningGesturePrivate : public QGesturePrivate { Q_DECLARE_PUBLIC(QPanningGesture) + +public: + Qt::DirectionType lastDirection; + Qt::DirectionType direction; }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 753d712..97f73b2 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -67,6 +67,9 @@ static const unsigned int maximumGestureRecognitionTimeout = 2000; QGestureManager::QGestureManager(QObject *parent) : QObject(parent), targetWidget(0), state(NotGesture) { + qRegisterMetaType(); + qRegisterMetaType(); + recognizers << new QDoubleTapGestureRecognizer(); recognizers << new QTapAndHoldGestureRecognizer(); recognizers << new QGestureRecognizerPan(); diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index 82a0a6f..0fa5201 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -79,13 +79,12 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType) // QGestureRecognizerPan::QGestureRecognizerPan() - : QGestureRecognizer(QString()), mousePressed(false), gestureFinished(false), + : QGestureRecognizer(QString()), + mousePressed(false), gestureFinished(false), lastDirection(Qt::NoDirection), currentDirection(Qt::NoDirection) { Q_D(QGestureRecognizer); d->gestureType = Qt::PanGesture; - - qRegisterMetaType(); } QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *event) @@ -145,14 +144,16 @@ QGesture* QGestureRecognizerPan::getGesture() { if (currentDirection == Qt::NoDirection) return 0; - QGesture *g = new QGesture(this, qt_getStandardGestureTypeName(Qt::PanGesture), - pressedPos, lastPos, currentPos, - QRect(), pressedPos, QDateTime(), 0, - gestureFinished ? Qt::GestureFinished : Qt::GestureStarted); - g->setProperty("lastDirection", QVariant::fromValue(lastDirection)); - g->setProperty("direction", QVariant::fromValue(currentDirection)); + QPanningGesturePrivate *d = gesture.d_func(); + d->startPos = pressedPos; + d->lastPos = lastPos; + d->pos = currentPos; + d->hotSpot = pressedPos; + d->state = gestureFinished ? Qt::GestureFinished : Qt::GestureStarted; + d->lastDirection = lastDirection; + d->direction = currentDirection; - return g; + return &gesture; } void QGestureRecognizerPan::reset() @@ -177,7 +178,8 @@ void QGestureRecognizerPan::internalReset() // QDoubleTapGestureRecognizer // QDoubleTapGestureRecognizer::QDoubleTapGestureRecognizer() - : QGestureRecognizer(QString()) + : QGestureRecognizer(QString()), + gesture(0, qt_getStandardGestureTypeName(Qt::DoubleTapGesture)) { Q_D(QGestureRecognizer); d->gestureType = Qt::DoubleTapGesture; @@ -207,9 +209,13 @@ QGestureRecognizer::Result QDoubleTapGestureRecognizer::filterEvent(const QEvent QGesture* QDoubleTapGestureRecognizer::getGesture() { - return new QGesture(this, qt_getStandardGestureTypeName(Qt::DoubleTapGesture), - pressedPosition, pressedPosition, pressedPosition, - QRect(), pressedPosition, QDateTime(), 0, Qt::GestureFinished); + QGesturePrivate *d = gesture.d_func(); + d->startPos = pressedPosition; + d->lastPos = pressedPosition; + d->pos = pressedPosition; + d->hotSpot = pressedPosition; + d->state = Qt::GestureFinished; + return &gesture; } void QDoubleTapGestureRecognizer::reset() @@ -224,7 +230,8 @@ const int QTapAndHoldGestureRecognizer::iterationCount = 40; const int QTapAndHoldGestureRecognizer::iterationTimeout = 50; QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer() - : QGestureRecognizer(QString()), iteration(0) + : QGestureRecognizer(QString()), iteration(0), + gesture(0, qt_getStandardGestureTypeName(Qt::TapAndHoldGesture)) { Q_D(QGestureRecognizer); d->gestureType = Qt::TapAndHoldGesture; @@ -266,11 +273,16 @@ void QTapAndHoldGestureRecognizer::timerEvent(QTimerEvent *event) QGesture* QTapAndHoldGestureRecognizer::getGesture() { - return new QGesture(this, qt_getStandardGestureTypeName(Qt::TapAndHoldGesture), - pressedPosition, pressedPosition, pressedPosition, - QRect(), pressedPosition, QDateTime(), 0, - iteration >= QTapAndHoldGestureRecognizer::iterationCount ? - Qt::GestureFinished : Qt::GestureStarted); + QGesturePrivate *d = gesture.d_func(); + d->startPos = pressedPosition; + d->lastPos = pressedPosition; + d->pos = pressedPosition; + d->hotSpot = pressedPosition; + if (iteration >= QTapAndHoldGestureRecognizer::iterationCount) + d->state = Qt::GestureFinished; + else + d->state = Qt::GestureStarted; + return &gesture; } void QTapAndHoldGestureRecognizer::reset() diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h index 8f1361f..5163bd6 100644 --- a/src/gui/kernel/qgesturestandardrecognizers_p.h +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -75,6 +75,8 @@ public: private: void internalReset(); + QPanningGesture gesture; + QPoint pressedPos; QPoint lastPos; QPoint currentPos; @@ -97,6 +99,7 @@ public: void reset(); private: + QGesture gesture; QPoint pressedPosition; }; @@ -114,6 +117,7 @@ protected: void timerEvent(QTimerEvent *event); private: + QGesture gesture; QPoint pressedPosition; QBasicTimer timer; int iteration; -- cgit v0.12 From 0fde21aa44289f367f11e79500310a54db1787bb Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 19 Mar 2009 17:16:12 +0100 Subject: Added coordinate translation for points inside gesture event when propagating event. --- src/gui/kernel/qgesture.h | 1 + src/gui/kernel/qgesturemanager.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 9f58f7a..f98b573 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -99,6 +99,7 @@ protected: virtual void translate(const QPoint &offset); private: + friend class QGestureManager; friend class QGestureRecognizerPan; friend class QDoubleTapGestureRecognizer; friend class QTapAndHoldGestureRecognizer; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 97f73b2..173b1a0 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -409,8 +409,13 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, QGestureEvent *event) foreach(const QString &gesture, event->gestureTypes()) eventGestures << qHash(gesture); - while (receiver && (receiver->d_func()->gestures & eventGestures).isEmpty()) + QPoint offset; + while (receiver && (receiver->d_func()->gestures & eventGestures).isEmpty()) { + offset += receiver->pos(); receiver = receiver->parentWidget(); + } + foreach(QGesture *gesture, event->gestures()) + gesture->translate(offset); return receiver ? qt_sendSpontaneousEvent(receiver, event) : false; } -- cgit v0.12 From d4e64228ef8154a1f69f91cfd6a78f29d89718e1 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 20 Mar 2009 15:24:50 +0100 Subject: Gesture manager takes ownership of the provided gesture recognizers. --- src/gui/kernel/qapplication.cpp | 2 ++ src/gui/kernel/qgesturemanager.cpp | 7 ++++--- src/gui/kernel/qgesturerecognizer.cpp | 6 +++--- src/gui/kernel/qgesturerecognizer.h | 2 +- src/gui/kernel/qgesturestandardrecognizers.cpp | 12 ++++++------ src/gui/kernel/qgesturestandardrecognizers_p.h | 6 +++--- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index d9a39c7..b042597 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -5091,6 +5091,8 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) /*! Adds custom gesture \a recognizer object. + Qt takes ownership of the provided \a recognizer. + \sa Qt::AA_EnableGestures, QGestureEvent */ void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 173b1a0..3e3f976 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -70,9 +70,9 @@ QGestureManager::QGestureManager(QObject *parent) qRegisterMetaType(); qRegisterMetaType(); - recognizers << new QDoubleTapGestureRecognizer(); - recognizers << new QTapAndHoldGestureRecognizer(); - recognizers << new QGestureRecognizerPan(); + recognizers << new QDoubleTapGestureRecognizer(this); + recognizers << new QTapAndHoldGestureRecognizer(this); + recognizers << new QGestureRecognizerPan(this); foreach(QGestureRecognizer *r, recognizers) connect(r, SIGNAL(stateChanged(QGestureRecognizer::Result)), @@ -81,6 +81,7 @@ QGestureManager::QGestureManager(QObject *parent) void QGestureManager::addRecognizer(QGestureRecognizer *recognizer) { + recognizer->setParent(this); recognizers << recognizer; } diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp index 7791ad7..278389a 100644 --- a/src/gui/kernel/qgesturerecognizer.cpp +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -125,13 +125,13 @@ QGestureRecognizerPrivate::QGestureRecognizerPrivate() /*! Creates a new gesture recognizer object that handles gestures of - the specific \a type. + the specific \a type as a child of \a parent. \sa QApplication::addGestureRecognizer(), QApplication::removeGestureRecognizer(), */ -QGestureRecognizer::QGestureRecognizer(const QString &type) - : QObject(*new QGestureRecognizerPrivate, 0) +QGestureRecognizer::QGestureRecognizer(const QString &type, QObject *parent) + : QObject(*new QGestureRecognizerPrivate, parent) { d_func()->customGestureType = type; } diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h index 9ffdb0d..9e51e7e 100644 --- a/src/gui/kernel/qgesturerecognizer.h +++ b/src/gui/kernel/qgesturerecognizer.h @@ -64,7 +64,7 @@ public: MaybeGesture }; - explicit QGestureRecognizer(const QString &type); + explicit QGestureRecognizer(const QString &type, QObject *parent = 0); QString gestureType() const; diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index 0fa5201..cb448c5 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -78,8 +78,8 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType) // QGestureRecognizerPan // -QGestureRecognizerPan::QGestureRecognizerPan() - : QGestureRecognizer(QString()), +QGestureRecognizerPan::QGestureRecognizerPan(QObject *parent) + : QGestureRecognizer(QString(), parent), mousePressed(false), gestureFinished(false), lastDirection(Qt::NoDirection), currentDirection(Qt::NoDirection) { @@ -177,8 +177,8 @@ void QGestureRecognizerPan::internalReset() // // QDoubleTapGestureRecognizer // -QDoubleTapGestureRecognizer::QDoubleTapGestureRecognizer() - : QGestureRecognizer(QString()), +QDoubleTapGestureRecognizer::QDoubleTapGestureRecognizer(QObject *parent) + : QGestureRecognizer(QString(), parent), gesture(0, qt_getStandardGestureTypeName(Qt::DoubleTapGesture)) { Q_D(QGestureRecognizer); @@ -229,8 +229,8 @@ void QDoubleTapGestureRecognizer::reset() const int QTapAndHoldGestureRecognizer::iterationCount = 40; const int QTapAndHoldGestureRecognizer::iterationTimeout = 50; -QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer() - : QGestureRecognizer(QString()), iteration(0), +QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer(QObject *parent) + : QGestureRecognizer(QString(), parent), iteration(0), gesture(0, qt_getStandardGestureTypeName(Qt::TapAndHoldGesture)) { Q_D(QGestureRecognizer); diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h index 5163bd6..a7a631c 100644 --- a/src/gui/kernel/qgesturestandardrecognizers_p.h +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -66,7 +66,7 @@ class QGestureRecognizerPan : public QGestureRecognizer { Q_OBJECT public: - QGestureRecognizerPan(); + QGestureRecognizerPan(QObject *parent); QGestureRecognizer::Result filterEvent(const QEvent *event); QGesture* getGesture(); @@ -92,7 +92,7 @@ class QDoubleTapGestureRecognizer : public QGestureRecognizer { Q_OBJECT public: - QDoubleTapGestureRecognizer(); + QDoubleTapGestureRecognizer(QObject *parent); QGestureRecognizer::Result filterEvent(const QEvent *event); QGesture* getGesture(); @@ -107,7 +107,7 @@ class QTapAndHoldGestureRecognizer : public QGestureRecognizer { Q_OBJECT public: - QTapAndHoldGestureRecognizer(); + QTapAndHoldGestureRecognizer(QObject *parent); QGestureRecognizer::Result filterEvent(const QEvent *event); QGesture* getGesture(); -- cgit v0.12 From 76fa94daad48382630a7a1b15dc692c6dcad6b93 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 20 Mar 2009 15:33:24 +0100 Subject: Oops, fixed a memory corruption in Pan gesture due to invalid d-pointer type. --- src/gui/kernel/qgesture.cpp | 3 ++- src/gui/kernel/qgesture.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index ccda805..ac135d7 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -250,7 +250,8 @@ QPoint QGesture::pos() const /*! \internal */ QPanningGesture::QPanningGesture(QObject *parent) - : QGesture(parent, qt_getStandardGestureTypeName(Qt::PanGesture)) + : QGesture(*new QPanningGesturePrivate, parent, + qt_getStandardGestureTypeName(Qt::PanGesture)) { } diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index f98b573..a3d9e43 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -95,7 +95,8 @@ public: QPoint pos() const; protected: - QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, Qt::GestureState state); + QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, + Qt::GestureState state = Qt::GestureStarted); virtual void translate(const QPoint &offset); private: -- cgit v0.12 From 20afdd7120872cc3c52b34283a574370db71efab Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 20 Mar 2009 17:26:55 +0100 Subject: Added Qt::GestureUpdated state for the gesture. So in total there are three main states - Started, Updated, Finished. --- doc/src/qnamespace.qdoc | 2 ++ src/corelib/global/qnamespace.h | 6 ++++-- src/gui/kernel/qgesturerecognizer.cpp | 19 +++++++++--------- src/gui/kernel/qgesturestandardrecognizers.cpp | 27 +++++++++++++++----------- src/gui/kernel/qgesturestandardrecognizers_p.h | 2 +- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/doc/src/qnamespace.qdoc b/doc/src/qnamespace.qdoc index c30dc88..dcad96b 100644 --- a/doc/src/qnamespace.qdoc +++ b/doc/src/qnamespace.qdoc @@ -2694,7 +2694,9 @@ This enum type describes the state of a gesture. + \omitvalue NoGesture \value GestureStarted The continuous gesture has started. + \value GestureUpdated The gesture continiues. \value GestureFinished The gesture has finished. \sa QGesture diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 7875fba..b5b5291 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1565,8 +1565,10 @@ public: enum GestureState { - GestureStarted = 0, - GestureFinished = 1 + NoGesture, + GestureStarted = 1, + GestureUpdated = 2, + GestureFinished = 3 }; enum DirectionType diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp index 278389a..c6e1f65 100644 --- a/src/gui/kernel/qgesturerecognizer.cpp +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -73,18 +73,19 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); \value NotGesture Not a gesture. - \value GestureStarted The long-term gesture has started. When the - recognizer is in started state, a gesture event containing - QGesture objects returned by the + \value GestureStarted The continuous gesture has started. When the + recognizer is in this state, a \l{QGestureEvent}{gesture event} + containing QGesture objects returned by the \l{QGestureRecognizer::}{getGesture()} will be sent to a widget. - \value GestureFinished A gesture has ended. The gesture event will - be sent to a widget. + \value GestureFinished The gesture has ended. A + \l{QGestureEvent}{gesture event} will be sent to a widget. - \value MaybeGesture Gesture hasn't started yet, but it might start - soon after next events are received by the recognizer. That means - that gesture manager shouldn't reset() the internal state of the - gesture recognizer. + \value MaybeGesture Gesture recognizer hasn't decided yet if a + gesture has started, but it might start soon after the following + events are received by the recognizer. This means that gesture + manager shouldn't reset() the internal state of the gesture + recognizer. */ /*! \fn QGestureRecognizer::Result QGestureRecognizer::filterEvent(const QEvent *event) diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index cb448c5..04994cf 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -80,7 +80,7 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType) QGestureRecognizerPan::QGestureRecognizerPan(QObject *parent) : QGestureRecognizer(QString(), parent), - mousePressed(false), gestureFinished(false), + mousePressed(false), gestureState(Qt::NoGesture), lastDirection(Qt::NoDirection), currentDirection(Qt::NoDirection) { Q_D(QGestureRecognizer); @@ -103,7 +103,7 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even } else if (event->type() == QEvent::MouseButtonRelease) { if (mousePressed && currentDirection != Qt::NoDirection) { DEBUG() << "Pan: MouseButtonRelease: pan detected"; - gestureFinished = true; + gestureState = Qt::GestureFinished; const QMouseEvent *ev = static_cast(event); currentPos = ev->pos(); internalReset(); @@ -123,12 +123,15 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even DEBUG() << "Pan: MouseMove: simplerecognizer result = " << direction; QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; if (currentDirection == Qt::NoDirection) { - if (direction == Qt::NoDirection) + if (direction == Qt::NoDirection) { result = QGestureRecognizer::MaybeGesture; - else + } else { result = QGestureRecognizer::GestureStarted; + gestureState = Qt::GestureStarted; + } } else { result = QGestureRecognizer::GestureStarted; + gestureState = Qt::GestureUpdated; } if (direction != Qt::NoDirection) { if (currentDirection != direction) @@ -149,7 +152,7 @@ QGesture* QGestureRecognizerPan::getGesture() d->lastPos = lastPos; d->pos = currentPos; d->hotSpot = pressedPos; - d->state = gestureFinished ? Qt::GestureFinished : Qt::GestureStarted; + d->state = gestureState; d->lastDirection = lastDirection; d->direction = currentDirection; @@ -161,7 +164,7 @@ void QGestureRecognizerPan::reset() mousePressed = false; lastDirection = Qt::NoDirection; currentDirection = Qt::NoDirection; - gestureFinished = false; + gestureState = Qt::NoGesture; diagonalRecognizer.reset(); simpleRecognizer.reset(); } @@ -230,8 +233,9 @@ const int QTapAndHoldGestureRecognizer::iterationCount = 40; const int QTapAndHoldGestureRecognizer::iterationTimeout = 50; QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer(QObject *parent) - : QGestureRecognizer(QString(), parent), iteration(0), - gesture(0, qt_getStandardGestureTypeName(Qt::TapAndHoldGesture)) + : QGestureRecognizer(QString(), parent), + gesture(0, qt_getStandardGestureTypeName(Qt::TapAndHoldGesture)), + iteration(0) { Q_D(QGestureRecognizer); d->gestureType = Qt::TapAndHoldGesture; @@ -263,12 +267,13 @@ void QTapAndHoldGestureRecognizer::timerEvent(QTimerEvent *event) { if (event->timerId() != timer.timerId()) return; - if (++iteration == QTapAndHoldGestureRecognizer::iterationCount) { - emit stateChanged(QGestureRecognizer::GestureFinished); + if (iteration == QTapAndHoldGestureRecognizer::iterationCount) { timer.stop(); + emit stateChanged(QGestureRecognizer::GestureFinished); } else { emit stateChanged(QGestureRecognizer::GestureStarted); } + ++iteration; } QGesture* QTapAndHoldGestureRecognizer::getGesture() @@ -281,7 +286,7 @@ QGesture* QTapAndHoldGestureRecognizer::getGesture() if (iteration >= QTapAndHoldGestureRecognizer::iterationCount) d->state = Qt::GestureFinished; else - d->state = Qt::GestureStarted; + d->state = iteration == 0 ? Qt::GestureStarted : Qt::GestureUpdated; return &gesture; } diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h index a7a631c..81db3e6 100644 --- a/src/gui/kernel/qgesturestandardrecognizers_p.h +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -81,7 +81,7 @@ private: QPoint lastPos; QPoint currentPos; bool mousePressed; - bool gestureFinished; + Qt::GestureState gestureState; Qt::DirectionType lastDirection; Qt::DirectionType currentDirection; QDirectionDiagonalRecognizer diagonalRecognizer; -- cgit v0.12 From 9fe44fba91199c9df4a10a118363168ffe3b6b0e Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 20 Mar 2009 17:35:53 +0100 Subject: Small rename of internal variable in gesture recognizer. --- src/gui/kernel/qgesturestandardrecognizers.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index 04994cf..00b4a39 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -118,12 +118,12 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even const QMouseEvent *ev = static_cast(event); lastPos = currentPos; currentPos = ev->pos(); - Qt::DirectionType direction = + Qt::DirectionType newDirection = simpleRecognizer.addPosition(ev->pos()).direction; - DEBUG() << "Pan: MouseMove: simplerecognizer result = " << direction; + DEBUG() << "Pan: MouseMove: simplerecognizer result = " << newDirection; QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; if (currentDirection == Qt::NoDirection) { - if (direction == Qt::NoDirection) { + if (newDirection == Qt::NoDirection) { result = QGestureRecognizer::MaybeGesture; } else { result = QGestureRecognizer::GestureStarted; @@ -133,10 +133,10 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even result = QGestureRecognizer::GestureStarted; gestureState = Qt::GestureUpdated; } - if (direction != Qt::NoDirection) { - if (currentDirection != direction) + if (newDirection != Qt::NoDirection) { + if (currentDirection != newDirection) lastDirection = currentDirection; - currentDirection = direction; + currentDirection = newDirection; } return result; } -- cgit v0.12 From 3661fb3fdafc450cd634e1f22912c87b36ce301d Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 20 Mar 2009 17:49:01 +0100 Subject: renamed QGesture::gestureType() to just type() and fixed the doc. --- src/gui/graphicsview/qgraphicssceneevent.cpp | 2 +- src/gui/kernel/qevent.cpp | 2 +- src/gui/kernel/qgesture.cpp | 10 +++++++--- src/gui/kernel/qgesture.h | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index b26f8cf..86825b1 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1887,7 +1887,7 @@ QPolygonF QGraphicsSceneGestureEvent::mapToItem(const QRect &rect, QGraphicsItem void QGraphicsSceneGestureEvent::setGestures(const QList &gestures) { foreach(QGesture *g, gestures) - m_gestures.insert(g->gestureType(), g); + m_gestures.insert(g->type(), g); } class QGraphicsSceneTouchEventPrivate : public QGraphicsSceneEventPrivate diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 23a45b5..f24f186 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3537,7 +3537,7 @@ QGestureEvent::QGestureEvent(const QList &gestures, : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) { foreach(QGesture *r, gestures) - m_gestures.insert(r->gestureType(), r); + m_gestures.insert(r->type(), r); } /*! diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index ac135d7..4d492f8 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -132,16 +132,20 @@ QGesture::~QGesture() } /*! - Returns the type of the gesture. + \property QGesture::type + + \brief The type of the gesture. */ -QString QGesture::gestureType() const +QString QGesture::type() const { return d_func()->type; } /*! - Returns the current state of the gesture. + \property QGesture::state + + \brief The current state of the gesture. */ Qt::GestureState QGesture::state() const { diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index a3d9e43..2a92588 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -61,7 +61,7 @@ class Q_GUI_EXPORT QGesture : public QObject Q_OBJECT Q_DECLARE_PRIVATE(QGesture) - Q_PROPERTY(QString gestureType READ gestureType) + Q_PROPERTY(QString type READ type) Q_PROPERTY(Qt::GestureState state READ state) Q_PROPERTY(QRect rect READ rect) @@ -82,7 +82,7 @@ public: uint duration, Qt::GestureState state); virtual ~QGesture(); - QString gestureType() const; + QString type() const; Qt::GestureState state() const; QRect rect() const; -- cgit v0.12 From 65795273a244666e671194545119b8607a53febd Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 24 Mar 2009 08:08:45 +0100 Subject: fix link error on windows --- examples/gestures/collidingmice/linjazaxgesture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gestures/collidingmice/linjazaxgesture.h b/examples/gestures/collidingmice/linjazaxgesture.h index 8a7cb23..92343cf 100644 --- a/examples/gestures/collidingmice/linjazaxgesture.h +++ b/examples/gestures/collidingmice/linjazaxgesture.h @@ -3,7 +3,7 @@ #include -class Q_GUI_EXPORT LinjaZaxGesture : public QGesture +class LinjaZaxGesture : public QGesture { public: enum DirectionType -- cgit v0.12 From 751ca601997ce065df2af46d502cf94492eaabfc Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 24 Mar 2009 08:37:36 +0100 Subject: add qgesture_p.h to the project --- src/gui/kernel/kernel.pri | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index adc2b10..3016afb 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -43,6 +43,7 @@ HEADERS += \ kernel/qwindowdefs.h \ kernel/qkeymapper_p.h \ kernel/qgesture.h \ + kernel/qgesture_p.h \ kernel/qgesturemanager_p.h \ kernel/qgesturerecognizer_p.h \ kernel/qgesturerecognizer.h \ -- cgit v0.12 From 6ee2d8e99253a8ae0930fdf81824bfb76a075188 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 1 Apr 2009 18:38:32 +0200 Subject: Removed the AA_EnableGestures attribute. --- src/gui/kernel/qapplication.cpp | 2 +- src/gui/kernel/qgesturemanager.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index b042597..ca5d77b 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -3707,7 +3707,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QPoint relpos = mouse->pos(); if (e->spontaneous()) { - if (QApplication::testAttribute(Qt::AA_EnableGestures)) { + if (!d->grabbedGestures.isEmpty()) { QWidget *w = static_cast(receiver); // if we are in gesture mode, we send all mouse events // directly to gesture recognizer. diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 3e3f976..54c32ea 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -92,9 +92,6 @@ void QGestureManager::removeRecognizer(QGestureRecognizer *recognizer) bool QGestureManager::filterEvent(QEvent *event) { - if (!QApplication::testAttribute(Qt::AA_EnableGestures)) - return false; - QPoint currentPos; switch (event->type()) { case QEvent::MouseButtonPress: @@ -339,9 +336,6 @@ void QGestureManager::setGestureTargetWidget(QWidget *widget) void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) { - if (!QApplication::testAttribute(Qt::AA_EnableGestures)) - return; - QGestureRecognizer *recognizer = qobject_cast(sender()); if (!recognizer) return; -- cgit v0.12 From ff58f277fdbe83f3ce958dca17da4d90b37d1050 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 17 Apr 2009 15:58:38 +0200 Subject: Removed obsolete Qt::AA_EnableGestures attribute from example apps. --- examples/gestures/collidingmice/main.cpp | 1 - examples/gestures/graphicsview/main.cpp | 1 - examples/gestures/imageviewer/main.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/examples/gestures/collidingmice/main.cpp b/examples/gestures/collidingmice/main.cpp index e8ef13f..4309bc0 100644 --- a/examples/gestures/collidingmice/main.cpp +++ b/examples/gestures/collidingmice/main.cpp @@ -123,7 +123,6 @@ private: int main(int argc, char **argv) { QApplication app(argc, argv); - QApplication::setAttribute(Qt::AA_EnableGestures); app.addGestureRecognizer(new GestureRecognizerLinjaZax); qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); //! [0] diff --git a/examples/gestures/graphicsview/main.cpp b/examples/gestures/graphicsview/main.cpp index 1a40805..5f27a15 100644 --- a/examples/gestures/graphicsview/main.cpp +++ b/examples/gestures/graphicsview/main.cpp @@ -142,7 +142,6 @@ private: int main(int argc, char *argv[]) { QApplication app(argc, argv); - QApplication::setAttribute(Qt::AA_EnableGestures); MainWidget w; w.show(); return app.exec(); diff --git a/examples/gestures/imageviewer/main.cpp b/examples/gestures/imageviewer/main.cpp index 5494b12..bddf929 100644 --- a/examples/gestures/imageviewer/main.cpp +++ b/examples/gestures/imageviewer/main.cpp @@ -75,7 +75,6 @@ void MainWidget::openDirectory(const QString &path) int main(int argc, char *argv[]) { QApplication app(argc, argv); - QApplication::setAttribute(Qt::AA_EnableGestures); MainWidget w; w.show(); -- cgit v0.12 From 0cc862890384929c4c3b07c6dc5787362f3aeb9c Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 17 Apr 2009 21:34:29 +0200 Subject: Implemented delaying and replaying mouse events when gesture is about to start. --- src/gui/kernel/qgesturemanager.cpp | 121 ++++++++++++++++++++++++++++++------- src/gui/kernel/qgesturemanager_p.h | 3 + 2 files changed, 102 insertions(+), 22 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 54c32ea..bb0ee6d 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -62,10 +62,13 @@ QT_BEGIN_NAMESPACE bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); -static const unsigned int maximumGestureRecognitionTimeout = 2000; +static const unsigned int MaximumGestureRecognitionTimeout = 2000; +static const unsigned int DelayedPressTimeout = 300; QGestureManager::QGestureManager(QObject *parent) - : QObject(parent), targetWidget(0), state(NotGesture) + : QObject(parent), targetWidget(0), delayedPressTimer(0), + lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), + state(NotGesture) { qRegisterMetaType(); qRegisterMetaType(); @@ -147,7 +150,7 @@ bool QGestureManager::filterEvent(QEvent *event) } foreach(QGestureRecognizer *r, newMaybeGestures) { if (!maybeGestures.contains(r)) { - int timerId = startTimer(maximumGestureRecognitionTimeout); + int timerId = startTimer(MaximumGestureRecognitionTimeout); if (!timerId) qWarning("QGestureManager: couldn't start timer!"); maybeGestures.insert(r, timerId); @@ -248,7 +251,7 @@ bool QGestureManager::filterEvent(QEvent *event) } foreach(QGestureRecognizer *r, newMaybeGestures) { if (!maybeGestures.contains(r)) { - int timerId = startTimer(maximumGestureRecognitionTimeout); + int timerId = startTimer(MaximumGestureRecognitionTimeout); if (!timerId) qWarning("QGestureManager: couldn't start timer!"); maybeGestures.insert(r, timerId); @@ -296,31 +299,100 @@ bool QGestureManager::filterEvent(QEvent *event) } } + if (delayedPressTimer && state == Gesture) { + DEBUG() << "QGestureManager: gesture started. Forgetting about postponed mouse press event"; + killTimer(delayedPressTimer); + delayedPressTimer = 0; + } else if (delayedPressTimer && (state == NotGesture || + event->type() == QEvent::MouseButtonRelease)) { + // not a gesture or released button too fast, so replay press + // event back. + DEBUG() << "QGestureManager: replaying mouse press event"; + QMap::const_iterator it = maybeGestures.begin(), + e = maybeGestures.end();; + for (; it != e; ++it) { + it.key()->reset(); + killTimer(it.value()); + } + maybeGestures.clear(); + state = NotGesture; + + Q_ASSERT(targetWidget != 0); + QApplication::sendEvent(targetWidget, &lastMousePressEvent); + if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *me = static_cast(event); + QMouseEvent move(QEvent::MouseMove, me->pos(), me->globalPos(), me->button(), + me->buttons(), me->modifiers()); + QApplication::sendEvent(targetWidget, &move); + ret = false; + } + killTimer(delayedPressTimer); + delayedPressTimer = 0; + } else if (state == MaybeGesture && event->type() == QEvent::MouseButtonPress) { + // postpone the press event delivery until we know for + // sure whether it is a gesture. + DEBUG() << "QGestureManager: postponing mouse press event"; + QMouseEvent *me = static_cast(event); + lastMousePressEvent = QMouseEvent(QEvent::MouseButtonPress, me->pos(), + me->globalPos(), me->button(), + me->buttons(), me->modifiers()); + Q_ASSERT(delayedPressTimer == 0); + delayedPressTimer = startTimer(DelayedPressTimeout); + if (!delayedPressTimer) + qWarning("QGestureManager: couldn't start delayed press timer!"); + } + // if we have postponed a mouse press event, postpone all + // following event + if (delayedPressTimer) + ret = true; + lastPos = currentPos; return ret; } void QGestureManager::timerEvent(QTimerEvent *event) { - // sanity checks, remove later - Q_ASSERT((state == Gesture && !activeGestures.isEmpty()) || (state != Gesture && activeGestures.isEmpty())); - - typedef QMap MaybeGestureMap; - for (MaybeGestureMap::iterator it = maybeGestures.begin(), e = maybeGestures.end(); - it != e; ++it) { - if (it.value() == event->timerId()) { - DEBUG() << "QGestureManager: gesture timeout."; - QGestureRecognizer *r = it.key(); - r->reset(); - maybeGestures.erase(it); - killTimer(event->timerId()); - break; + if (event->timerId() == delayedPressTimer) { + DEBUG() << "QGestureManager: replaying mouse press event due to timeout"; + // sanity checks + Q_ASSERT(state != Gesture); + + QMap::const_iterator it = maybeGestures.begin(), + e = maybeGestures.end();; + for (; it != e; ++it) { + it.key()->reset(); + killTimer(it.value()); } - } - - if (state == MaybeGesture && maybeGestures.isEmpty()) { - DEBUG() << "QGestureManager: new state = NotGesture because of timeout"; + maybeGestures.clear(); state = NotGesture; + + // we neither received a mouse release event nor gesture + // started, so we replay stored mouse press event. + QApplication::sendEvent(targetWidget, &lastMousePressEvent); + + killTimer(delayedPressTimer); + delayedPressTimer = 0; + } else { + // sanity checks, remove later + Q_ASSERT((state == Gesture && !activeGestures.isEmpty()) || (state != Gesture && activeGestures.isEmpty())); + + typedef QMap MaybeGestureMap; + for (MaybeGestureMap::iterator it = maybeGestures.begin(), e = maybeGestures.end(); + it != e; ++it) { + if (it.value() == event->timerId()) { + DEBUG() << "QGestureManager: gesture timeout."; + QGestureRecognizer *r = it.key(); + r->reset(); + maybeGestures.erase(it); + killTimer(event->timerId()); + break; + } + } + + if (state == MaybeGesture && maybeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = NotGesture because of timeout"; + state = NotGesture; + } } } @@ -378,7 +450,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) sendGestureEvent(targetWidget, &event); } if (!maybeGestures.contains(recognizer)) { - int timerId = startTimer(maximumGestureRecognitionTimeout); + int timerId = startTimer(MaximumGestureRecognitionTimeout); if (!timerId) qWarning("QGestureManager: couldn't start timer!"); maybeGestures.insert(recognizer, timerId); @@ -396,6 +468,11 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) default: Q_ASSERT(false); } + + if (delayedPressTimer && state == Gesture) { + killTimer(delayedPressTimer); + delayedPressTimer = 0; + } } bool QGestureManager::sendGestureEvent(QWidget *receiver, QGestureEvent *event) diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 2500ecf..ef3cdaf 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -94,6 +94,9 @@ private: QWidget *targetWidget; QPoint lastPos; + int delayedPressTimer; + QMouseEvent lastMousePressEvent; + enum State { Gesture, NotGesture, -- cgit v0.12 From 62229907918ddf1b830a51c8a2613cf2492ae1c7 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 20 Apr 2009 11:44:23 +0200 Subject: Added a QApplication::eventDeliveryDelayForGestures property that specifies a timout for mouse event delivery to allow the gesture framework to successfully recognizer a gesture. --- src/gui/kernel/qapplication.cpp | 27 +++++++++++++++++++++++++++ src/gui/kernel/qapplication.h | 4 ++++ src/gui/kernel/qgesturemanager.cpp | 20 +++++++++++++++----- src/gui/kernel/qgesturemanager_p.h | 5 ++++- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index ca5d77b..f652942 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -5116,6 +5116,33 @@ void QApplication::removeGestureRecognizer(QGestureRecognizer *recognizer) d->gestureManager->removeRecognizer(recognizer); } +/*! + \property QApplication::eventDeliveryDelayForGestures + + Specifies the \a delay before input events are delivered to the + gesture enabled widgets. + + The delay allows to postpone widget's input event handling until + gestures framework can successfully recognize a gesture. + + \sa QWidget::grabGesture +*/ +void QApplication::setEventDeliveryDelayForGestures(int delay) +{ + Q_D(QApplication); + if (!d->gestureManager) + d->gestureManager = new QGestureManager(this); + d->gestureManager->setEventDeliveryDelay(delay); +} + +int QApplication::eventDeliveryDelayForGestures() +{ + Q_D(QApplication); + if (!d->gestureManager) + d->gestureManager = new QGestureManager(this); + return d->gestureManager->eventDeliveryDelay(); +} + /*! \fn QDecoration &QApplication::qwsDecoration() Return the QWSDecoration used for decorating windows. diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index 70458ef..e09ea08 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -107,6 +107,7 @@ class Q_GUI_EXPORT QApplication : public QCoreApplication Q_PROPERTY(int autoMaximizeThreshold READ autoMaximizeThreshold WRITE setAutoMaximizeThreshold) Q_PROPERTY(bool autoSipEnabled READ autoSipEnabled WRITE setAutoSipEnabled) #endif + Q_PROPERTY(int eventDeliveryDelayForGestures READ eventDeliveryDelayForGestures WRITE setEventDeliveryDelayForGestures) public: enum Type { Tty, GuiClient, GuiServer }; @@ -270,6 +271,9 @@ public: void addGestureRecognizer(QGestureRecognizer *recognizer); void removeGestureRecognizer(QGestureRecognizer *recognizer); + void setEventDeliveryDelayForGestures(int delay); + int eventDeliveryDelayForGestures(); + Q_SIGNALS: void lastWindowClosed(); void focusChanged(QWidget *old, QWidget *now); diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index bb0ee6d..1a37a05 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -63,11 +63,10 @@ QT_BEGIN_NAMESPACE bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); static const unsigned int MaximumGestureRecognitionTimeout = 2000; -static const unsigned int DelayedPressTimeout = 300; QGestureManager::QGestureManager(QObject *parent) - : QObject(parent), targetWidget(0), delayedPressTimer(0), - lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), + : QObject(parent), targetWidget(0), eventDeliveryDelayTimeout(300), + delayedPressTimer(0), lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), state(NotGesture) { qRegisterMetaType(); @@ -328,7 +327,8 @@ bool QGestureManager::filterEvent(QEvent *event) } killTimer(delayedPressTimer); delayedPressTimer = 0; - } else if (state == MaybeGesture && event->type() == QEvent::MouseButtonPress) { + } else if (state == MaybeGesture && event->type() == QEvent::MouseButtonPress + && eventDeliveryDelayTimeout) { // postpone the press event delivery until we know for // sure whether it is a gesture. DEBUG() << "QGestureManager: postponing mouse press event"; @@ -337,7 +337,7 @@ bool QGestureManager::filterEvent(QEvent *event) me->globalPos(), me->button(), me->buttons(), me->modifiers()); Q_ASSERT(delayedPressTimer == 0); - delayedPressTimer = startTimer(DelayedPressTimeout); + delayedPressTimer = startTimer(eventDeliveryDelayTimeout); if (!delayedPressTimer) qWarning("QGestureManager: couldn't start delayed press timer!"); } @@ -491,6 +491,16 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, QGestureEvent *event) return receiver ? qt_sendSpontaneousEvent(receiver, event) : false; } +int QGestureManager::eventDeliveryDelay() const +{ + return eventDeliveryDelayTimeout; +} + +void QGestureManager::setEventDeliveryDelay(int ms) +{ + eventDeliveryDelayTimeout = ms; +} + QT_END_NAMESPACE #include "moc_qgesturemanager_p.cpp" diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index ef3cdaf..c22027f 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -69,9 +69,11 @@ class Q_GUI_EXPORT QGestureManager : public QObject public: QGestureManager(QObject *parent); - // should be internal void setGestureTargetWidget(QWidget *widget); + int eventDeliveryDelay() const; + void setEventDeliveryDelay(int ms); + void addRecognizer(QGestureRecognizer *recognizer); void removeRecognizer(QGestureRecognizer *recognizer); @@ -94,6 +96,7 @@ private: QWidget *targetWidget; QPoint lastPos; + int eventDeliveryDelayTimeout; int delayedPressTimer; QMouseEvent lastMousePressEvent; -- cgit v0.12 From 00353a9af82ac40c836a39ec5aa47a7284dea214 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 20 Apr 2009 15:02:31 +0200 Subject: Added pannablewebview example. --- examples/gestures/gestures.pro | 3 +- examples/gestures/pannablewebview/main.cpp | 44 ++++++++++++++++++++++ .../gestures/pannablewebview/pannablewebview.pro | 4 ++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 examples/gestures/pannablewebview/main.cpp create mode 100644 examples/gestures/pannablewebview/pannablewebview.pro diff --git a/examples/gestures/gestures.pro b/examples/gestures/gestures.pro index 35164ac..8bd0acc 100644 --- a/examples/gestures/gestures.pro +++ b/examples/gestures/gestures.pro @@ -3,7 +3,8 @@ TEMPLATE = \ SUBDIRS = \ imageviewer \ graphicsview \ - collidingmice + collidingmice \ + pannablewebview # install target.path = $$[QT_INSTALL_EXAMPLES]/gestures diff --git a/examples/gestures/pannablewebview/main.cpp b/examples/gestures/pannablewebview/main.cpp new file mode 100644 index 0000000..5b7a67a --- /dev/null +++ b/examples/gestures/pannablewebview/main.cpp @@ -0,0 +1,44 @@ +#include +#include + +class PannableWebView : public QWebView +{ +public: + PannableWebView(QWidget *parent = 0) + : QWebView(parent) + { + grabGesture(Qt::PanGesture); + } +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) + { + QGestureEvent *ev = static_cast(event); + if (const QGesture *g = ev->gesture(Qt::PanGesture)) { + if (QWebFrame *frame = page()->mainFrame()) { + QPoint offset = g->pos() - g->lastPos(); + frame->setScrollPosition(frame->scrollPosition() - offset); + } + event->accept(); + } + return true; + } + return QWebView::event(event); + } +}; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QGraphicsScene scene; + QGraphicsView w(&scene); + + QWebView *wv = new PannableWebView; + wv->resize(480, 800); + wv->setUrl(QUrl("http://www.trolltech.com")); + scene.addWidget(wv); + w.show(); + + return app.exec(); +} diff --git a/examples/gestures/pannablewebview/pannablewebview.pro b/examples/gestures/pannablewebview/pannablewebview.pro new file mode 100644 index 0000000..07d8f91 --- /dev/null +++ b/examples/gestures/pannablewebview/pannablewebview.pro @@ -0,0 +1,4 @@ +SOURCES += \ + main.cpp + +QT += webkit -- cgit v0.12 From c63f97389b2a654de76713f20266f45ffc83062d Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 20 Apr 2009 15:13:51 +0200 Subject: Added a dummy button on top of the webview to show how input events are delayed. --- examples/gestures/pannablewebview/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/gestures/pannablewebview/main.cpp b/examples/gestures/pannablewebview/main.cpp index 5b7a67a..7c74bbd 100644 --- a/examples/gestures/pannablewebview/main.cpp +++ b/examples/gestures/pannablewebview/main.cpp @@ -7,6 +7,11 @@ public: PannableWebView(QWidget *parent = 0) : QWebView(parent) { +#if 0 + QPushButton *btn = new QPushButton("Some test button", this); + btn->resize(300, 200); + btn->move(40, 300); +#endif grabGesture(Qt::PanGesture); } protected: -- cgit v0.12 From 7bda9fff3b48d2534fb70cc6a9c4b0b945eaaf12 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 21 Apr 2009 13:56:49 +0200 Subject: Fixed replaying mouse events if gesture starts over non-gesture-enabled subwidget. --- src/gui/kernel/qapplication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index f652942..ef602c6 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -3723,7 +3723,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) while (target && target->d_func()->gestures.isEmpty()) target = target->parentWidget(); if (target) { - d->gestureManager->setGestureTargetWidget(target); + d->gestureManager->setGestureTargetWidget(w); if (d->gestureManager->filterEvent(e)) return true; } -- cgit v0.12 From ec94be7f23bfd9c13e42b9b20f01af69bae83660 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 22 Apr 2009 13:03:53 +0200 Subject: Removed all weird qHash usage for gesture identification. --- src/gui/graphicsview/qgraphicsitem.cpp | 9 ++++---- src/gui/kernel/qapplication.cpp | 25 ++++++++++----------- src/gui/kernel/qapplication_p.h | 4 ++-- src/gui/kernel/qgesturemanager.cpp | 41 ++++++++++++++++++++++++++++------ src/gui/kernel/qgesturemanager_p.h | 12 +++++++++- src/gui/kernel/qwidget.cpp | 35 +++++++++++++++++------------ src/gui/kernel/qwidget_p.h | 1 + 7 files changed, 85 insertions(+), 42 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 0dc227a..3d2d1b6 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -521,6 +521,7 @@ #include #include #include +#include #ifdef Q_WS_X11 #include @@ -5811,10 +5812,7 @@ int QGraphicsItem::grabGesture(Qt::GestureType gesture) */ int QGraphicsItem::grabGesture(const QString &gesture) { - int id = qHash(gesture); - QSet::iterator it = d_ptr->gestures.find(id); - if (it != d_ptr->gestures.end()) - return *it; + int id = QGestureManager::instance()->makeGestureId(gesture); d_ptr->gestures << id; if (d_ptr->scene) d_ptr->scene->d_func()->grabGesture(this, id); @@ -5829,9 +5827,10 @@ int QGraphicsItem::grabGesture(const QString &gesture) */ void QGraphicsItem::releaseGesture(int gestureId) { - d_ptr->gestures.remove(gestureId); if (d_ptr->scene) d_ptr->scene->d_func()->releaseGesture(this, gestureId); + QGestureManager::instance()->releaseGestureId(gestureId); + d_ptr->gestures.remove(gestureId); } /*! diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index ef602c6..39aeaa6 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -137,6 +137,14 @@ int QApplicationPrivate::autoMaximizeThreshold = -1; bool QApplicationPrivate::autoSipEnabled = false; #endif +QGestureManager* QGestureManager::instance() +{ + QApplicationPrivate *d = qApp->d_func(); + if (!d->gestureManager) + d->gestureManager = new QGestureManager(qApp); + return d->gestureManager; +} + QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type) : QCoreApplicationPrivate(argc, argv) { @@ -3711,8 +3719,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QWidget *w = static_cast(receiver); // if we are in gesture mode, we send all mouse events // directly to gesture recognizer. - if (!d->gestureManager) - d->gestureManager = new QGestureManager(this); + (void)QGestureManager::instance(); if (d->gestureManager->inGestureMode()) { if (d->gestureManager->filterEvent(e)) return true; @@ -5098,9 +5105,7 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) { Q_D(QApplication); - if (!d->gestureManager) - d->gestureManager = new QGestureManager(this); - d->gestureManager->addRecognizer(recognizer); + QGestureManager::instance()->addRecognizer(recognizer); } /*! @@ -5129,18 +5134,12 @@ void QApplication::removeGestureRecognizer(QGestureRecognizer *recognizer) */ void QApplication::setEventDeliveryDelayForGestures(int delay) { - Q_D(QApplication); - if (!d->gestureManager) - d->gestureManager = new QGestureManager(this); - d->gestureManager->setEventDeliveryDelay(delay); + QGestureManager::instance()->setEventDeliveryDelay(delay); } int QApplication::eventDeliveryDelayForGestures() { - Q_D(QApplication); - if (!d->gestureManager) - d->gestureManager = new QGestureManager(this); - return d->gestureManager->eventDeliveryDelay(); + return QGestureManager::instance()->eventDeliveryDelay(); } /*! \fn QDecoration &QApplication::qwsDecoration() diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index a6cb927..a375933 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -429,9 +429,9 @@ public: void sendSyntheticEnterLeave(QWidget *widget); #endif - // map number of grabbed widgets (something like refcount) QGestureManager *gestureManager; - QMap grabbedGestures; + // map number of widget subscribed to it> + QMap grabbedGestures; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 1a37a05..b4939c4 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -45,6 +45,7 @@ #include "qapplication.h" #include "qapplication_p.h" +#include "qwidget.h" #include "qwidget_p.h" #include "qgesturestandardrecognizers_p.h" @@ -104,7 +105,7 @@ bool QGestureManager::filterEvent(QEvent *event) default: break; } - const QMap &grabbedGestures = qApp->d_func()->grabbedGestures; + const QMap &grabbedGestures = qApp->d_func()->grabbedGestures; bool ret = false; QSet startedGestures; @@ -120,7 +121,7 @@ bool QGestureManager::filterEvent(QEvent *event) QSet stillMaybeGestures; // try other recognizers. foreach(QGestureRecognizer *r, recognizers) { - if (grabbedGestures.value(qHash(r->gestureType()), 0) <= 0) + if (grabbedGestures.value(r->gestureType(), 0) <= 0) continue; QGestureRecognizer::Result result = r->filterEvent(event); if (result == QGestureRecognizer::GestureStarted) { @@ -213,7 +214,7 @@ bool QGestureManager::filterEvent(QEvent *event) Q_ASSERT(!activeGestures.isEmpty()); foreach(QGestureRecognizer *r, recognizers) { - if (grabbedGestures.value(qHash(r->gestureType()), 0) <= 0) + if (grabbedGestures.value(r->gestureType(), 0) <= 0) continue; QGestureRecognizer::Result result = r->filterEvent(event); if (result == QGestureRecognizer::GestureStarted) { @@ -411,7 +412,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) QGestureRecognizer *recognizer = qobject_cast(sender()); if (!recognizer) return; - if (qApp->d_func()->grabbedGestures.value(qHash(recognizer->gestureType()), 0) <= 0) { + if (qApp->d_func()->grabbedGestures.value(recognizer->gestureType(), 0) <= 0) { recognizer->reset(); return; } @@ -477,12 +478,22 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) bool QGestureManager::sendGestureEvent(QWidget *receiver, QGestureEvent *event) { - QSet eventGestures; + QSet eventGestures; foreach(const QString &gesture, event->gestureTypes()) - eventGestures << qHash(gesture); + eventGestures << gesture; QPoint offset; - while (receiver && (receiver->d_func()->gestures & eventGestures).isEmpty()) { + bool found = false; + while (receiver) { + QSet widgetGestures = receiver->d_func()->gestures; + foreach(int gestureId, widgetGestures) { + if (eventGestures.contains(gestureNameFromId(gestureId))) { + found = true; + break; + } + } + if (found) + break; offset += receiver->pos(); receiver = receiver->parentWidget(); } @@ -501,6 +512,22 @@ void QGestureManager::setEventDeliveryDelay(int ms) eventDeliveryDelayTimeout = ms; } +int QGestureManager::makeGestureId(const QString &name) +{ + gestureIdMap[++lastGestureId] = name; + return lastGestureId; +} + +void QGestureManager::releaseGestureId(int gestureId) +{ + gestureIdMap.remove(gestureId); +} + +QString QGestureManager::gestureNameFromId(int gestureId) const +{ + return gestureIdMap.value(gestureId); +} + QT_END_NAMESPACE #include "moc_qgesturemanager_p.cpp" diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index c22027f..9d6d500 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -53,7 +53,6 @@ // We mean it. // -#include "qwidget.h" #include "qlist.h" #include "qset.h" #include "qevent.h" @@ -63,6 +62,7 @@ QT_BEGIN_NAMESPACE +class QWidget; class Q_GUI_EXPORT QGestureManager : public QObject { Q_OBJECT @@ -80,6 +80,13 @@ public: bool filterEvent(QEvent *event); bool inGestureMode(); + int makeGestureId(const QString &name); + void releaseGestureId(int gestureId); + QString gestureNameFromId(int gestureId) const; + + // declared in qapplication.cpp + static QGestureManager* instance(); + protected: void timerEvent(QTimerEvent *event); @@ -100,6 +107,9 @@ private: int delayedPressTimer; QMouseEvent lastMousePressEvent; + QMap gestureIdMap; + int lastGestureId; + enum State { Gesture, NotGesture, diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 0029809..22c2ea7 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -109,6 +109,7 @@ #include "private/qabstractscrollarea_p.h" #include "private/qgraphicssystem_p.h" +#include "private/qgesturemanager_p.h" // widget/widget data creation count //#define QWIDGET_EXTRA_DEBUG @@ -11032,17 +11033,27 @@ QWindowSurface *QWidget::windowSurface() const int QWidget::grabGesture(const QString &gesture) { Q_D(QWidget); - return d->grabGesture(qHash(gesture)); + return d->grabGesture(QGestureManager::instance()->makeGestureId(gesture)); } -int QWidgetPrivate::grabGesture(int id) +int QWidgetPrivate::grabGesture(int gestureId) { - QSet::iterator it = gestures.find(id); - if (it != gestures.end()) - return *it; - gestures << id; - ++qApp->d_func()->grabbedGestures[id]; - return id; + gestures << gestureId; + ++qApp->d_func()->grabbedGestures[QGestureManager::instance()->gestureNameFromId(gestureId)]; + return gestureId; +} + +bool QWidgetPrivate::releaseGesture(int gestureId) +{ + QApplicationPrivate *qAppPriv = qApp->d_func(); + if (gestures.contains(gestureId)) { + QString name = QGestureManager::instance()->gestureNameFromId(gestureId); + Q_ASSERT(qAppPriv->grabbedGestures[name] > 0); + --qAppPriv->grabbedGestures[name]; + gestures.remove(gestureId); + return true; + } + return false; } /*! @@ -11066,12 +11077,8 @@ int QWidget::grabGesture(Qt::GestureType gesture) void QWidget::releaseGesture(int gestureId) { Q_D(QWidget); - QSet::iterator it = d->gestures.find(gestureId); - if (it != d->gestures.end()) { - Q_ASSERT(qApp->d_func()->grabbedGestures[gestureId] > 0); - --qApp->d_func()->grabbedGestures[gestureId]; - d->gestures.erase(it); - } + if (d->releaseGesture(gestureId)) + QGestureManager::instance()->releaseGestureId(gestureId); } /*! diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index abd7b8a..1ab31e4 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -595,6 +595,7 @@ public: QSet gestures; int grabGesture(int gestureId); + bool releaseGesture(int gestureId); #if defined(Q_WS_X11) || defined (Q_WS_WIN) || defined(Q_WS_MAC) #ifdef Q_WS_MAC -- cgit v0.12 From 9337786ca073b185f82898e10be473ea4177f787 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 22 Apr 2009 15:00:56 +0200 Subject: Allow to start a Pan gesture only with the left mouse button. --- src/gui/kernel/qgesturestandardrecognizers.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index 00b4a39..cf2bb41 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -96,15 +96,19 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even reset(); return QGestureRecognizer::NotGesture; } + if (ev->button() != Qt::LeftButton) { + return QGestureRecognizer::NotGesture; + } DEBUG() << "Pan: MouseButtonPress: maybe gesture started"; mousePressed = true; pressedPos = lastPos = currentPos = ev->pos(); return QGestureRecognizer::MaybeGesture; } else if (event->type() == QEvent::MouseButtonRelease) { - if (mousePressed && currentDirection != Qt::NoDirection) { + const QMouseEvent *ev = static_cast(event); + if (mousePressed && currentDirection != Qt::NoDirection + && ev->button() == Qt::LeftButton) { DEBUG() << "Pan: MouseButtonRelease: pan detected"; gestureState = Qt::GestureFinished; - const QMouseEvent *ev = static_cast(event); currentPos = ev->pos(); internalReset(); return QGestureRecognizer::GestureFinished; -- cgit v0.12 From 01da5e343223fc973a8a88c3b825f9fc9b5fde42 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 23 Apr 2009 16:35:20 +0200 Subject: Added a gesture-enabled browser demo. --- examples/gestures/browser/Info_mac.plist | 43 + examples/gestures/browser/addbookmarkdialog.ui | 98 ++ examples/gestures/browser/autosaver.cpp | 94 ++ examples/gestures/browser/autosaver.h | 76 ++ examples/gestures/browser/bookmarks.cpp | 987 +++++++++++++++ examples/gestures/browser/bookmarks.h | 310 +++++ examples/gestures/browser/bookmarks.ui | 106 ++ examples/gestures/browser/browser.icns | Bin 0 -> 50218 bytes examples/gestures/browser/browser.ico | Bin 0 -> 15374 bytes examples/gestures/browser/browser.pro | 91 ++ examples/gestures/browser/browser.rc | 2 + examples/gestures/browser/browserapplication.cpp | 447 +++++++ examples/gestures/browser/browserapplication.h | 119 ++ examples/gestures/browser/browsermainwindow.cpp | 991 +++++++++++++++ examples/gestures/browser/browsermainwindow.h | 169 +++ examples/gestures/browser/chasewidget.cpp | 142 +++ examples/gestures/browser/chasewidget.h | 85 ++ examples/gestures/browser/cookiejar.cpp | 733 +++++++++++ examples/gestures/browser/cookiejar.h | 204 ++++ examples/gestures/browser/cookies.ui | 106 ++ examples/gestures/browser/cookiesexceptions.ui | 184 +++ examples/gestures/browser/downloaditem.ui | 134 ++ examples/gestures/browser/downloadmanager.cpp | 579 +++++++++ examples/gestures/browser/downloadmanager.h | 162 +++ examples/gestures/browser/downloads.ui | 83 ++ examples/gestures/browser/edittableview.cpp | 78 ++ examples/gestures/browser/edittableview.h | 61 + examples/gestures/browser/edittreeview.cpp | 77 ++ examples/gestures/browser/edittreeview.h | 61 + examples/gestures/browser/history.cpp | 1282 ++++++++++++++++++++ examples/gestures/browser/history.h | 350 ++++++ examples/gestures/browser/history.ui | 106 ++ examples/gestures/browser/main.cpp | 53 + examples/gestures/browser/modelmenu.cpp | 227 ++++ examples/gestures/browser/modelmenu.h | 105 ++ examples/gestures/browser/networkaccessmanager.cpp | 171 +++ examples/gestures/browser/networkaccessmanager.h | 68 ++ examples/gestures/browser/passworddialog.ui | 111 ++ examples/gestures/browser/proxy.ui | 104 ++ examples/gestures/browser/searchlineedit.cpp | 238 ++++ examples/gestures/browser/searchlineedit.h | 103 ++ examples/gestures/browser/settings.cpp | 324 +++++ examples/gestures/browser/settings.h | 74 ++ examples/gestures/browser/settings.ui | 614 ++++++++++ examples/gestures/browser/squeezelabel.cpp | 61 + examples/gestures/browser/squeezelabel.h | 60 + examples/gestures/browser/tabwidget.cpp | 830 +++++++++++++ examples/gestures/browser/tabwidget.h | 224 ++++ examples/gestures/browser/toolbarsearch.cpp | 161 +++ examples/gestures/browser/toolbarsearch.h | 84 ++ examples/gestures/browser/urllineedit.cpp | 340 ++++++ examples/gestures/browser/urllineedit.h | 115 ++ examples/gestures/browser/webview.cpp | 325 +++++ examples/gestures/browser/webview.h | 121 ++ examples/gestures/browser/xbel.cpp | 320 +++++ examples/gestures/browser/xbel.h | 113 ++ 56 files changed, 12906 insertions(+) create mode 100644 examples/gestures/browser/Info_mac.plist create mode 100644 examples/gestures/browser/addbookmarkdialog.ui create mode 100644 examples/gestures/browser/autosaver.cpp create mode 100644 examples/gestures/browser/autosaver.h create mode 100644 examples/gestures/browser/bookmarks.cpp create mode 100644 examples/gestures/browser/bookmarks.h create mode 100644 examples/gestures/browser/bookmarks.ui create mode 100644 examples/gestures/browser/browser.icns create mode 100644 examples/gestures/browser/browser.ico create mode 100644 examples/gestures/browser/browser.pro create mode 100644 examples/gestures/browser/browser.rc create mode 100644 examples/gestures/browser/browserapplication.cpp create mode 100644 examples/gestures/browser/browserapplication.h create mode 100644 examples/gestures/browser/browsermainwindow.cpp create mode 100644 examples/gestures/browser/browsermainwindow.h create mode 100644 examples/gestures/browser/chasewidget.cpp create mode 100644 examples/gestures/browser/chasewidget.h create mode 100644 examples/gestures/browser/cookiejar.cpp create mode 100644 examples/gestures/browser/cookiejar.h create mode 100644 examples/gestures/browser/cookies.ui create mode 100644 examples/gestures/browser/cookiesexceptions.ui create mode 100644 examples/gestures/browser/downloaditem.ui create mode 100644 examples/gestures/browser/downloadmanager.cpp create mode 100644 examples/gestures/browser/downloadmanager.h create mode 100644 examples/gestures/browser/downloads.ui create mode 100644 examples/gestures/browser/edittableview.cpp create mode 100644 examples/gestures/browser/edittableview.h create mode 100644 examples/gestures/browser/edittreeview.cpp create mode 100644 examples/gestures/browser/edittreeview.h create mode 100644 examples/gestures/browser/history.cpp create mode 100644 examples/gestures/browser/history.h create mode 100644 examples/gestures/browser/history.ui create mode 100644 examples/gestures/browser/main.cpp create mode 100644 examples/gestures/browser/modelmenu.cpp create mode 100644 examples/gestures/browser/modelmenu.h create mode 100644 examples/gestures/browser/networkaccessmanager.cpp create mode 100644 examples/gestures/browser/networkaccessmanager.h create mode 100644 examples/gestures/browser/passworddialog.ui create mode 100644 examples/gestures/browser/proxy.ui create mode 100644 examples/gestures/browser/searchlineedit.cpp create mode 100644 examples/gestures/browser/searchlineedit.h create mode 100644 examples/gestures/browser/settings.cpp create mode 100644 examples/gestures/browser/settings.h create mode 100644 examples/gestures/browser/settings.ui create mode 100644 examples/gestures/browser/squeezelabel.cpp create mode 100644 examples/gestures/browser/squeezelabel.h create mode 100644 examples/gestures/browser/tabwidget.cpp create mode 100644 examples/gestures/browser/tabwidget.h create mode 100644 examples/gestures/browser/toolbarsearch.cpp create mode 100644 examples/gestures/browser/toolbarsearch.h create mode 100644 examples/gestures/browser/urllineedit.cpp create mode 100644 examples/gestures/browser/urllineedit.h create mode 100644 examples/gestures/browser/webview.cpp create mode 100644 examples/gestures/browser/webview.h create mode 100644 examples/gestures/browser/xbel.cpp create mode 100644 examples/gestures/browser/xbel.h diff --git a/examples/gestures/browser/Info_mac.plist b/examples/gestures/browser/Info_mac.plist new file mode 100644 index 0000000..5648631 --- /dev/null +++ b/examples/gestures/browser/Info_mac.plist @@ -0,0 +1,43 @@ + + + + + CFBundleIconFile + @ICON@ + CFBundlePackageType + APPL + CFBundleGetInfoString + Created by Qt/QMake + CFBundleIdentifier + com.trolltech.DemoBrowser + CFBundleSignature + ttxt + CFBundleExecutable + @EXECUTABLE@ + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + html + htm + shtml + xht + xhtml + + CFBundleTypeIconFile + @ICON@ + CFBundleTypeName + HTML Document + CFBundleTypeOSTypes + + HTML + + CFBundleTypeRole + Viewer + + + NOTE + DemoBrowser by Nokia Corporation and/or its subsidiary(-ies) + + diff --git a/examples/gestures/browser/addbookmarkdialog.ui b/examples/gestures/browser/addbookmarkdialog.ui new file mode 100644 index 0000000..3460d7b --- /dev/null +++ b/examples/gestures/browser/addbookmarkdialog.ui @@ -0,0 +1,98 @@ + + AddBookmarkDialog + + + + 0 + 0 + 240 + 168 + + + + Add Bookmark + + + + + + Type a name for the bookmark, and choose where to keep it. + + + Qt::PlainText + + + true + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 2 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + + buttonBox + accepted() + AddBookmarkDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddBookmarkDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/examples/gestures/browser/autosaver.cpp b/examples/gestures/browser/autosaver.cpp new file mode 100644 index 0000000..ea8c182 --- /dev/null +++ b/examples/gestures/browser/autosaver.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "autosaver.h" + +#include +#include +#include +#include + +#define AUTOSAVE_IN 1000 * 3 // seconds +#define MAXWAIT 1000 * 15 // seconds + +AutoSaver::AutoSaver(QObject *parent) : QObject(parent) +{ + Q_ASSERT(parent); +} + +AutoSaver::~AutoSaver() +{ + if (m_timer.isActive()) + qWarning() << "AutoSaver: still active when destroyed, changes not saved."; +} + +void AutoSaver::changeOccurred() +{ + if (m_firstChange.isNull()) + m_firstChange.start(); + + if (m_firstChange.elapsed() > MAXWAIT) { + saveIfNeccessary(); + } else { + m_timer.start(AUTOSAVE_IN, this); + } +} + +void AutoSaver::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timer.timerId()) { + saveIfNeccessary(); + } else { + QObject::timerEvent(event); + } +} + +void AutoSaver::saveIfNeccessary() +{ + if (!m_timer.isActive()) + return; + m_timer.stop(); + m_firstChange = QTime(); + if (!QMetaObject::invokeMethod(parent(), "save", Qt::DirectConnection)) { + qWarning() << "AutoSaver: error invoking slot save() on parent"; + } +} + diff --git a/examples/gestures/browser/autosaver.h b/examples/gestures/browser/autosaver.h new file mode 100644 index 0000000..e6f2b79 --- /dev/null +++ b/examples/gestures/browser/autosaver.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUTOSAVER_H +#define AUTOSAVER_H + +#include +#include +#include + +/* + This class will call the save() slot on the parent object when the parent changes. + It will wait several seconds after changed() to combining multiple changes and + prevent continuous writing to disk. + */ +class AutoSaver : public QObject { + +Q_OBJECT + +public: + AutoSaver(QObject *parent); + ~AutoSaver(); + void saveIfNeccessary(); + +public slots: + void changeOccurred(); + +protected: + void timerEvent(QTimerEvent *event); + +private: + QBasicTimer m_timer; + QTime m_firstChange; + +}; + +#endif // AUTOSAVER_H + diff --git a/examples/gestures/browser/bookmarks.cpp b/examples/gestures/browser/bookmarks.cpp new file mode 100644 index 0000000..56a4d89 --- /dev/null +++ b/examples/gestures/browser/bookmarks.cpp @@ -0,0 +1,987 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bookmarks.h" + +#include "autosaver.h" +#include "browserapplication.h" +#include "history.h" +#include "xbel.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define BOOKMARKBAR "Bookmarks Bar" +#define BOOKMARKMENU "Bookmarks Menu" + +BookmarksManager::BookmarksManager(QObject *parent) + : QObject(parent) + , m_loaded(false) + , m_saveTimer(new AutoSaver(this)) + , m_bookmarkRootNode(0) + , m_bookmarkModel(0) +{ + connect(this, SIGNAL(entryAdded(BookmarkNode *)), + m_saveTimer, SLOT(changeOccurred())); + connect(this, SIGNAL(entryRemoved(BookmarkNode *, int, BookmarkNode *)), + m_saveTimer, SLOT(changeOccurred())); + connect(this, SIGNAL(entryChanged(BookmarkNode *)), + m_saveTimer, SLOT(changeOccurred())); +} + +BookmarksManager::~BookmarksManager() +{ + m_saveTimer->saveIfNeccessary(); +} + +void BookmarksManager::changeExpanded() +{ + m_saveTimer->changeOccurred(); +} + +void BookmarksManager::load() +{ + if (m_loaded) + return; + m_loaded = true; + + QString dir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel"); + if (!QFile::exists(bookmarkFile)) + bookmarkFile = QLatin1String(":defaultbookmarks.xbel"); + + XbelReader reader; + m_bookmarkRootNode = reader.read(bookmarkFile); + if (reader.error() != QXmlStreamReader::NoError) { + QMessageBox::warning(0, QLatin1String("Loading Bookmark"), + tr("Error when loading bookmarks on line %1, column %2:\n" + "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString())); + } + + BookmarkNode *toolbar = 0; + BookmarkNode *menu = 0; + QList others; + for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) { + BookmarkNode *node = m_bookmarkRootNode->children().at(i); + if (node->type() == BookmarkNode::Folder) { + // Automatically convert + if (node->title == tr("Toolbar Bookmarks") && !toolbar) { + node->title = tr(BOOKMARKBAR); + } + if (node->title == tr(BOOKMARKBAR) && !toolbar) { + toolbar = node; + } + + // Automatically convert + if (node->title == tr("Menu") && !menu) { + node->title = tr(BOOKMARKMENU); + } + if (node->title == tr(BOOKMARKMENU) && !menu) { + menu = node; + } + } else { + others.append(node); + } + m_bookmarkRootNode->remove(node); + } + Q_ASSERT(m_bookmarkRootNode->children().count() == 0); + if (!toolbar) { + toolbar = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode); + toolbar->title = tr(BOOKMARKBAR); + } else { + m_bookmarkRootNode->add(toolbar); + } + + if (!menu) { + menu = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode); + menu->title = tr(BOOKMARKMENU); + } else { + m_bookmarkRootNode->add(menu); + } + + for (int i = 0; i < others.count(); ++i) + menu->add(others.at(i)); +} + +void BookmarksManager::save() const +{ + if (!m_loaded) + return; + + XbelWriter writer; + QString dir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel"); + if (!writer.write(bookmarkFile, m_bookmarkRootNode)) + qWarning() << "BookmarkManager: error saving to" << bookmarkFile; +} + +void BookmarksManager::addBookmark(BookmarkNode *parent, BookmarkNode *node, int row) +{ + if (!m_loaded) + return; + Q_ASSERT(parent); + InsertBookmarksCommand *command = new InsertBookmarksCommand(this, parent, node, row); + m_commands.push(command); +} + +void BookmarksManager::removeBookmark(BookmarkNode *node) +{ + if (!m_loaded) + return; + + Q_ASSERT(node); + BookmarkNode *parent = node->parent(); + int row = parent->children().indexOf(node); + RemoveBookmarksCommand *command = new RemoveBookmarksCommand(this, parent, row); + m_commands.push(command); +} + +void BookmarksManager::setTitle(BookmarkNode *node, const QString &newTitle) +{ + if (!m_loaded) + return; + + Q_ASSERT(node); + ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newTitle, true); + m_commands.push(command); +} + +void BookmarksManager::setUrl(BookmarkNode *node, const QString &newUrl) +{ + if (!m_loaded) + return; + + Q_ASSERT(node); + ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newUrl, false); + m_commands.push(command); +} + +BookmarkNode *BookmarksManager::bookmarks() +{ + if (!m_loaded) + load(); + return m_bookmarkRootNode; +} + +BookmarkNode *BookmarksManager::menu() +{ + if (!m_loaded) + load(); + + for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) { + BookmarkNode *node = m_bookmarkRootNode->children().at(i); + if (node->title == tr(BOOKMARKMENU)) + return node; + } + Q_ASSERT(false); + return 0; +} + +BookmarkNode *BookmarksManager::toolbar() +{ + if (!m_loaded) + load(); + + for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) { + BookmarkNode *node = m_bookmarkRootNode->children().at(i); + if (node->title == tr(BOOKMARKBAR)) + return node; + } + Q_ASSERT(false); + return 0; +} + +BookmarksModel *BookmarksManager::bookmarksModel() +{ + if (!m_bookmarkModel) + m_bookmarkModel = new BookmarksModel(this, this); + return m_bookmarkModel; +} + +void BookmarksManager::importBookmarks() +{ + QString fileName = QFileDialog::getOpenFileName(0, tr("Open File"), + QString(), + tr("XBEL (*.xbel *.xml)")); + if (fileName.isEmpty()) + return; + + XbelReader reader; + BookmarkNode *importRootNode = reader.read(fileName); + if (reader.error() != QXmlStreamReader::NoError) { + QMessageBox::warning(0, QLatin1String("Loading Bookmark"), + tr("Error when loading bookmarks on line %1, column %2:\n" + "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString())); + } + + importRootNode->setType(BookmarkNode::Folder); + importRootNode->title = (tr("Imported %1").arg(QDate::currentDate().toString(Qt::SystemLocaleShortDate))); + addBookmark(menu(), importRootNode); +} + +void BookmarksManager::exportBookmarks() +{ + QString fileName = QFileDialog::getSaveFileName(0, tr("Save File"), + tr("%1 Bookmarks.xbel").arg(QCoreApplication::applicationName()), + tr("XBEL (*.xbel *.xml)")); + if (fileName.isEmpty()) + return; + + XbelWriter writer; + if (!writer.write(fileName, m_bookmarkRootNode)) + QMessageBox::critical(0, tr("Export error"), tr("error saving bookmarks")); +} + +RemoveBookmarksCommand::RemoveBookmarksCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *parent, int row) + : QUndoCommand(BookmarksManager::tr("Remove Bookmark")) + , m_row(row) + , m_bookmarkManagaer(m_bookmarkManagaer) + , m_node(parent->children().value(row)) + , m_parent(parent) + , m_done(false) +{ +} + +RemoveBookmarksCommand::~RemoveBookmarksCommand() +{ + if (m_done && !m_node->parent()) { + delete m_node; + } +} + +void RemoveBookmarksCommand::undo() +{ + m_parent->add(m_node, m_row); + emit m_bookmarkManagaer->entryAdded(m_node); + m_done = false; +} + +void RemoveBookmarksCommand::redo() +{ + m_parent->remove(m_node); + emit m_bookmarkManagaer->entryRemoved(m_parent, m_row, m_node); + m_done = true; +} + +InsertBookmarksCommand::InsertBookmarksCommand(BookmarksManager *m_bookmarkManagaer, + BookmarkNode *parent, BookmarkNode *node, int row) + : RemoveBookmarksCommand(m_bookmarkManagaer, parent, row) +{ + setText(BookmarksManager::tr("Insert Bookmark")); + m_node = node; +} + +ChangeBookmarkCommand::ChangeBookmarkCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *node, + const QString &newValue, bool title) + : QUndoCommand() + , m_bookmarkManagaer(m_bookmarkManagaer) + , m_title(title) + , m_newValue(newValue) + , m_node(node) +{ + if (m_title) { + m_oldValue = m_node->title; + setText(BookmarksManager::tr("Name Change")); + } else { + m_oldValue = m_node->url; + setText(BookmarksManager::tr("Address Change")); + } +} + +void ChangeBookmarkCommand::undo() +{ + if (m_title) + m_node->title = m_oldValue; + else + m_node->url = m_oldValue; + emit m_bookmarkManagaer->entryChanged(m_node); +} + +void ChangeBookmarkCommand::redo() +{ + if (m_title) + m_node->title = m_newValue; + else + m_node->url = m_newValue; + emit m_bookmarkManagaer->entryChanged(m_node); +} + +BookmarksModel::BookmarksModel(BookmarksManager *bookmarkManager, QObject *parent) + : QAbstractItemModel(parent) + , m_endMacro(false) + , m_bookmarksManager(bookmarkManager) +{ + connect(bookmarkManager, SIGNAL(entryAdded(BookmarkNode *)), + this, SLOT(entryAdded(BookmarkNode *))); + connect(bookmarkManager, SIGNAL(entryRemoved(BookmarkNode *, int, BookmarkNode *)), + this, SLOT(entryRemoved(BookmarkNode *, int, BookmarkNode *))); + connect(bookmarkManager, SIGNAL(entryChanged(BookmarkNode *)), + this, SLOT(entryChanged(BookmarkNode *))); +} + +QModelIndex BookmarksModel::index(BookmarkNode *node) const +{ + BookmarkNode *parent = node->parent(); + if (!parent) + return QModelIndex(); + return createIndex(parent->children().indexOf(node), 0, node); +} + +void BookmarksModel::entryAdded(BookmarkNode *item) +{ + Q_ASSERT(item && item->parent()); + int row = item->parent()->children().indexOf(item); + BookmarkNode *parent = item->parent(); + // item was already added so remove beore beginInsertRows is called + parent->remove(item); + beginInsertRows(index(parent), row, row); + parent->add(item, row); + endInsertRows(); +} + +void BookmarksModel::entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item) +{ + // item was already removed, re-add so beginRemoveRows works + parent->add(item, row); + beginRemoveRows(index(parent), row, row); + parent->remove(item); + endRemoveRows(); +} + +void BookmarksModel::entryChanged(BookmarkNode *item) +{ + QModelIndex idx = index(item); + emit dataChanged(idx, idx); +} + +bool BookmarksModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || count <= 0 || row + count > rowCount(parent)) + return false; + + BookmarkNode *bookmarkNode = node(parent); + for (int i = row + count - 1; i >= row; --i) { + BookmarkNode *node = bookmarkNode->children().at(i); + if (node == m_bookmarksManager->menu() + || node == m_bookmarksManager->toolbar()) + continue; + + m_bookmarksManager->removeBookmark(node); + } + if (m_endMacro) { + m_bookmarksManager->undoRedoStack()->endMacro(); + m_endMacro = false; + } + return true; +} + +QVariant BookmarksModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case 0: return tr("Title"); + case 1: return tr("Address"); + } + } + return QAbstractItemModel::headerData(section, orientation, role); +} + +QVariant BookmarksModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.model() != this) + return QVariant(); + + const BookmarkNode *bookmarkNode = node(index); + switch (role) { + case Qt::EditRole: + case Qt::DisplayRole: + if (bookmarkNode->type() == BookmarkNode::Separator) { + switch (index.column()) { + case 0: return QString(50, 0xB7); + case 1: return QString(); + } + } + + switch (index.column()) { + case 0: return bookmarkNode->title; + case 1: return bookmarkNode->url; + } + break; + case BookmarksModel::UrlRole: + return QUrl(bookmarkNode->url); + break; + case BookmarksModel::UrlStringRole: + return bookmarkNode->url; + break; + case BookmarksModel::TypeRole: + return bookmarkNode->type(); + break; + case BookmarksModel::SeparatorRole: + return (bookmarkNode->type() == BookmarkNode::Separator); + break; + case Qt::DecorationRole: + if (index.column() == 0) { + if (bookmarkNode->type() == BookmarkNode::Folder) + return QApplication::style()->standardIcon(QStyle::SP_DirIcon); + return BrowserApplication::instance()->icon(bookmarkNode->url); + } + } + + return QVariant(); +} + +int BookmarksModel::columnCount(const QModelIndex &parent) const +{ + return (parent.column() > 0) ? 0 : 2; +} + +int BookmarksModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + return m_bookmarksManager->bookmarks()->children().count(); + + const BookmarkNode *item = static_cast(parent.internalPointer()); + return item->children().count(); +} + +QModelIndex BookmarksModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent)) + return QModelIndex(); + + // get the parent node + BookmarkNode *parentNode = node(parent); + return createIndex(row, column, parentNode->children().at(row)); +} + +QModelIndex BookmarksModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + BookmarkNode *itemNode = node(index); + BookmarkNode *parentNode = (itemNode ? itemNode->parent() : 0); + if (!parentNode || parentNode == m_bookmarksManager->bookmarks()) + return QModelIndex(); + + // get the parent's row + BookmarkNode *grandParentNode = parentNode->parent(); + int parentRow = grandParentNode->children().indexOf(parentNode); + Q_ASSERT(parentRow >= 0); + return createIndex(parentRow, 0, parentNode); +} + +bool BookmarksModel::hasChildren(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return true; + const BookmarkNode *parentNode = node(parent); + return (parentNode->type() == BookmarkNode::Folder); +} + +Qt::ItemFlags BookmarksModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + BookmarkNode *bookmarkNode = node(index); + + if (bookmarkNode != m_bookmarksManager->menu() + && bookmarkNode != m_bookmarksManager->toolbar()) { + flags |= Qt::ItemIsDragEnabled; + if (bookmarkNode->type() != BookmarkNode::Separator) + flags |= Qt::ItemIsEditable; + } + if (hasChildren(index)) + flags |= Qt::ItemIsDropEnabled; + return flags; +} + +Qt::DropActions BookmarksModel::supportedDropActions () const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +#define MIMETYPE QLatin1String("application/bookmarks.xbel") + +QStringList BookmarksModel::mimeTypes() const +{ + QStringList types; + types << MIMETYPE; + return types; +} + +QMimeData *BookmarksModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData(); + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + foreach (QModelIndex index, indexes) { + if (index.column() != 0 || !index.isValid()) + continue; + QByteArray encodedData; + QBuffer buffer(&encodedData); + buffer.open(QBuffer::ReadWrite); + XbelWriter writer; + const BookmarkNode *parentNode = node(index); + writer.write(&buffer, parentNode); + stream << encodedData; + } + mimeData->setData(MIMETYPE, data); + return mimeData; +} + +bool BookmarksModel::dropMimeData(const QMimeData *data, + Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (!data->hasFormat(MIMETYPE) + || column > 0) + return false; + + QByteArray ba = data->data(MIMETYPE); + QDataStream stream(&ba, QIODevice::ReadOnly); + if (stream.atEnd()) + return false; + + QUndoStack *undoStack = m_bookmarksManager->undoRedoStack(); + undoStack->beginMacro(QLatin1String("Move Bookmarks")); + + while (!stream.atEnd()) { + QByteArray encodedData; + stream >> encodedData; + QBuffer buffer(&encodedData); + buffer.open(QBuffer::ReadOnly); + + XbelReader reader; + BookmarkNode *rootNode = reader.read(&buffer); + QList children = rootNode->children(); + for (int i = 0; i < children.count(); ++i) { + BookmarkNode *bookmarkNode = children.at(i); + rootNode->remove(bookmarkNode); + row = qMax(0, row); + BookmarkNode *parentNode = node(parent); + m_bookmarksManager->addBookmark(parentNode, bookmarkNode, row); + m_endMacro = true; + } + delete rootNode; + } + return true; +} + +bool BookmarksModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || (flags(index) & Qt::ItemIsEditable) == 0) + return false; + + BookmarkNode *item = node(index); + + switch (role) { + case Qt::EditRole: + case Qt::DisplayRole: + if (index.column() == 0) { + m_bookmarksManager->setTitle(item, value.toString()); + break; + } + if (index.column() == 1) { + m_bookmarksManager->setUrl(item, value.toString()); + break; + } + return false; + case BookmarksModel::UrlRole: + m_bookmarksManager->setUrl(item, value.toUrl().toString()); + break; + case BookmarksModel::UrlStringRole: + m_bookmarksManager->setUrl(item, value.toString()); + break; + default: + break; + return false; + } + + return true; +} + +BookmarkNode *BookmarksModel::node(const QModelIndex &index) const +{ + BookmarkNode *itemNode = static_cast(index.internalPointer()); + if (!itemNode) + return m_bookmarksManager->bookmarks(); + return itemNode; +} + + +AddBookmarkProxyModel::AddBookmarkProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +int AddBookmarkProxyModel::columnCount(const QModelIndex &parent) const +{ + return qMin(1, QSortFilterProxyModel::columnCount(parent)); +} + +bool AddBookmarkProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); + return sourceModel()->hasChildren(idx); +} + +AddBookmarkDialog::AddBookmarkDialog(const QString &url, const QString &title, QWidget *parent, BookmarksManager *bookmarkManager) + : QDialog(parent) + , m_url(url) + , m_bookmarksManager(bookmarkManager) +{ + setWindowFlags(Qt::Sheet); + if (!m_bookmarksManager) + m_bookmarksManager = BrowserApplication::bookmarksManager(); + setupUi(this); + QTreeView *view = new QTreeView(this); + m_proxyModel = new AddBookmarkProxyModel(this); + BookmarksModel *model = m_bookmarksManager->bookmarksModel(); + m_proxyModel->setSourceModel(model); + view->setModel(m_proxyModel); + view->expandAll(); + view->header()->setStretchLastSection(true); + view->header()->hide(); + view->setItemsExpandable(false); + view->setRootIsDecorated(false); + view->setIndentation(10); + location->setModel(m_proxyModel); + view->show(); + location->setView(view); + BookmarkNode *menu = m_bookmarksManager->menu(); + QModelIndex idx = m_proxyModel->mapFromSource(model->index(menu)); + view->setCurrentIndex(idx); + location->setCurrentIndex(idx.row()); + name->setText(title); +} + +void AddBookmarkDialog::accept() +{ + QModelIndex index = location->view()->currentIndex(); + index = m_proxyModel->mapToSource(index); + if (!index.isValid()) + index = m_bookmarksManager->bookmarksModel()->index(0, 0); + BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(index); + BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark); + bookmark->url = m_url; + bookmark->title = name->text(); + m_bookmarksManager->addBookmark(parent, bookmark); + QDialog::accept(); +} + +BookmarksMenu::BookmarksMenu(QWidget *parent) + : ModelMenu(parent) + , m_bookmarksManager(0) +{ + connect(this, SIGNAL(activated(const QModelIndex &)), + this, SLOT(activated(const QModelIndex &))); + setMaxRows(-1); + setHoverRole(BookmarksModel::UrlStringRole); + setSeparatorRole(BookmarksModel::SeparatorRole); +} + +void BookmarksMenu::activated(const QModelIndex &index) +{ + emit openUrl(index.data(BookmarksModel::UrlRole).toUrl()); +} + +bool BookmarksMenu::prePopulated() +{ + m_bookmarksManager = BrowserApplication::bookmarksManager(); + setModel(m_bookmarksManager->bookmarksModel()); + setRootIndex(m_bookmarksManager->bookmarksModel()->index(1, 0)); + // initial actions + for (int i = 0; i < m_initialActions.count(); ++i) + addAction(m_initialActions.at(i)); + if (!m_initialActions.isEmpty()) + addSeparator(); + createMenu(model()->index(0, 0), 1, this); + return true; +} + +void BookmarksMenu::setInitialActions(QList actions) +{ + m_initialActions = actions; + for (int i = 0; i < m_initialActions.count(); ++i) + addAction(m_initialActions.at(i)); +} + +BookmarksDialog::BookmarksDialog(QWidget *parent, BookmarksManager *manager) + : QDialog(parent) +{ + m_bookmarksManager = manager; + if (!m_bookmarksManager) + m_bookmarksManager = BrowserApplication::bookmarksManager(); + setupUi(this); + + tree->setUniformRowHeights(true); + tree->setSelectionBehavior(QAbstractItemView::SelectRows); + tree->setSelectionMode(QAbstractItemView::ContiguousSelection); + tree->setTextElideMode(Qt::ElideMiddle); + m_bookmarksModel = m_bookmarksManager->bookmarksModel(); + m_proxyModel = new TreeProxyModel(this); + connect(search, SIGNAL(textChanged(QString)), + m_proxyModel, SLOT(setFilterFixedString(QString))); + connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne())); + m_proxyModel->setSourceModel(m_bookmarksModel); + tree->setModel(m_proxyModel); + tree->setDragDropMode(QAbstractItemView::InternalMove); + tree->setExpanded(m_proxyModel->index(0, 0), true); + tree->setAlternatingRowColors(true); + QFontMetrics fm(font()); + int header = fm.width(QLatin1Char('m')) * 40; + tree->header()->resizeSection(0, header); + tree->header()->setStretchLastSection(true); + connect(tree, SIGNAL(activated(const QModelIndex&)), + this, SLOT(open())); + tree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tree, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(customContextMenuRequested(const QPoint &))); + connect(addFolderButton, SIGNAL(clicked()), + this, SLOT(newFolder())); + expandNodes(m_bookmarksManager->bookmarks()); + setAttribute(Qt::WA_DeleteOnClose); +} + +BookmarksDialog::~BookmarksDialog() +{ + if (saveExpandedNodes(tree->rootIndex())) + m_bookmarksManager->changeExpanded(); +} + +bool BookmarksDialog::saveExpandedNodes(const QModelIndex &parent) +{ + bool changed = false; + for (int i = 0; i < m_proxyModel->rowCount(parent); ++i) { + QModelIndex child = m_proxyModel->index(i, 0, parent); + QModelIndex sourceIndex = m_proxyModel->mapToSource(child); + BookmarkNode *childNode = m_bookmarksModel->node(sourceIndex); + bool wasExpanded = childNode->expanded; + if (tree->isExpanded(child)) { + childNode->expanded = true; + changed |= saveExpandedNodes(child); + } else { + childNode->expanded = false; + } + changed |= (wasExpanded != childNode->expanded); + } + return changed; +} + +void BookmarksDialog::expandNodes(BookmarkNode *node) +{ + for (int i = 0; i < node->children().count(); ++i) { + BookmarkNode *childNode = node->children()[i]; + if (childNode->expanded) { + QModelIndex idx = m_bookmarksModel->index(childNode); + idx = m_proxyModel->mapFromSource(idx); + tree->setExpanded(idx, true); + expandNodes(childNode); + } + } +} + +void BookmarksDialog::customContextMenuRequested(const QPoint &pos) +{ + QMenu menu; + QModelIndex index = tree->indexAt(pos); + index = index.sibling(index.row(), 0); + if (index.isValid() && !tree->model()->hasChildren(index)) { + menu.addAction(tr("Open"), this, SLOT(open())); + menu.addSeparator(); + } + menu.addAction(tr("Delete"), tree, SLOT(removeOne())); + menu.exec(QCursor::pos()); +} + +void BookmarksDialog::open() +{ + QModelIndex index = tree->currentIndex(); + if (!index.parent().isValid()) + return; + emit openUrl(index.sibling(index.row(), 1).data(BookmarksModel::UrlRole).toUrl()); +} + +void BookmarksDialog::newFolder() +{ + QModelIndex currentIndex = tree->currentIndex(); + QModelIndex idx = currentIndex; + if (idx.isValid() && !idx.model()->hasChildren(idx)) + idx = idx.parent(); + if (!idx.isValid()) + idx = tree->rootIndex(); + idx = m_proxyModel->mapToSource(idx); + BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(idx); + BookmarkNode *node = new BookmarkNode(BookmarkNode::Folder); + node->title = tr("New Folder"); + m_bookmarksManager->addBookmark(parent, node, currentIndex.row() + 1); +} + +BookmarksToolBar::BookmarksToolBar(BookmarksModel *model, QWidget *parent) + : QToolBar(tr("Bookmark"), parent) + , m_bookmarksModel(model) +{ + connect(this, SIGNAL(actionTriggered(QAction*)), this, SLOT(triggered(QAction*))); + setRootIndex(model->index(0, 0)); + connect(m_bookmarksModel, SIGNAL(modelReset()), this, SLOT(build())); + connect(m_bookmarksModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(build())); + connect(m_bookmarksModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(build())); + connect(m_bookmarksModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(build())); + setAcceptDrops(true); +} + +void BookmarksToolBar::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *mimeData = event->mimeData(); + if (mimeData->hasUrls()) + event->acceptProposedAction(); + QToolBar::dragEnterEvent(event); +} + +void BookmarksToolBar::dropEvent(QDropEvent *event) +{ + const QMimeData *mimeData = event->mimeData(); + if (mimeData->hasUrls() && mimeData->hasText()) { + QList urls = mimeData->urls(); + QAction *action = actionAt(event->pos()); + QString dropText; + if (action) + dropText = action->text(); + int row = -1; + QModelIndex parentIndex = m_root; + for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) { + QModelIndex idx = m_bookmarksModel->index(i, 0, m_root); + QString title = idx.data().toString(); + if (title == dropText) { + row = i; + if (m_bookmarksModel->hasChildren(idx)) { + parentIndex = idx; + row = -1; + } + break; + } + } + BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark); + bookmark->url = urls.at(0).toString(); + bookmark->title = mimeData->text(); + + BookmarkNode *parent = m_bookmarksModel->node(parentIndex); + BookmarksManager *bookmarksManager = m_bookmarksModel->bookmarksManager(); + bookmarksManager->addBookmark(parent, bookmark, row); + event->acceptProposedAction(); + } + QToolBar::dropEvent(event); +} + + +void BookmarksToolBar::setRootIndex(const QModelIndex &index) +{ + m_root = index; + build(); +} + +QModelIndex BookmarksToolBar::rootIndex() const +{ + return m_root; +} + +void BookmarksToolBar::build() +{ + clear(); + for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) { + QModelIndex idx = m_bookmarksModel->index(i, 0, m_root); + if (m_bookmarksModel->hasChildren(idx)) { + QToolButton *button = new QToolButton(this); + button->setPopupMode(QToolButton::InstantPopup); + button->setArrowType(Qt::DownArrow); + button->setText(idx.data().toString()); + ModelMenu *menu = new ModelMenu(this); + connect(menu, SIGNAL(activated(const QModelIndex &)), + this, SLOT(activated(const QModelIndex &))); + menu->setModel(m_bookmarksModel); + menu->setRootIndex(idx); + menu->addAction(new QAction(menu)); + button->setMenu(menu); + button->setToolButtonStyle(Qt::ToolButtonTextOnly); + QAction *a = addWidget(button); + a->setText(idx.data().toString()); + } else { + QAction *action = addAction(idx.data().toString()); + action->setData(idx.data(BookmarksModel::UrlRole)); + } + } +} + +void BookmarksToolBar::triggered(QAction *action) +{ + QVariant v = action->data(); + if (v.canConvert()) { + emit openUrl(v.toUrl()); + } +} + +void BookmarksToolBar::activated(const QModelIndex &index) +{ + emit openUrl(index.data(BookmarksModel::UrlRole).toUrl()); +} + diff --git a/examples/gestures/browser/bookmarks.h b/examples/gestures/browser/bookmarks.h new file mode 100644 index 0000000..b25ce8d --- /dev/null +++ b/examples/gestures/browser/bookmarks.h @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BOOKMARKS_H +#define BOOKMARKS_H + +#include +#include + +#include + +/*! + Bookmark manager, owner of the bookmarks, loads, saves and basic tasks + */ +class AutoSaver; +class BookmarkNode; +class BookmarksModel; +class BookmarksManager : public QObject +{ + Q_OBJECT + +signals: + void entryAdded(BookmarkNode *item); + void entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item); + void entryChanged(BookmarkNode *item); + +public: + BookmarksManager(QObject *parent = 0); + ~BookmarksManager(); + + void addBookmark(BookmarkNode *parent, BookmarkNode *node, int row = -1); + void removeBookmark(BookmarkNode *node); + void setTitle(BookmarkNode *node, const QString &newTitle); + void setUrl(BookmarkNode *node, const QString &newUrl); + void changeExpanded(); + + BookmarkNode *bookmarks(); + BookmarkNode *menu(); + BookmarkNode *toolbar(); + + BookmarksModel *bookmarksModel(); + QUndoStack *undoRedoStack() { return &m_commands; }; + +public slots: + void importBookmarks(); + void exportBookmarks(); + +private slots: + void save() const; + +private: + void load(); + + bool m_loaded; + AutoSaver *m_saveTimer; + BookmarkNode *m_bookmarkRootNode; + BookmarksModel *m_bookmarkModel; + QUndoStack m_commands; + + friend class RemoveBookmarksCommand; + friend class ChangeBookmarkCommand; +}; + +class RemoveBookmarksCommand : public QUndoCommand +{ + +public: + RemoveBookmarksCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *parent, int row); + ~RemoveBookmarksCommand(); + void undo(); + void redo(); + +protected: + int m_row; + BookmarksManager *m_bookmarkManagaer; + BookmarkNode *m_node; + BookmarkNode *m_parent; + bool m_done; +}; + +class InsertBookmarksCommand : public RemoveBookmarksCommand +{ + +public: + InsertBookmarksCommand(BookmarksManager *m_bookmarkManagaer, + BookmarkNode *parent, BookmarkNode *node, int row); + void undo() { RemoveBookmarksCommand::redo(); } + void redo() { RemoveBookmarksCommand::undo(); } + +}; + +class ChangeBookmarkCommand : public QUndoCommand +{ + +public: + ChangeBookmarkCommand(BookmarksManager *m_bookmarkManagaer, + BookmarkNode *node, const QString &newValue, bool title); + void undo(); + void redo(); + +private: + BookmarksManager *m_bookmarkManagaer; + bool m_title; + QString m_oldValue; + QString m_newValue; + BookmarkNode *m_node; +}; + +/*! + BookmarksModel is a QAbstractItemModel wrapper around the BookmarkManager + */ +#include +class BookmarksModel : public QAbstractItemModel +{ + Q_OBJECT + +public slots: + void entryAdded(BookmarkNode *item); + void entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item); + void entryChanged(BookmarkNode *item); + +public: + enum Roles { + TypeRole = Qt::UserRole + 1, + UrlRole = Qt::UserRole + 2, + UrlStringRole = Qt::UserRole + 3, + SeparatorRole = Qt::UserRole + 4 + }; + + BookmarksModel(BookmarksManager *bookmarkManager, QObject *parent = 0); + inline BookmarksManager *bookmarksManager() const { return m_bookmarksManager; } + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index= QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + Qt::DropActions supportedDropActions () const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QMimeData *mimeData(const QModelIndexList &indexes) const; + QStringList mimeTypes() const; + bool dropMimeData(const QMimeData *data, + Qt::DropAction action, int row, int column, const QModelIndex &parent); + bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + + BookmarkNode *node(const QModelIndex &index) const; + QModelIndex index(BookmarkNode *node) const; + +private: + + bool m_endMacro; + BookmarksManager *m_bookmarksManager; +}; + +// Menu that is dynamically populated from the bookmarks +#include "modelmenu.h" +class BookmarksMenu : public ModelMenu +{ + Q_OBJECT + +signals: + void openUrl(const QUrl &url); + +public: + BookmarksMenu(QWidget *parent = 0); + void setInitialActions(QList actions); + +protected: + bool prePopulated(); + +private slots: + void activated(const QModelIndex &index); + +private: + BookmarksManager *m_bookmarksManager; + QList m_initialActions; +}; + +/* + Proxy model that filters out the bookmarks so only the folders + are left behind. Used in the add bookmark dialog combobox. + */ +#include +class AddBookmarkProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + AddBookmarkProxyModel(QObject * parent = 0); + int columnCount(const QModelIndex & parent = QModelIndex()) const; + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; +}; + +/*! + Add bookmark dialog + */ +#include "ui_addbookmarkdialog.h" +class AddBookmarkDialog : public QDialog, public Ui_AddBookmarkDialog +{ + Q_OBJECT + +public: + AddBookmarkDialog(const QString &url, const QString &title, QWidget *parent = 0, BookmarksManager *bookmarkManager = 0); + +private slots: + void accept(); + +private: + QString m_url; + BookmarksManager *m_bookmarksManager; + AddBookmarkProxyModel *m_proxyModel; +}; + +#include "ui_bookmarks.h" +class TreeProxyModel; +class BookmarksDialog : public QDialog, public Ui_BookmarksDialog +{ + Q_OBJECT + +signals: + void openUrl(const QUrl &url); + +public: + BookmarksDialog(QWidget *parent = 0, BookmarksManager *manager = 0); + ~BookmarksDialog(); + +private slots: + void customContextMenuRequested(const QPoint &pos); + void open(); + void newFolder(); + +private: + void expandNodes(BookmarkNode *node); + bool saveExpandedNodes(const QModelIndex &parent); + + BookmarksManager *m_bookmarksManager; + BookmarksModel *m_bookmarksModel; + TreeProxyModel *m_proxyModel; +}; + +#include +class BookmarksToolBar : public QToolBar +{ + Q_OBJECT + +signals: + void openUrl(const QUrl &url); + +public: + BookmarksToolBar(BookmarksModel *model, QWidget *parent = 0); + void setRootIndex(const QModelIndex &index); + QModelIndex rootIndex() const; + +protected: + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + +private slots: + void triggered(QAction *action); + void activated(const QModelIndex &index); + void build(); + +private: + BookmarksModel *m_bookmarksModel; + QPersistentModelIndex m_root; +}; + +#endif // BOOKMARKS_H diff --git a/examples/gestures/browser/bookmarks.ui b/examples/gestures/browser/bookmarks.ui new file mode 100644 index 0000000..c893e94 --- /dev/null +++ b/examples/gestures/browser/bookmarks.ui @@ -0,0 +1,106 @@ + + BookmarksDialog + + + + 0 + 0 + 758 + 450 + + + + Bookmarks + + + + + + Qt::Horizontal + + + + 252 + 20 + + + + + + + + + + + + + + + + &Remove + + + + + + + Add Folder + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + SearchLineEdit + QLineEdit +
searchlineedit.h
+
+ + EditTreeView + QTreeView +
edittreeview.h
+
+
+ + + + buttonBox + accepted() + BookmarksDialog + accept() + + + 472 + 329 + + + 461 + 356 + + + + +
diff --git a/examples/gestures/browser/browser.icns b/examples/gestures/browser/browser.icns new file mode 100644 index 0000000..f591ae4 Binary files /dev/null and b/examples/gestures/browser/browser.icns differ diff --git a/examples/gestures/browser/browser.ico b/examples/gestures/browser/browser.ico new file mode 100644 index 0000000..7f9be93 Binary files /dev/null and b/examples/gestures/browser/browser.ico differ diff --git a/examples/gestures/browser/browser.pro b/examples/gestures/browser/browser.pro new file mode 100644 index 0000000..d970f99 --- /dev/null +++ b/examples/gestures/browser/browser.pro @@ -0,0 +1,91 @@ +TEMPLATE = app +TARGET = browser +QT += webkit network + +CONFIG += qt warn_on +contains(QT_BUILD_PARTS, tools): CONFIG += uitools +else: DEFINES += QT_NO_UITOOLS + +FORMS += \ + addbookmarkdialog.ui \ + bookmarks.ui \ + cookies.ui \ + cookiesexceptions.ui \ + downloaditem.ui \ + downloads.ui \ + history.ui \ + passworddialog.ui \ + proxy.ui \ + settings.ui + +HEADERS += \ + autosaver.h \ + bookmarks.h \ + browserapplication.h \ + browsermainwindow.h \ + chasewidget.h \ + cookiejar.h \ + downloadmanager.h \ + edittableview.h \ + edittreeview.h \ + history.h \ + modelmenu.h \ + networkaccessmanager.h \ + searchlineedit.h \ + settings.h \ + squeezelabel.h \ + tabwidget.h \ + toolbarsearch.h \ + urllineedit.h \ + webview.h \ + xbel.h + +SOURCES += \ + autosaver.cpp \ + bookmarks.cpp \ + browserapplication.cpp \ + browsermainwindow.cpp \ + chasewidget.cpp \ + cookiejar.cpp \ + downloadmanager.cpp \ + edittableview.cpp \ + edittreeview.cpp \ + history.cpp \ + modelmenu.cpp \ + networkaccessmanager.cpp \ + searchlineedit.cpp \ + settings.cpp \ + squeezelabel.cpp \ + tabwidget.cpp \ + toolbarsearch.cpp \ + urllineedit.cpp \ + webview.cpp \ + xbel.cpp \ + main.cpp + +RESOURCES += data/data.qrc htmls/htmls.qrc + +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} + +win32 { + RC_FILE = browser.rc +} + +mac { + ICON = browser.icns + QMAKE_INFO_PLIST = Info_mac.plist + TARGET = Browser +} + +wince*: { + DEPLOYMENT_PLUGIN += qjpeg qgif +} + +# install +target.path = $$[QT_INSTALL_DEMOS]/browser +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS *.plist *.icns *.ico *.rc *.pro *.html *.doc images htmls +sources.path = $$[QT_INSTALL_DEMOS]/browser +INSTALLS += target sources diff --git a/examples/gestures/browser/browser.rc b/examples/gestures/browser/browser.rc new file mode 100644 index 0000000..89a237c --- /dev/null +++ b/examples/gestures/browser/browser.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "browser.ico" + diff --git a/examples/gestures/browser/browserapplication.cpp b/examples/gestures/browser/browserapplication.cpp new file mode 100644 index 0000000..5a1d880 --- /dev/null +++ b/examples/gestures/browser/browserapplication.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "browserapplication.h" + +#include "bookmarks.h" +#include "browsermainwindow.h" +#include "cookiejar.h" +#include "downloadmanager.h" +#include "history.h" +#include "networkaccessmanager.h" +#include "tabwidget.h" +#include "webview.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +DownloadManager *BrowserApplication::s_downloadManager = 0; +HistoryManager *BrowserApplication::s_historyManager = 0; +NetworkAccessManager *BrowserApplication::s_networkAccessManager = 0; +BookmarksManager *BrowserApplication::s_bookmarksManager = 0; + +BrowserApplication::BrowserApplication(int &argc, char **argv) + : QApplication(argc, argv) + , m_localServer(0) +{ + QCoreApplication::setOrganizationName(QLatin1String("Trolltech")); + QCoreApplication::setApplicationName(QLatin1String("demobrowser")); + QCoreApplication::setApplicationVersion(QLatin1String("0.1")); +#ifdef Q_WS_QWS + // Use a different server name for QWS so we can run an X11 + // browser and a QWS browser in parallel on the same machine for + // debugging + QString serverName = QCoreApplication::applicationName() + QLatin1String("_qws"); +#else + QString serverName = QCoreApplication::applicationName(); +#endif + QLocalSocket socket; + socket.connectToServer(serverName); + if (socket.waitForConnected(500)) { + QTextStream stream(&socket); + QStringList args = QCoreApplication::arguments(); + if (args.count() > 1) + stream << args.last(); + else + stream << QString(); + stream.flush(); + socket.waitForBytesWritten(); + return; + } + +#if defined(Q_WS_MAC) + QApplication::setQuitOnLastWindowClosed(false); +#else + QApplication::setQuitOnLastWindowClosed(true); +#endif + + m_localServer = new QLocalServer(this); + connect(m_localServer, SIGNAL(newConnection()), + this, SLOT(newLocalSocketConnection())); + if (!m_localServer->listen(serverName)) { + if (m_localServer->serverError() == QAbstractSocket::AddressInUseError + && QFile::exists(m_localServer->serverName())) { + QFile::remove(m_localServer->serverName()); + m_localServer->listen(serverName); + } + } + + QDesktopServices::setUrlHandler(QLatin1String("http"), this, "openUrl"); + QString localSysName = QLocale::system().name(); + + installTranslator(QLatin1String("qt_") + localSysName); + + QSettings settings; + settings.beginGroup(QLatin1String("sessions")); + m_lastSession = settings.value(QLatin1String("lastSession")).toByteArray(); + settings.endGroup(); + +#if defined(Q_WS_MAC) + connect(this, SIGNAL(lastWindowClosed()), + this, SLOT(lastWindowClosed())); +#endif + + QTimer::singleShot(0, this, SLOT(postLaunch())); +} + +BrowserApplication::~BrowserApplication() +{ + delete s_downloadManager; + for (int i = 0; i < m_mainWindows.size(); ++i) { + BrowserMainWindow *window = m_mainWindows.at(i); + delete window; + } + delete s_networkAccessManager; + delete s_bookmarksManager; +} + +#if defined(Q_WS_MAC) +void BrowserApplication::lastWindowClosed() +{ + clean(); + BrowserMainWindow *mw = new BrowserMainWindow; + mw->slotHome(); + m_mainWindows.prepend(mw); +} +#endif + +BrowserApplication *BrowserApplication::instance() +{ + return (static_cast(QCoreApplication::instance())); +} + +#if defined(Q_WS_MAC) +#include +void BrowserApplication::quitBrowser() +{ + clean(); + int tabCount = 0; + for (int i = 0; i < m_mainWindows.count(); ++i) { + tabCount =+ m_mainWindows.at(i)->tabWidget()->count(); + } + + if (tabCount > 1) { + int ret = QMessageBox::warning(mainWindow(), QString(), + tr("There are %1 windows and %2 tabs open\n" + "Do you want to quit anyway?").arg(m_mainWindows.count()).arg(tabCount), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (ret == QMessageBox::No) + return; + } + + exit(0); +} +#endif + +/*! + Any actions that can be delayed until the window is visible + */ +void BrowserApplication::postLaunch() +{ + QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + if (directory.isEmpty()) + directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName(); + QWebSettings::setIconDatabasePath(directory); + + setWindowIcon(QIcon(QLatin1String(":browser.svg"))); + + loadSettings(); + + // newMainWindow() needs to be called in main() for this to happen + if (m_mainWindows.count() > 0) { + QStringList args = QCoreApplication::arguments(); + if (args.count() > 1) + mainWindow()->loadPage(args.last()); + else + mainWindow()->slotHome(); + } + BrowserApplication::historyManager(); +} + +void BrowserApplication::loadSettings() +{ + QSettings settings; + settings.beginGroup(QLatin1String("websettings")); + + QWebSettings *defaultSettings = QWebSettings::globalSettings(); + QString standardFontFamily = defaultSettings->fontFamily(QWebSettings::StandardFont); + int standardFontSize = defaultSettings->fontSize(QWebSettings::DefaultFontSize); + QFont standardFont = QFont(standardFontFamily, standardFontSize); + standardFont = qVariantValue(settings.value(QLatin1String("standardFont"), standardFont)); + defaultSettings->setFontFamily(QWebSettings::StandardFont, standardFont.family()); + defaultSettings->setFontSize(QWebSettings::DefaultFontSize, standardFont.pointSize()); + + QString fixedFontFamily = defaultSettings->fontFamily(QWebSettings::FixedFont); + int fixedFontSize = defaultSettings->fontSize(QWebSettings::DefaultFixedFontSize); + QFont fixedFont = QFont(fixedFontFamily, fixedFontSize); + fixedFont = qVariantValue(settings.value(QLatin1String("fixedFont"), fixedFont)); + defaultSettings->setFontFamily(QWebSettings::FixedFont, fixedFont.family()); + defaultSettings->setFontSize(QWebSettings::DefaultFixedFontSize, fixedFont.pointSize()); + + defaultSettings->setAttribute(QWebSettings::JavascriptEnabled, settings.value(QLatin1String("enableJavascript"), true).toBool()); + defaultSettings->setAttribute(QWebSettings::PluginsEnabled, settings.value(QLatin1String("enablePlugins"), true).toBool()); + + QUrl url = settings.value(QLatin1String("userStyleSheet")).toUrl(); + defaultSettings->setUserStyleSheetUrl(url); + + settings.endGroup(); +} + +QList BrowserApplication::mainWindows() +{ + clean(); + QList list; + for (int i = 0; i < m_mainWindows.count(); ++i) + list.append(m_mainWindows.at(i)); + return list; +} + +void BrowserApplication::clean() +{ + // cleanup any deleted main windows first + for (int i = m_mainWindows.count() - 1; i >= 0; --i) + if (m_mainWindows.at(i).isNull()) + m_mainWindows.removeAt(i); +} + +void BrowserApplication::saveSession() +{ + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return; + + clean(); + + QSettings settings; + settings.beginGroup(QLatin1String("sessions")); + + QByteArray data; + QBuffer buffer(&data); + QDataStream stream(&buffer); + buffer.open(QIODevice::ReadWrite); + + stream << m_mainWindows.count(); + for (int i = 0; i < m_mainWindows.count(); ++i) + stream << m_mainWindows.at(i)->saveState(); + settings.setValue(QLatin1String("lastSession"), data); + settings.endGroup(); +} + +bool BrowserApplication::canRestoreSession() const +{ + return !m_lastSession.isEmpty(); +} + +void BrowserApplication::restoreLastSession() +{ + QList windows; + QBuffer buffer(&m_lastSession); + QDataStream stream(&buffer); + buffer.open(QIODevice::ReadOnly); + int windowCount; + stream >> windowCount; + for (int i = 0; i < windowCount; ++i) { + QByteArray windowState; + stream >> windowState; + windows.append(windowState); + } + for (int i = 0; i < windows.count(); ++i) { + BrowserMainWindow *newWindow = 0; + if (m_mainWindows.count() == 1 + && mainWindow()->tabWidget()->count() == 1 + && mainWindow()->currentTab()->url() == QUrl()) { + newWindow = mainWindow(); + } else { + newWindow = newMainWindow(); + } + newWindow->restoreState(windows.at(i)); + } +} + +bool BrowserApplication::isTheOnlyBrowser() const +{ + return (m_localServer != 0); +} + +void BrowserApplication::installTranslator(const QString &name) +{ + QTranslator *translator = new QTranslator(this); + translator->load(name, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + QApplication::installTranslator(translator); +} + +#if defined(Q_WS_MAC) +bool BrowserApplication::event(QEvent* event) +{ + switch (event->type()) { + case QEvent::ApplicationActivate: { + clean(); + if (!m_mainWindows.isEmpty()) { + BrowserMainWindow *mw = mainWindow(); + if (mw && !mw->isMinimized()) { + mainWindow()->show(); + } + return true; + } + } + case QEvent::FileOpen: + if (!m_mainWindows.isEmpty()) { + mainWindow()->loadPage(static_cast(event)->file()); + return true; + } + default: + break; + } + return QApplication::event(event); +} +#endif + +void BrowserApplication::openUrl(const QUrl &url) +{ + mainWindow()->loadPage(url.toString()); +} + +BrowserMainWindow *BrowserApplication::newMainWindow() +{ + BrowserMainWindow *browser = new BrowserMainWindow(); + m_mainWindows.prepend(browser); + browser->show(); + return browser; +} + +BrowserMainWindow *BrowserApplication::mainWindow() +{ + clean(); + if (m_mainWindows.isEmpty()) + newMainWindow(); + return m_mainWindows[0]; +} + +void BrowserApplication::newLocalSocketConnection() +{ + QLocalSocket *socket = m_localServer->nextPendingConnection(); + if (!socket) + return; + socket->waitForReadyRead(1000); + QTextStream stream(socket); + QString url; + stream >> url; + if (!url.isEmpty()) { + QSettings settings; + settings.beginGroup(QLatin1String("general")); + int openLinksIn = settings.value(QLatin1String("openLinksIn"), 0).toInt(); + settings.endGroup(); + if (openLinksIn == 1) + newMainWindow(); + else + mainWindow()->tabWidget()->newTab(); + openUrl(url); + } + delete socket; + mainWindow()->raise(); + mainWindow()->activateWindow(); +} + +CookieJar *BrowserApplication::cookieJar() +{ + return (CookieJar*)networkAccessManager()->cookieJar(); +} + +DownloadManager *BrowserApplication::downloadManager() +{ + if (!s_downloadManager) { + s_downloadManager = new DownloadManager(); + } + return s_downloadManager; +} + +NetworkAccessManager *BrowserApplication::networkAccessManager() +{ + if (!s_networkAccessManager) { + s_networkAccessManager = new NetworkAccessManager(); + s_networkAccessManager->setCookieJar(new CookieJar); + } + return s_networkAccessManager; +} + +HistoryManager *BrowserApplication::historyManager() +{ + if (!s_historyManager) { + s_historyManager = new HistoryManager(); + QWebHistoryInterface::setDefaultInterface(s_historyManager); + } + return s_historyManager; +} + +BookmarksManager *BrowserApplication::bookmarksManager() +{ + if (!s_bookmarksManager) { + s_bookmarksManager = new BookmarksManager; + } + return s_bookmarksManager; +} + +QIcon BrowserApplication::icon(const QUrl &url) const +{ + QIcon icon = QWebSettings::iconForUrl(url); + if (!icon.isNull()) + return icon.pixmap(16, 16); + if (m_defaultIcon.isNull()) + m_defaultIcon = QIcon(QLatin1String(":defaulticon.png")); + return m_defaultIcon.pixmap(16, 16); +} + diff --git a/examples/gestures/browser/browserapplication.h b/examples/gestures/browser/browserapplication.h new file mode 100644 index 0000000..6ab8abb --- /dev/null +++ b/examples/gestures/browser/browserapplication.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BROWSERAPPLICATION_H +#define BROWSERAPPLICATION_H + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QLocalServer; +QT_END_NAMESPACE + +class BookmarksManager; +class BrowserMainWindow; +class CookieJar; +class DownloadManager; +class HistoryManager; +class NetworkAccessManager; +class BrowserApplication : public QApplication +{ + Q_OBJECT + +public: + BrowserApplication(int &argc, char **argv); + ~BrowserApplication(); + static BrowserApplication *instance(); + void loadSettings(); + + bool isTheOnlyBrowser() const; + BrowserMainWindow *mainWindow(); + QList mainWindows(); + QIcon icon(const QUrl &url) const; + + void saveSession(); + bool canRestoreSession() const; + + static HistoryManager *historyManager(); + static CookieJar *cookieJar(); + static DownloadManager *downloadManager(); + static NetworkAccessManager *networkAccessManager(); + static BookmarksManager *bookmarksManager(); + +#if defined(Q_WS_MAC) + bool event(QEvent *event); +#endif + +public slots: + BrowserMainWindow *newMainWindow(); + void restoreLastSession(); +#if defined(Q_WS_MAC) + void lastWindowClosed(); + void quitBrowser(); +#endif + +private slots: + void postLaunch(); + void openUrl(const QUrl &url); + void newLocalSocketConnection(); + +private: + void clean(); + void installTranslator(const QString &name); + + static HistoryManager *s_historyManager; + static DownloadManager *s_downloadManager; + static NetworkAccessManager *s_networkAccessManager; + static BookmarksManager *s_bookmarksManager; + + QList > m_mainWindows; + QLocalServer *m_localServer; + QByteArray m_lastSession; + mutable QIcon m_defaultIcon; +}; + +#endif // BROWSERAPPLICATION_H + diff --git a/examples/gestures/browser/browsermainwindow.cpp b/examples/gestures/browser/browsermainwindow.cpp new file mode 100644 index 0000000..321adf2 --- /dev/null +++ b/examples/gestures/browser/browsermainwindow.cpp @@ -0,0 +1,991 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "browsermainwindow.h" + +#include "autosaver.h" +#include "bookmarks.h" +#include "browserapplication.h" +#include "chasewidget.h" +#include "downloadmanager.h" +#include "history.h" +#include "settings.h" +#include "tabwidget.h" +#include "toolbarsearch.h" +#include "ui_passworddialog.h" +#include "webview.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +BrowserMainWindow::BrowserMainWindow(QWidget *parent, Qt::WindowFlags flags) + : QMainWindow(parent, flags) + , m_tabWidget(new TabWidget(this)) + , m_autoSaver(new AutoSaver(this)) + , m_historyBack(0) + , m_historyForward(0) + , m_stop(0) + , m_reload(0) +{ + setAttribute(Qt::WA_DeleteOnClose, true); + statusBar()->setSizeGripEnabled(true); + setupMenu(); + setupToolBar(); + + QWidget *centralWidget = new QWidget(this); + BookmarksModel *boomarksModel = BrowserApplication::bookmarksManager()->bookmarksModel(); + m_bookmarksToolbar = new BookmarksToolBar(boomarksModel, this); + connect(m_bookmarksToolbar, SIGNAL(openUrl(const QUrl&)), + m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&))); + connect(m_bookmarksToolbar->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(updateBookmarksToolbarActionText(bool))); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setSpacing(0); + layout->setMargin(0); +#if defined(Q_WS_MAC) + layout->addWidget(m_bookmarksToolbar); + layout->addWidget(new QWidget); // <- OS X tab widget style bug +#else + addToolBarBreak(); + addToolBar(m_bookmarksToolbar); +#endif + layout->addWidget(m_tabWidget); + centralWidget->setLayout(layout); + setCentralWidget(centralWidget); + + connect(m_tabWidget, SIGNAL(loadPage(const QString &)), + this, SLOT(loadPage(const QString &))); + connect(m_tabWidget, SIGNAL(setCurrentTitle(const QString &)), + this, SLOT(slotUpdateWindowTitle(const QString &))); + connect(m_tabWidget, SIGNAL(showStatusBarMessage(const QString&)), + statusBar(), SLOT(showMessage(const QString&))); + connect(m_tabWidget, SIGNAL(linkHovered(const QString&)), + statusBar(), SLOT(showMessage(const QString&))); + connect(m_tabWidget, SIGNAL(loadProgress(int)), + this, SLOT(slotLoadProgress(int))); + connect(m_tabWidget, SIGNAL(tabsChanged()), + m_autoSaver, SLOT(changeOccurred())); + connect(m_tabWidget, SIGNAL(geometryChangeRequested(const QRect &)), + this, SLOT(geometryChangeRequested(const QRect &))); + connect(m_tabWidget, SIGNAL(printRequested(QWebFrame *)), + this, SLOT(printRequested(QWebFrame *))); + connect(m_tabWidget, SIGNAL(menuBarVisibilityChangeRequested(bool)), + menuBar(), SLOT(setVisible(bool))); + connect(m_tabWidget, SIGNAL(statusBarVisibilityChangeRequested(bool)), + statusBar(), SLOT(setVisible(bool))); + connect(m_tabWidget, SIGNAL(toolBarVisibilityChangeRequested(bool)), + m_navigationBar, SLOT(setVisible(bool))); + connect(m_tabWidget, SIGNAL(toolBarVisibilityChangeRequested(bool)), + m_bookmarksToolbar, SLOT(setVisible(bool))); +#if defined(Q_WS_MAC) + connect(m_tabWidget, SIGNAL(lastTabClosed()), + this, SLOT(close())); +#else + connect(m_tabWidget, SIGNAL(lastTabClosed()), + m_tabWidget, SLOT(newTab())); +#endif + + slotUpdateWindowTitle(); + loadDefaultState(); + m_tabWidget->newTab(); + + int size = m_tabWidget->lineEditStack()->sizeHint().height(); + m_navigationBar->setIconSize(QSize(size, size)); + +} + +BrowserMainWindow::~BrowserMainWindow() +{ + m_autoSaver->changeOccurred(); + m_autoSaver->saveIfNeccessary(); +} + +void BrowserMainWindow::loadDefaultState() +{ + QSettings settings; + settings.beginGroup(QLatin1String("BrowserMainWindow")); + QByteArray data = settings.value(QLatin1String("defaultState")).toByteArray(); + restoreState(data); + settings.endGroup(); +} + +QSize BrowserMainWindow::sizeHint() const +{ + QRect desktopRect = QApplication::desktop()->screenGeometry(); + QSize size = desktopRect.size() * qreal(0.9); + return size; +} + +void BrowserMainWindow::save() +{ + BrowserApplication::instance()->saveSession(); + + QSettings settings; + settings.beginGroup(QLatin1String("BrowserMainWindow")); + QByteArray data = saveState(false); + settings.setValue(QLatin1String("defaultState"), data); + settings.endGroup(); +} + +static const qint32 BrowserMainWindowMagic = 0xba; + +QByteArray BrowserMainWindow::saveState(bool withTabs) const +{ + int version = 2; + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + stream << qint32(BrowserMainWindowMagic); + stream << qint32(version); + + stream << size(); + stream << !m_navigationBar->isHidden(); + stream << !m_bookmarksToolbar->isHidden(); + stream << !statusBar()->isHidden(); + if (withTabs) + stream << tabWidget()->saveState(); + else + stream << QByteArray(); + return data; +} + +bool BrowserMainWindow::restoreState(const QByteArray &state) +{ + int version = 2; + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + if (stream.atEnd()) + return false; + + qint32 marker; + qint32 v; + stream >> marker; + stream >> v; + if (marker != BrowserMainWindowMagic || v != version) + return false; + + QSize size; + bool showToolbar; + bool showBookmarksBar; + bool showStatusbar; + QByteArray tabState; + + stream >> size; + stream >> showToolbar; + stream >> showBookmarksBar; + stream >> showStatusbar; + stream >> tabState; + + resize(size); + + m_navigationBar->setVisible(showToolbar); + updateToolbarActionText(showToolbar); + + m_bookmarksToolbar->setVisible(showBookmarksBar); + updateBookmarksToolbarActionText(showBookmarksBar); + + statusBar()->setVisible(showStatusbar); + updateStatusbarActionText(showStatusbar); + + if (!tabWidget()->restoreState(tabState)) + return false; + + return true; +} + +void BrowserMainWindow::setupMenu() +{ + new QShortcut(QKeySequence(Qt::Key_F6), this, SLOT(slotSwapFocus())); + + // File + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + + fileMenu->addAction(tr("&New Window"), this, SLOT(slotFileNew()), QKeySequence::New); + fileMenu->addAction(m_tabWidget->newTabAction()); + fileMenu->addAction(tr("&Open File..."), this, SLOT(slotFileOpen()), QKeySequence::Open); + fileMenu->addAction(tr("Open &Location..."), this, + SLOT(slotSelectLineEdit()), QKeySequence(Qt::ControlModifier + Qt::Key_L)); + fileMenu->addSeparator(); + fileMenu->addAction(m_tabWidget->closeTabAction()); + fileMenu->addSeparator(); + fileMenu->addAction(tr("&Save As..."), this, + SLOT(slotFileSaveAs()), QKeySequence(QKeySequence::Save)); + fileMenu->addSeparator(); + BookmarksManager *bookmarksManager = BrowserApplication::bookmarksManager(); + fileMenu->addAction(tr("&Import Bookmarks..."), bookmarksManager, SLOT(importBookmarks())); + fileMenu->addAction(tr("&Export Bookmarks..."), bookmarksManager, SLOT(exportBookmarks())); + fileMenu->addSeparator(); + fileMenu->addAction(tr("P&rint Preview..."), this, SLOT(slotFilePrintPreview())); + fileMenu->addAction(tr("&Print..."), this, SLOT(slotFilePrint()), QKeySequence::Print); + fileMenu->addSeparator(); + QAction *action = fileMenu->addAction(tr("Private &Browsing..."), this, SLOT(slotPrivateBrowsing())); + action->setCheckable(true); + fileMenu->addSeparator(); + +#if defined(Q_WS_MAC) + fileMenu->addAction(tr("&Quit"), BrowserApplication::instance(), SLOT(quitBrowser()), QKeySequence(Qt::CTRL | Qt::Key_Q)); +#else + fileMenu->addAction(tr("&Quit"), this, SLOT(close()), QKeySequence(Qt::CTRL | Qt::Key_Q)); +#endif + + // Edit + QMenu *editMenu = menuBar()->addMenu(tr("&Edit")); + QAction *m_undo = editMenu->addAction(tr("&Undo")); + m_undo->setShortcuts(QKeySequence::Undo); + m_tabWidget->addWebAction(m_undo, QWebPage::Undo); + QAction *m_redo = editMenu->addAction(tr("&Redo")); + m_redo->setShortcuts(QKeySequence::Redo); + m_tabWidget->addWebAction(m_redo, QWebPage::Redo); + editMenu->addSeparator(); + QAction *m_cut = editMenu->addAction(tr("Cu&t")); + m_cut->setShortcuts(QKeySequence::Cut); + m_tabWidget->addWebAction(m_cut, QWebPage::Cut); + QAction *m_copy = editMenu->addAction(tr("&Copy")); + m_copy->setShortcuts(QKeySequence::Copy); + m_tabWidget->addWebAction(m_copy, QWebPage::Copy); + QAction *m_paste = editMenu->addAction(tr("&Paste")); + m_paste->setShortcuts(QKeySequence::Paste); + m_tabWidget->addWebAction(m_paste, QWebPage::Paste); + editMenu->addSeparator(); + + QAction *m_find = editMenu->addAction(tr("&Find")); + m_find->setShortcuts(QKeySequence::Find); + connect(m_find, SIGNAL(triggered()), this, SLOT(slotEditFind())); + new QShortcut(QKeySequence(Qt::Key_Slash), this, SLOT(slotEditFind())); + + QAction *m_findNext = editMenu->addAction(tr("&Find Next")); + m_findNext->setShortcuts(QKeySequence::FindNext); + connect(m_findNext, SIGNAL(triggered()), this, SLOT(slotEditFindNext())); + + QAction *m_findPrevious = editMenu->addAction(tr("&Find Previous")); + m_findPrevious->setShortcuts(QKeySequence::FindPrevious); + connect(m_findPrevious, SIGNAL(triggered()), this, SLOT(slotEditFindPrevious())); + + editMenu->addSeparator(); + editMenu->addAction(tr("&Preferences"), this, SLOT(slotPreferences()), tr("Ctrl+,")); + + // View + QMenu *viewMenu = menuBar()->addMenu(tr("&View")); + + m_viewBookmarkBar = new QAction(this); + updateBookmarksToolbarActionText(true); + m_viewBookmarkBar->setShortcut(tr("Shift+Ctrl+B")); + connect(m_viewBookmarkBar, SIGNAL(triggered()), this, SLOT(slotViewBookmarksBar())); + viewMenu->addAction(m_viewBookmarkBar); + + m_viewToolbar = new QAction(this); + updateToolbarActionText(true); + m_viewToolbar->setShortcut(tr("Ctrl+|")); + connect(m_viewToolbar, SIGNAL(triggered()), this, SLOT(slotViewToolbar())); + viewMenu->addAction(m_viewToolbar); + + m_viewStatusbar = new QAction(this); + updateStatusbarActionText(true); + m_viewStatusbar->setShortcut(tr("Ctrl+/")); + connect(m_viewStatusbar, SIGNAL(triggered()), this, SLOT(slotViewStatusbar())); + viewMenu->addAction(m_viewStatusbar); + + viewMenu->addSeparator(); + + m_stop = viewMenu->addAction(tr("&Stop")); + QList shortcuts; + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Period)); + shortcuts.append(Qt::Key_Escape); + m_stop->setShortcuts(shortcuts); + m_tabWidget->addWebAction(m_stop, QWebPage::Stop); + + m_reload = viewMenu->addAction(tr("Reload Page")); + m_reload->setShortcuts(QKeySequence::Refresh); + m_tabWidget->addWebAction(m_reload, QWebPage::Reload); + + viewMenu->addAction(tr("Zoom &In"), this, SLOT(slotViewZoomIn()), QKeySequence(Qt::CTRL | Qt::Key_Plus)); + viewMenu->addAction(tr("Zoom &Out"), this, SLOT(slotViewZoomOut()), QKeySequence(Qt::CTRL | Qt::Key_Minus)); + viewMenu->addAction(tr("Reset &Zoom"), this, SLOT(slotViewResetZoom()), QKeySequence(Qt::CTRL | Qt::Key_0)); + QAction *zoomTextOnlyAction = viewMenu->addAction(tr("Zoom &Text Only")); + connect(zoomTextOnlyAction, SIGNAL(toggled(bool)), this, SLOT(slotViewZoomTextOnly(bool))); + zoomTextOnlyAction->setCheckable(true); + zoomTextOnlyAction->setChecked(false); + + viewMenu->addSeparator(); + viewMenu->addAction(tr("Page S&ource"), this, SLOT(slotViewPageSource()), tr("Ctrl+Alt+U")); + QAction *a = viewMenu->addAction(tr("&Full Screen"), this, SLOT(slotViewFullScreen(bool)), Qt::Key_F11); + a->setCheckable(true); + + // History + HistoryMenu *historyMenu = new HistoryMenu(this); + connect(historyMenu, SIGNAL(openUrl(const QUrl&)), + m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&))); + connect(historyMenu, SIGNAL(hovered(const QString&)), this, + SLOT(slotUpdateStatusbar(const QString&))); + historyMenu->setTitle(tr("Hi&story")); + menuBar()->addMenu(historyMenu); + QList historyActions; + + m_historyBack = new QAction(tr("Back"), this); + m_tabWidget->addWebAction(m_historyBack, QWebPage::Back); + m_historyBack->setShortcuts(QKeySequence::Back); + m_historyBack->setIconVisibleInMenu(false); + + m_historyForward = new QAction(tr("Forward"), this); + m_tabWidget->addWebAction(m_historyForward, QWebPage::Forward); + m_historyForward->setShortcuts(QKeySequence::Forward); + m_historyForward->setIconVisibleInMenu(false); + + QAction *m_historyHome = new QAction(tr("Home"), this); + connect(m_historyHome, SIGNAL(triggered()), this, SLOT(slotHome())); + m_historyHome->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_H)); + + m_restoreLastSession = new QAction(tr("Restore Last Session"), this); + connect(m_restoreLastSession, SIGNAL(triggered()), BrowserApplication::instance(), SLOT(restoreLastSession())); + m_restoreLastSession->setEnabled(BrowserApplication::instance()->canRestoreSession()); + + historyActions.append(m_historyBack); + historyActions.append(m_historyForward); + historyActions.append(m_historyHome); + historyActions.append(m_tabWidget->recentlyClosedTabsAction()); + historyActions.append(m_restoreLastSession); + historyMenu->setInitialActions(historyActions); + + // Bookmarks + BookmarksMenu *bookmarksMenu = new BookmarksMenu(this); + connect(bookmarksMenu, SIGNAL(openUrl(const QUrl&)), + m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&))); + connect(bookmarksMenu, SIGNAL(hovered(const QString&)), + this, SLOT(slotUpdateStatusbar(const QString&))); + bookmarksMenu->setTitle(tr("&Bookmarks")); + menuBar()->addMenu(bookmarksMenu); + + QList bookmarksActions; + + QAction *showAllBookmarksAction = new QAction(tr("Show All Bookmarks"), this); + connect(showAllBookmarksAction, SIGNAL(triggered()), this, SLOT(slotShowBookmarksDialog())); + m_addBookmark = new QAction(QIcon(QLatin1String(":addbookmark.png")), tr("Add Bookmark..."), this); + m_addBookmark->setIconVisibleInMenu(false); + + connect(m_addBookmark, SIGNAL(triggered()), this, SLOT(slotAddBookmark())); + m_addBookmark->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_D)); + + bookmarksActions.append(showAllBookmarksAction); + bookmarksActions.append(m_addBookmark); + bookmarksMenu->setInitialActions(bookmarksActions); + + // Window + m_windowMenu = menuBar()->addMenu(tr("&Window")); + connect(m_windowMenu, SIGNAL(aboutToShow()), + this, SLOT(slotAboutToShowWindowMenu())); + slotAboutToShowWindowMenu(); + + QMenu *toolsMenu = menuBar()->addMenu(tr("&Tools")); + toolsMenu->addAction(tr("Web &Search"), this, SLOT(slotWebSearch()), QKeySequence(tr("Ctrl+K", "Web Search"))); +#ifndef Q_CC_MINGW + a = toolsMenu->addAction(tr("Enable Web &Inspector"), this, SLOT(slotToggleInspector(bool))); + a->setCheckable(true); +#endif + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt())); + helpMenu->addAction(tr("About &Demo Browser"), this, SLOT(slotAboutApplication())); +} + +void BrowserMainWindow::setupToolBar() +{ + setUnifiedTitleAndToolBarOnMac(true); + m_navigationBar = addToolBar(tr("Navigation")); + connect(m_navigationBar->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(updateToolbarActionText(bool))); + + m_historyBack->setIcon(style()->standardIcon(QStyle::SP_ArrowBack, 0, this)); + m_historyBackMenu = new QMenu(this); + m_historyBack->setMenu(m_historyBackMenu); + connect(m_historyBackMenu, SIGNAL(aboutToShow()), + this, SLOT(slotAboutToShowBackMenu())); + connect(m_historyBackMenu, SIGNAL(triggered(QAction *)), + this, SLOT(slotOpenActionUrl(QAction *))); + m_navigationBar->addAction(m_historyBack); + + m_historyForward->setIcon(style()->standardIcon(QStyle::SP_ArrowForward, 0, this)); + m_historyForwardMenu = new QMenu(this); + connect(m_historyForwardMenu, SIGNAL(aboutToShow()), + this, SLOT(slotAboutToShowForwardMenu())); + connect(m_historyForwardMenu, SIGNAL(triggered(QAction *)), + this, SLOT(slotOpenActionUrl(QAction *))); + m_historyForward->setMenu(m_historyForwardMenu); + m_navigationBar->addAction(m_historyForward); + + m_stopReload = new QAction(this); + m_reloadIcon = style()->standardIcon(QStyle::SP_BrowserReload); + m_stopReload->setIcon(m_reloadIcon); + + m_navigationBar->addAction(m_stopReload); + + m_navigationBar->addWidget(m_tabWidget->lineEditStack()); + + m_toolbarSearch = new ToolbarSearch(m_navigationBar); + m_navigationBar->addWidget(m_toolbarSearch); + connect(m_toolbarSearch, SIGNAL(search(const QUrl&)), SLOT(loadUrl(const QUrl&))); + + m_chaseWidget = new ChaseWidget(this); + m_navigationBar->addWidget(m_chaseWidget); +} + +void BrowserMainWindow::slotShowBookmarksDialog() +{ + BookmarksDialog *dialog = new BookmarksDialog(this); + connect(dialog, SIGNAL(openUrl(const QUrl&)), + m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&))); + dialog->show(); +} + +void BrowserMainWindow::slotAddBookmark() +{ + WebView *webView = currentTab(); + QString url = webView->url().toString(); + QString title = webView->title(); + AddBookmarkDialog dialog(url, title); + dialog.exec(); +} + +void BrowserMainWindow::slotViewToolbar() +{ + if (m_navigationBar->isVisible()) { + updateToolbarActionText(false); + m_navigationBar->close(); + } else { + updateToolbarActionText(true); + m_navigationBar->show(); + } + m_autoSaver->changeOccurred(); +} + +void BrowserMainWindow::slotViewBookmarksBar() +{ + if (m_bookmarksToolbar->isVisible()) { + updateBookmarksToolbarActionText(false); + m_bookmarksToolbar->close(); + } else { + updateBookmarksToolbarActionText(true); + m_bookmarksToolbar->show(); + } + m_autoSaver->changeOccurred(); +} + +void BrowserMainWindow::updateStatusbarActionText(bool visible) +{ + m_viewStatusbar->setText(!visible ? tr("Show Status Bar") : tr("Hide Status Bar")); +} + +void BrowserMainWindow::updateToolbarActionText(bool visible) +{ + m_viewToolbar->setText(!visible ? tr("Show Toolbar") : tr("Hide Toolbar")); +} + +void BrowserMainWindow::updateBookmarksToolbarActionText(bool visible) +{ + m_viewBookmarkBar->setText(!visible ? tr("Show Bookmarks bar") : tr("Hide Bookmarks bar")); +} + +void BrowserMainWindow::slotViewStatusbar() +{ + if (statusBar()->isVisible()) { + updateStatusbarActionText(false); + statusBar()->close(); + } else { + updateStatusbarActionText(true); + statusBar()->show(); + } + m_autoSaver->changeOccurred(); +} + +QUrl BrowserMainWindow::guessUrlFromString(const QString &string) +{ + QString urlStr = string.trimmed(); + QRegExp test(QLatin1String("^[a-zA-Z]+\\:.*")); + + // Check if it looks like a qualified URL. Try parsing it and see. + bool hasSchema = test.exactMatch(urlStr); + if (hasSchema) { + QUrl url = QUrl::fromEncoded(urlStr.toUtf8(), QUrl::TolerantMode); + if (url.isValid()) + return url; + } + + // Might be a file. + if (QFile::exists(urlStr)) { + QFileInfo info(urlStr); + return QUrl::fromLocalFile(info.absoluteFilePath()); + } + + // Might be a shorturl - try to detect the schema. + if (!hasSchema) { + int dotIndex = urlStr.indexOf(QLatin1Char('.')); + if (dotIndex != -1) { + QString prefix = urlStr.left(dotIndex).toLower(); + QByteArray schema = (prefix == QLatin1String("ftp")) ? prefix.toLatin1() : "http"; + QUrl url = + QUrl::fromEncoded(schema + "://" + urlStr.toUtf8(), QUrl::TolerantMode); + if (url.isValid()) + return url; + } + } + + // Fall back to QUrl's own tolerant parser. + QUrl url = QUrl::fromEncoded(string.toUtf8(), QUrl::TolerantMode); + + // finally for cases where the user just types in a hostname add http + if (url.scheme().isEmpty()) + url = QUrl::fromEncoded("http://" + string.toUtf8(), QUrl::TolerantMode); + return url; +} + +void BrowserMainWindow::loadUrl(const QUrl &url) +{ + if (!currentTab() || !url.isValid()) + return; + + m_tabWidget->currentLineEdit()->setText(QString::fromUtf8(url.toEncoded())); + m_tabWidget->loadUrlInCurrentTab(url); +} + +void BrowserMainWindow::slotDownloadManager() +{ + BrowserApplication::downloadManager()->show(); +} + +void BrowserMainWindow::slotSelectLineEdit() +{ + m_tabWidget->currentLineEdit()->selectAll(); + m_tabWidget->currentLineEdit()->setFocus(); +} + +void BrowserMainWindow::slotFileSaveAs() +{ + BrowserApplication::downloadManager()->download(currentTab()->url(), true); +} + +void BrowserMainWindow::slotPreferences() +{ + SettingsDialog *s = new SettingsDialog(this); + s->show(); +} + +void BrowserMainWindow::slotUpdateStatusbar(const QString &string) +{ + statusBar()->showMessage(string, 2000); +} + +void BrowserMainWindow::slotUpdateWindowTitle(const QString &title) +{ + if (title.isEmpty()) { + setWindowTitle(tr("Qt Demo Browser")); + } else { +#if defined(Q_WS_MAC) + setWindowTitle(title); +#else + setWindowTitle(tr("%1 - Qt Demo Browser", "Page title and Browser name").arg(title)); +#endif + } +} + +void BrowserMainWindow::slotAboutApplication() +{ + QMessageBox::about(this, tr("About"), tr( + "Version %1" + "

This demo demonstrates Qt's " + "webkit facilities in action, providing an example " + "browser for you to experiment with.

" + "

QtWebKit is based on the Open Source WebKit Project developed at http://webkit.org/." + ).arg(QCoreApplication::applicationVersion())); +} + +void BrowserMainWindow::slotFileNew() +{ + BrowserApplication::instance()->newMainWindow(); + BrowserMainWindow *mw = BrowserApplication::instance()->mainWindow(); + mw->slotHome(); +} + +void BrowserMainWindow::slotFileOpen() +{ + QString file = QFileDialog::getOpenFileName(this, tr("Open Web Resource"), QString(), + tr("Web Resources (*.html *.htm *.svg *.png *.gif *.svgz);;All files (*.*)")); + + if (file.isEmpty()) + return; + + loadPage(file); +} + +void BrowserMainWindow::slotFilePrintPreview() +{ +#ifndef QT_NO_PRINTER + if (!currentTab()) + return; + QPrintPreviewDialog *dialog = new QPrintPreviewDialog(this); + connect(dialog, SIGNAL(paintRequested(QPrinter *)), + currentTab(), SLOT(print(QPrinter *))); + dialog->exec(); +#endif +} + +void BrowserMainWindow::slotFilePrint() +{ + if (!currentTab()) + return; + printRequested(currentTab()->page()->mainFrame()); +} + +void BrowserMainWindow::printRequested(QWebFrame *frame) +{ +#ifndef QT_NO_PRINTER + QPrinter printer; + QPrintDialog *dialog = new QPrintDialog(&printer, this); + dialog->setWindowTitle(tr("Print Document")); + if (dialog->exec() != QDialog::Accepted) + return; + frame->print(&printer); +#endif +} + +void BrowserMainWindow::slotPrivateBrowsing() +{ + QWebSettings *settings = QWebSettings::globalSettings(); + bool pb = settings->testAttribute(QWebSettings::PrivateBrowsingEnabled); + if (!pb) { + QString title = tr("Are you sure you want to turn on private browsing?"); + QString text = tr("%1

When private browsing in turned on," + " webpages are not added to the history," + " items are automatically removed from the Downloads window," \ + " new cookies are not stored, current cookies can't be accessed," \ + " site icons wont be stored, session wont be saved, " \ + " and searches are not addded to the pop-up menu in the Google search box." \ + " Until you close the window, you can still click the Back and Forward buttons" \ + " to return to the webpages you have opened.").arg(title); + + QMessageBox::StandardButton button = QMessageBox::question(this, QString(), text, + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok); + if (button == QMessageBox::Ok) { + settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, true); + } + } else { + settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, false); + + QList windows = BrowserApplication::instance()->mainWindows(); + for (int i = 0; i < windows.count(); ++i) { + BrowserMainWindow *window = windows.at(i); + window->m_lastSearch = QString::null; + window->tabWidget()->clear(); + } + } +} + +void BrowserMainWindow::closeEvent(QCloseEvent *event) +{ + if (m_tabWidget->count() > 1) { + int ret = QMessageBox::warning(this, QString(), + tr("Are you sure you want to close the window?" + " There are %1 tab open").arg(m_tabWidget->count()), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (ret == QMessageBox::No) { + event->ignore(); + return; + } + } + event->accept(); + deleteLater(); +} + +void BrowserMainWindow::slotEditFind() +{ + if (!currentTab()) + return; + bool ok; + QString search = QInputDialog::getText(this, tr("Find"), + tr("Text:"), QLineEdit::Normal, + m_lastSearch, &ok); + if (ok && !search.isEmpty()) { + m_lastSearch = search; + if (!currentTab()->findText(m_lastSearch)) + slotUpdateStatusbar(tr("\"%1\" not found.").arg(m_lastSearch)); + } +} + +void BrowserMainWindow::slotEditFindNext() +{ + if (!currentTab() && !m_lastSearch.isEmpty()) + return; + currentTab()->findText(m_lastSearch); +} + +void BrowserMainWindow::slotEditFindPrevious() +{ + if (!currentTab() && !m_lastSearch.isEmpty()) + return; + currentTab()->findText(m_lastSearch, QWebPage::FindBackward); +} + +void BrowserMainWindow::slotViewZoomIn() +{ + if (!currentTab()) + return; + currentTab()->setZoomFactor(currentTab()->zoomFactor() + 0.1); +} + +void BrowserMainWindow::slotViewZoomOut() +{ + if (!currentTab()) + return; + currentTab()->setZoomFactor(currentTab()->zoomFactor() - 0.1); +} + +void BrowserMainWindow::slotViewResetZoom() +{ + if (!currentTab()) + return; + currentTab()->setZoomFactor(1.0); +} + +void BrowserMainWindow::slotViewZoomTextOnly(bool enable) +{ + if (!currentTab()) + return; + currentTab()->page()->settings()->setAttribute(QWebSettings::ZoomTextOnly, enable); +} + +void BrowserMainWindow::slotViewFullScreen(bool makeFullScreen) +{ + if (makeFullScreen) { + showFullScreen(); + } else { + if (isMinimized()) + showMinimized(); + else if (isMaximized()) + showMaximized(); + else showNormal(); + } +} + +void BrowserMainWindow::slotViewPageSource() +{ + if (!currentTab()) + return; + + QString markup = currentTab()->page()->mainFrame()->toHtml(); + QPlainTextEdit *view = new QPlainTextEdit(markup); + view->setWindowTitle(tr("Page Source of %1").arg(currentTab()->title())); + view->setMinimumWidth(640); + view->setAttribute(Qt::WA_DeleteOnClose); + view->show(); +} + +void BrowserMainWindow::slotHome() +{ + QSettings settings; + settings.beginGroup(QLatin1String("MainWindow")); + QString home = settings.value(QLatin1String("home"), QLatin1String("http://qtsoftware.com/")).toString(); + loadPage(home); +} + +void BrowserMainWindow::slotWebSearch() +{ + m_toolbarSearch->lineEdit()->selectAll(); + m_toolbarSearch->lineEdit()->setFocus(); +} + +void BrowserMainWindow::slotToggleInspector(bool enable) +{ + QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enable); + if (enable) { + int result = QMessageBox::question(this, tr("Web Inspector"), + tr("The web inspector will only work correctly for pages that were loaded after enabling.\n" + "Do you want to reload all pages?"), + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) { + m_tabWidget->reloadAllTabs(); + } + } +} + +void BrowserMainWindow::slotSwapFocus() +{ + if (currentTab()->hasFocus()) + m_tabWidget->currentLineEdit()->setFocus(); + else + currentTab()->setFocus(); +} + +void BrowserMainWindow::loadPage(const QString &page) +{ + QUrl url = guessUrlFromString(page); + loadUrl(url); +} + +TabWidget *BrowserMainWindow::tabWidget() const +{ + return m_tabWidget; +} + +WebView *BrowserMainWindow::currentTab() const +{ + return m_tabWidget->currentWebView(); +} + +void BrowserMainWindow::slotLoadProgress(int progress) +{ + if (progress < 100 && progress > 0) { + m_chaseWidget->setAnimated(true); + disconnect(m_stopReload, SIGNAL(triggered()), m_reload, SLOT(trigger())); + if (m_stopIcon.isNull()) + m_stopIcon = style()->standardIcon(QStyle::SP_BrowserStop); + m_stopReload->setIcon(m_stopIcon); + connect(m_stopReload, SIGNAL(triggered()), m_stop, SLOT(trigger())); + m_stopReload->setToolTip(tr("Stop loading the current page")); + } else { + m_chaseWidget->setAnimated(false); + disconnect(m_stopReload, SIGNAL(triggered()), m_stop, SLOT(trigger())); + m_stopReload->setIcon(m_reloadIcon); + connect(m_stopReload, SIGNAL(triggered()), m_reload, SLOT(trigger())); + m_stopReload->setToolTip(tr("Reload the current page")); + } +} + +void BrowserMainWindow::slotAboutToShowBackMenu() +{ + m_historyBackMenu->clear(); + if (!currentTab()) + return; + QWebHistory *history = currentTab()->history(); + int historyCount = history->count(); + for (int i = history->backItems(historyCount).count() - 1; i >= 0; --i) { + QWebHistoryItem item = history->backItems(history->count()).at(i); + QAction *action = new QAction(this); + action->setData(-1*(historyCount-i-1)); + QIcon icon = BrowserApplication::instance()->icon(item.url()); + action->setIcon(icon); + action->setText(item.title()); + m_historyBackMenu->addAction(action); + } +} + +void BrowserMainWindow::slotAboutToShowForwardMenu() +{ + m_historyForwardMenu->clear(); + if (!currentTab()) + return; + QWebHistory *history = currentTab()->history(); + int historyCount = history->count(); + for (int i = 0; i < history->forwardItems(history->count()).count(); ++i) { + QWebHistoryItem item = history->forwardItems(historyCount).at(i); + QAction *action = new QAction(this); + action->setData(historyCount-i); + QIcon icon = BrowserApplication::instance()->icon(item.url()); + action->setIcon(icon); + action->setText(item.title()); + m_historyForwardMenu->addAction(action); + } +} + +void BrowserMainWindow::slotAboutToShowWindowMenu() +{ + m_windowMenu->clear(); + m_windowMenu->addAction(m_tabWidget->nextTabAction()); + m_windowMenu->addAction(m_tabWidget->previousTabAction()); + m_windowMenu->addSeparator(); + m_windowMenu->addAction(tr("Downloads"), this, SLOT(slotDownloadManager()), QKeySequence(tr("Alt+Ctrl+L", "Download Manager"))); + + m_windowMenu->addSeparator(); + QList windows = BrowserApplication::instance()->mainWindows(); + for (int i = 0; i < windows.count(); ++i) { + BrowserMainWindow *window = windows.at(i); + QAction *action = m_windowMenu->addAction(window->windowTitle(), this, SLOT(slotShowWindow())); + action->setData(i); + action->setCheckable(true); + if (window == this) + action->setChecked(true); + } +} + +void BrowserMainWindow::slotShowWindow() +{ + if (QAction *action = qobject_cast(sender())) { + QVariant v = action->data(); + if (v.canConvert()) { + int offset = qvariant_cast(v); + QList windows = BrowserApplication::instance()->mainWindows(); + windows.at(offset)->activateWindow(); + windows.at(offset)->currentTab()->setFocus(); + } + } +} + +void BrowserMainWindow::slotOpenActionUrl(QAction *action) +{ + int offset = action->data().toInt(); + QWebHistory *history = currentTab()->history(); + if (offset < 0) + history->goToItem(history->backItems(-1*offset).first()); // back + else if (offset > 0) + history->goToItem(history->forwardItems(history->count() - offset + 1).back()); // forward + } + +void BrowserMainWindow::geometryChangeRequested(const QRect &geometry) +{ + setGeometry(geometry); +} + diff --git a/examples/gestures/browser/browsermainwindow.h b/examples/gestures/browser/browsermainwindow.h new file mode 100644 index 0000000..a8d08b0 --- /dev/null +++ b/examples/gestures/browser/browsermainwindow.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BROWSERMAINWINDOW_H +#define BROWSERMAINWINDOW_H + +#include +#include +#include + +class AutoSaver; +class BookmarksToolBar; +class ChaseWidget; +class QWebFrame; +class TabWidget; +class ToolbarSearch; +class WebView; + +/*! + The MainWindow of the Browser Application. + + Handles the tab widget and all the actions + */ +class BrowserMainWindow : public QMainWindow { + Q_OBJECT + +public: + BrowserMainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~BrowserMainWindow(); + QSize sizeHint() const; + +public: + static QUrl guessUrlFromString(const QString &url); + TabWidget *tabWidget() const; + WebView *currentTab() const; + QByteArray saveState(bool withTabs = true) const; + bool restoreState(const QByteArray &state); + +public slots: + void loadPage(const QString &url); + void slotHome(); + +protected: + void closeEvent(QCloseEvent *event); + +private slots: + void save(); + + void slotLoadProgress(int); + void slotUpdateStatusbar(const QString &string); + void slotUpdateWindowTitle(const QString &title = QString()); + + void loadUrl(const QUrl &url); + void slotPreferences(); + + void slotFileNew(); + void slotFileOpen(); + void slotFilePrintPreview(); + void slotFilePrint(); + void slotPrivateBrowsing(); + void slotFileSaveAs(); + void slotEditFind(); + void slotEditFindNext(); + void slotEditFindPrevious(); + void slotShowBookmarksDialog(); + void slotAddBookmark(); + void slotViewZoomIn(); + void slotViewZoomOut(); + void slotViewResetZoom(); + void slotViewZoomTextOnly(bool enable); + void slotViewToolbar(); + void slotViewBookmarksBar(); + void slotViewStatusbar(); + void slotViewPageSource(); + void slotViewFullScreen(bool enable); + + void slotWebSearch(); + void slotToggleInspector(bool enable); + void slotAboutApplication(); + void slotDownloadManager(); + void slotSelectLineEdit(); + + void slotAboutToShowBackMenu(); + void slotAboutToShowForwardMenu(); + void slotAboutToShowWindowMenu(); + void slotOpenActionUrl(QAction *action); + void slotShowWindow(); + void slotSwapFocus(); + + void printRequested(QWebFrame *frame); + void geometryChangeRequested(const QRect &geometry); + void updateToolbarActionText(bool visible); + void updateBookmarksToolbarActionText(bool visible); + +private: + void loadDefaultState(); + void setupMenu(); + void setupToolBar(); + void updateStatusbarActionText(bool visible); + +private: + QToolBar *m_navigationBar; + ToolbarSearch *m_toolbarSearch; + BookmarksToolBar *m_bookmarksToolbar; + ChaseWidget *m_chaseWidget; + TabWidget *m_tabWidget; + AutoSaver *m_autoSaver; + + QAction *m_historyBack; + QMenu *m_historyBackMenu; + QAction *m_historyForward; + QMenu *m_historyForwardMenu; + QMenu *m_windowMenu; + + QAction *m_stop; + QAction *m_reload; + QAction *m_stopReload; + QAction *m_viewToolbar; + QAction *m_viewBookmarkBar; + QAction *m_viewStatusbar; + QAction *m_restoreLastSession; + QAction *m_addBookmark; + + QIcon m_reloadIcon; + QIcon m_stopIcon; + + QString m_lastSearch; +}; + +#endif // BROWSERMAINWINDOW_H + diff --git a/examples/gestures/browser/chasewidget.cpp b/examples/gestures/browser/chasewidget.cpp new file mode 100644 index 0000000..c05bf30 --- /dev/null +++ b/examples/gestures/browser/chasewidget.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "chasewidget.h" + +#include + +#include +#include +#include +#include +#include + +ChaseWidget::ChaseWidget(QWidget *parent, QPixmap pixmap, bool pixmapEnabled) + : QWidget(parent) + , m_segment(0) + , m_delay(100) + , m_step(40) + , m_timerId(-1) + , m_animated(false) + , m_pixmap(pixmap) + , m_pixmapEnabled(pixmapEnabled) +{ +} + +void ChaseWidget::setAnimated(bool value) +{ + if (m_animated == value) + return; + m_animated = value; + if (m_timerId != -1) { + killTimer(m_timerId); + m_timerId = -1; + } + if (m_animated) { + m_segment = 0; + m_timerId = startTimer(m_delay); + } + update(); +} + +void ChaseWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter p(this); + if (m_pixmapEnabled && !m_pixmap.isNull()) { + p.drawPixmap(0, 0, m_pixmap); + return; + } + + const int extent = qMin(width() - 8, height() - 8); + const int displ = extent / 4; + const int ext = extent / 4 - 1; + + p.setRenderHint(QPainter::Antialiasing, true); + + if(m_animated) + p.setPen(Qt::gray); + else + p.setPen(QPen(palette().dark().color())); + + p.translate(width() / 2, height() / 2); // center + + for (int segment = 0; segment < segmentCount(); ++segment) { + p.rotate(QApplication::isRightToLeft() ? m_step : -m_step); + if(m_animated) + p.setBrush(colorForSegment(segment)); + else + p.setBrush(palette().background()); + p.drawEllipse(QRect(displ, -ext / 2, ext, ext)); + } +} + +QSize ChaseWidget::sizeHint() const +{ + return QSize(32, 32); +} + +void ChaseWidget::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timerId) { + ++m_segment; + update(); + } + QWidget::timerEvent(event); +} + +QColor ChaseWidget::colorForSegment(int seg) const +{ + int index = ((seg + m_segment) % segmentCount()); + int comp = qMax(0, 255 - (index * (255 / segmentCount()))); + return QColor(comp, comp, comp, 255); +} + +int ChaseWidget::segmentCount() const +{ + return 360 / m_step; +} + +void ChaseWidget::setPixmapEnabled(bool enable) +{ + m_pixmapEnabled = enable; +} + diff --git a/examples/gestures/browser/chasewidget.h b/examples/gestures/browser/chasewidget.h new file mode 100644 index 0000000..257eb38 --- /dev/null +++ b/examples/gestures/browser/chasewidget.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CHASEWIDGET_H +#define CHASEWIDGET_H + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QHideEvent; +class QShowEvent; +class QPaintEvent; +class QTimerEvent; +QT_END_NAMESPACE + +class ChaseWidget : public QWidget +{ + Q_OBJECT +public: + ChaseWidget(QWidget *parent = 0, QPixmap pixmap = QPixmap(), bool pixmapEnabled = false); + + void setAnimated(bool value); + void setPixmapEnabled(bool enable); + QSize sizeHint() const; + +protected: + void paintEvent(QPaintEvent *event); + void timerEvent(QTimerEvent *event); + +private: + int segmentCount() const; + QColor colorForSegment(int segment) const; + + int m_segment; + int m_delay; + int m_step; + int m_timerId; + bool m_animated; + QPixmap m_pixmap; + bool m_pixmapEnabled; +}; + +#endif diff --git a/examples/gestures/browser/cookiejar.cpp b/examples/gestures/browser/cookiejar.cpp new file mode 100644 index 0000000..16aa5c6 --- /dev/null +++ b/examples/gestures/browser/cookiejar.cpp @@ -0,0 +1,733 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "cookiejar.h" + +#include "autosaver.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static const unsigned int JAR_VERSION = 23; + +QT_BEGIN_NAMESPACE +QDataStream &operator<<(QDataStream &stream, const QList &list) +{ + stream << JAR_VERSION; + stream << quint32(list.size()); + for (int i = 0; i < list.size(); ++i) + stream << list.at(i).toRawForm(); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QList &list) +{ + list.clear(); + + quint32 version; + stream >> version; + + if (version != JAR_VERSION) + return stream; + + quint32 count; + stream >> count; + for(quint32 i = 0; i < count; ++i) + { + QByteArray value; + stream >> value; + QList newCookies = QNetworkCookie::parseCookies(value); + if (newCookies.count() == 0 && value.length() != 0) { + qWarning() << "CookieJar: Unable to parse saved cookie:" << value; + } + for (int j = 0; j < newCookies.count(); ++j) + list.append(newCookies.at(j)); + if (stream.atEnd()) + break; + } + return stream; +} +QT_END_NAMESPACE + +CookieJar::CookieJar(QObject *parent) + : QNetworkCookieJar(parent) + , m_loaded(false) + , m_saveTimer(new AutoSaver(this)) + , m_acceptCookies(AcceptOnlyFromSitesNavigatedTo) +{ +} + +CookieJar::~CookieJar() +{ + if (m_keepCookies == KeepUntilExit) + clear(); + m_saveTimer->saveIfNeccessary(); +} + +void CookieJar::clear() +{ + setAllCookies(QList()); + m_saveTimer->changeOccurred(); + emit cookiesChanged(); +} + +void CookieJar::load() +{ + if (m_loaded) + return; + // load cookies and exceptions + qRegisterMetaTypeStreamOperators >("QList"); + QSettings cookieSettings(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/cookies.ini"), QSettings::IniFormat); + setAllCookies(qvariant_cast >(cookieSettings.value(QLatin1String("cookies")))); + cookieSettings.beginGroup(QLatin1String("Exceptions")); + m_exceptions_block = cookieSettings.value(QLatin1String("block")).toStringList(); + m_exceptions_allow = cookieSettings.value(QLatin1String("allow")).toStringList(); + m_exceptions_allowForSession = cookieSettings.value(QLatin1String("allowForSession")).toStringList(); + qSort(m_exceptions_block.begin(), m_exceptions_block.end()); + qSort(m_exceptions_allow.begin(), m_exceptions_allow.end()); + qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end()); + + loadSettings(); +} + +void CookieJar::loadSettings() +{ + QSettings settings; + settings.beginGroup(QLatin1String("cookies")); + QByteArray value = settings.value(QLatin1String("acceptCookies"), + QLatin1String("AcceptOnlyFromSitesNavigatedTo")).toByteArray(); + QMetaEnum acceptPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AcceptPolicy")); + m_acceptCookies = acceptPolicyEnum.keyToValue(value) == -1 ? + AcceptOnlyFromSitesNavigatedTo : + static_cast(acceptPolicyEnum.keyToValue(value)); + + value = settings.value(QLatin1String("keepCookiesUntil"), QLatin1String("KeepUntilExpire")).toByteArray(); + QMetaEnum keepPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("KeepPolicy")); + m_keepCookies = keepPolicyEnum.keyToValue(value) == -1 ? + KeepUntilExpire : + static_cast(keepPolicyEnum.keyToValue(value)); + + if (m_keepCookies == KeepUntilExit) + setAllCookies(QList()); + + m_loaded = true; + emit cookiesChanged(); +} + +void CookieJar::save() +{ + if (!m_loaded) + return; + purgeOldCookies(); + QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + if (directory.isEmpty()) + directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName(); + if (!QFile::exists(directory)) { + QDir dir; + dir.mkpath(directory); + } + QSettings cookieSettings(directory + QLatin1String("/cookies.ini"), QSettings::IniFormat); + QList cookies = allCookies(); + for (int i = cookies.count() - 1; i >= 0; --i) { + if (cookies.at(i).isSessionCookie()) + cookies.removeAt(i); + } + cookieSettings.setValue(QLatin1String("cookies"), qVariantFromValue >(cookies)); + cookieSettings.beginGroup(QLatin1String("Exceptions")); + cookieSettings.setValue(QLatin1String("block"), m_exceptions_block); + cookieSettings.setValue(QLatin1String("allow"), m_exceptions_allow); + cookieSettings.setValue(QLatin1String("allowForSession"), m_exceptions_allowForSession); + + // save cookie settings + QSettings settings; + settings.beginGroup(QLatin1String("cookies")); + QMetaEnum acceptPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AcceptPolicy")); + settings.setValue(QLatin1String("acceptCookies"), QLatin1String(acceptPolicyEnum.valueToKey(m_acceptCookies))); + + QMetaEnum keepPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("KeepPolicy")); + settings.setValue(QLatin1String("keepCookiesUntil"), QLatin1String(keepPolicyEnum.valueToKey(m_keepCookies))); +} + +void CookieJar::purgeOldCookies() +{ + QList cookies = allCookies(); + if (cookies.isEmpty()) + return; + int oldCount = cookies.count(); + QDateTime now = QDateTime::currentDateTime(); + for (int i = cookies.count() - 1; i >= 0; --i) { + if (!cookies.at(i).isSessionCookie() && cookies.at(i).expirationDate() < now) + cookies.removeAt(i); + } + if (oldCount == cookies.count()) + return; + setAllCookies(cookies); + emit cookiesChanged(); +} + +QList CookieJar::cookiesForUrl(const QUrl &url) const +{ + CookieJar *that = const_cast(this); + if (!m_loaded) + that->load(); + + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) { + QList noCookies; + return noCookies; + } + + return QNetworkCookieJar::cookiesForUrl(url); +} + +bool CookieJar::setCookiesFromUrl(const QList &cookieList, const QUrl &url) +{ + if (!m_loaded) + load(); + + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return false; + + QString host = url.host(); + bool eBlock = qBinaryFind(m_exceptions_block.begin(), m_exceptions_block.end(), host) != m_exceptions_block.end(); + bool eAllow = qBinaryFind(m_exceptions_allow.begin(), m_exceptions_allow.end(), host) != m_exceptions_allow.end(); + bool eAllowSession = qBinaryFind(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end(), host) != m_exceptions_allowForSession.end(); + + bool addedCookies = false; + // pass exceptions + bool acceptInitially = (m_acceptCookies != AcceptNever); + if ((acceptInitially && !eBlock) + || (!acceptInitially && (eAllow || eAllowSession))) { + // pass url domain == cookie domain + QDateTime soon = QDateTime::currentDateTime(); + soon = soon.addDays(90); + foreach(QNetworkCookie cookie, cookieList) { + QList lst; + if (m_keepCookies == KeepUntilTimeLimit + && !cookie.isSessionCookie() + && cookie.expirationDate() > soon) { + cookie.setExpirationDate(soon); + } + lst += cookie; + if (QNetworkCookieJar::setCookiesFromUrl(lst, url)) { + addedCookies = true; + } else { + // finally force it in if wanted + if (m_acceptCookies == AcceptAlways) { + QList cookies = allCookies(); + cookies += cookie; + setAllCookies(cookies); + addedCookies = true; + } +#if 0 + else + qWarning() << "setCookiesFromUrl failed" << url << cookieList.value(0).toRawForm(); +#endif + } + } + } + + if (addedCookies) { + m_saveTimer->changeOccurred(); + emit cookiesChanged(); + } + return addedCookies; +} + +CookieJar::AcceptPolicy CookieJar::acceptPolicy() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_acceptCookies; +} + +void CookieJar::setAcceptPolicy(AcceptPolicy policy) +{ + if (!m_loaded) + load(); + if (policy == m_acceptCookies) + return; + m_acceptCookies = policy; + m_saveTimer->changeOccurred(); +} + +CookieJar::KeepPolicy CookieJar::keepPolicy() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_keepCookies; +} + +void CookieJar::setKeepPolicy(KeepPolicy policy) +{ + if (!m_loaded) + load(); + if (policy == m_keepCookies) + return; + m_keepCookies = policy; + m_saveTimer->changeOccurred(); +} + +QStringList CookieJar::blockedCookies() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_exceptions_block; +} + +QStringList CookieJar::allowedCookies() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_exceptions_allow; +} + +QStringList CookieJar::allowForSessionCookies() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_exceptions_allowForSession; +} + +void CookieJar::setBlockedCookies(const QStringList &list) +{ + if (!m_loaded) + load(); + m_exceptions_block = list; + qSort(m_exceptions_block.begin(), m_exceptions_block.end()); + m_saveTimer->changeOccurred(); +} + +void CookieJar::setAllowedCookies(const QStringList &list) +{ + if (!m_loaded) + load(); + m_exceptions_allow = list; + qSort(m_exceptions_allow.begin(), m_exceptions_allow.end()); + m_saveTimer->changeOccurred(); +} + +void CookieJar::setAllowForSessionCookies(const QStringList &list) +{ + if (!m_loaded) + load(); + m_exceptions_allowForSession = list; + qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end()); + m_saveTimer->changeOccurred(); +} + +CookieModel::CookieModel(CookieJar *cookieJar, QObject *parent) + : QAbstractTableModel(parent) + , m_cookieJar(cookieJar) +{ + connect(m_cookieJar, SIGNAL(cookiesChanged()), this, SLOT(cookiesChanged())); + m_cookieJar->load(); +} + +QVariant CookieModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::SizeHintRole) { + QFont font; + font.setPointSize(10); + QFontMetrics fm(font); + int height = fm.height() + fm.height()/3; + int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString()); + return QSize(width, height); + } + + if (orientation == Qt::Horizontal) { + if (role != Qt::DisplayRole) + return QVariant(); + + switch (section) { + case 0: + return tr("Website"); + case 1: + return tr("Name"); + case 2: + return tr("Path"); + case 3: + return tr("Secure"); + case 4: + return tr("Expires"); + case 5: + return tr("Contents"); + default: + return QVariant(); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + +QVariant CookieModel::data(const QModelIndex &index, int role) const +{ + QList lst; + if (m_cookieJar) + lst = m_cookieJar->allCookies(); + if (index.row() < 0 || index.row() >= lst.size()) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + case Qt::EditRole: { + QNetworkCookie cookie = lst.at(index.row()); + switch (index.column()) { + case 0: + return cookie.domain(); + case 1: + return cookie.name(); + case 2: + return cookie.path(); + case 3: + return cookie.isSecure(); + case 4: + return cookie.expirationDate(); + case 5: + return cookie.value(); + } + } + case Qt::FontRole:{ + QFont font; + font.setPointSize(10); + return font; + } + } + + return QVariant(); +} + +int CookieModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 6; +} + +int CookieModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() || !m_cookieJar) ? 0 : m_cookieJar->allCookies().count(); +} + +bool CookieModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid() || !m_cookieJar) + return false; + int lastRow = row + count - 1; + beginRemoveRows(parent, row, lastRow); + QList lst = m_cookieJar->allCookies(); + for (int i = lastRow; i >= row; --i) { + lst.removeAt(i); + } + m_cookieJar->setAllCookies(lst); + endRemoveRows(); + return true; +} + +void CookieModel::cookiesChanged() +{ + reset(); +} + +CookiesDialog::CookiesDialog(CookieJar *cookieJar, QWidget *parent) : QDialog(parent) +{ + setupUi(this); + setWindowFlags(Qt::Sheet); + CookieModel *model = new CookieModel(cookieJar, this); + m_proxyModel = new QSortFilterProxyModel(this); + connect(search, SIGNAL(textChanged(QString)), + m_proxyModel, SLOT(setFilterFixedString(QString))); + connect(removeButton, SIGNAL(clicked()), cookiesTable, SLOT(removeOne())); + connect(removeAllButton, SIGNAL(clicked()), cookiesTable, SLOT(removeAll())); + m_proxyModel->setSourceModel(model); + cookiesTable->verticalHeader()->hide(); + cookiesTable->setSelectionBehavior(QAbstractItemView::SelectRows); + cookiesTable->setModel(m_proxyModel); + cookiesTable->setAlternatingRowColors(true); + cookiesTable->setTextElideMode(Qt::ElideMiddle); + cookiesTable->setShowGrid(false); + cookiesTable->setSortingEnabled(true); + QFont f = font(); + f.setPointSize(10); + QFontMetrics fm(f); + int height = fm.height() + fm.height()/3; + cookiesTable->verticalHeader()->setDefaultSectionSize(height); + cookiesTable->verticalHeader()->setMinimumSectionSize(-1); + for (int i = 0; i < model->columnCount(); ++i){ + int header = cookiesTable->horizontalHeader()->sectionSizeHint(i); + switch (i) { + case 0: + header = fm.width(QLatin1String("averagehost.domain.com")); + break; + case 1: + header = fm.width(QLatin1String("_session_id")); + break; + case 4: + header = fm.width(QDateTime::currentDateTime().toString(Qt::LocalDate)); + break; + } + int buffer = fm.width(QLatin1String("xx")); + header += buffer; + cookiesTable->horizontalHeader()->resizeSection(i, header); + } + cookiesTable->horizontalHeader()->setStretchLastSection(true); +} + + + +CookieExceptionsModel::CookieExceptionsModel(CookieJar *cookiejar, QObject *parent) + : QAbstractTableModel(parent) + , m_cookieJar(cookiejar) +{ + m_allowedCookies = m_cookieJar->allowedCookies(); + m_blockedCookies = m_cookieJar->blockedCookies(); + m_sessionCookies = m_cookieJar->allowForSessionCookies(); +} + +QVariant CookieExceptionsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::SizeHintRole) { + QFont font; + font.setPointSize(10); + QFontMetrics fm(font); + int height = fm.height() + fm.height()/3; + int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString()); + return QSize(width, height); + } + + if (orientation == Qt::Horizontal + && role == Qt::DisplayRole) { + switch (section) { + case 0: + return tr("Website"); + case 1: + return tr("Status"); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + +QVariant CookieExceptionsModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= rowCount()) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + case Qt::EditRole: { + int row = index.row(); + if (row < m_allowedCookies.count()) { + switch (index.column()) { + case 0: + return m_allowedCookies.at(row); + case 1: + return tr("Allow"); + } + } + row = row - m_allowedCookies.count(); + if (row < m_blockedCookies.count()) { + switch (index.column()) { + case 0: + return m_blockedCookies.at(row); + case 1: + return tr("Block"); + } + } + row = row - m_blockedCookies.count(); + if (row < m_sessionCookies.count()) { + switch (index.column()) { + case 0: + return m_sessionCookies.at(row); + case 1: + return tr("Allow For Session"); + } + } + } + case Qt::FontRole:{ + QFont font; + font.setPointSize(10); + return font; + } + } + return QVariant(); +} + +int CookieExceptionsModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + +int CookieExceptionsModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() || !m_cookieJar) ? 0 : m_allowedCookies.count() + m_blockedCookies.count() + m_sessionCookies.count(); +} + +bool CookieExceptionsModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid() || !m_cookieJar) + return false; + + int lastRow = row + count - 1; + beginRemoveRows(parent, row, lastRow); + for (int i = lastRow; i >= row; --i) { + if (i < m_allowedCookies.count()) { + m_allowedCookies.removeAt(row); + continue; + } + i = i - m_allowedCookies.count(); + if (i < m_blockedCookies.count()) { + m_blockedCookies.removeAt(row); + continue; + } + i = i - m_blockedCookies.count(); + if (i < m_sessionCookies.count()) { + m_sessionCookies.removeAt(row); + continue; + } + } + m_cookieJar->setAllowedCookies(m_allowedCookies); + m_cookieJar->setBlockedCookies(m_blockedCookies); + m_cookieJar->setAllowForSessionCookies(m_sessionCookies); + endRemoveRows(); + return true; +} + +CookiesExceptionsDialog::CookiesExceptionsDialog(CookieJar *cookieJar, QWidget *parent) + : QDialog(parent) + , m_cookieJar(cookieJar) +{ + setupUi(this); + setWindowFlags(Qt::Sheet); + connect(removeButton, SIGNAL(clicked()), exceptionTable, SLOT(removeOne())); + connect(removeAllButton, SIGNAL(clicked()), exceptionTable, SLOT(removeAll())); + exceptionTable->verticalHeader()->hide(); + exceptionTable->setSelectionBehavior(QAbstractItemView::SelectRows); + exceptionTable->setAlternatingRowColors(true); + exceptionTable->setTextElideMode(Qt::ElideMiddle); + exceptionTable->setShowGrid(false); + exceptionTable->setSortingEnabled(true); + m_exceptionsModel = new CookieExceptionsModel(cookieJar, this); + m_proxyModel = new QSortFilterProxyModel(this); + m_proxyModel->setSourceModel(m_exceptionsModel); + connect(search, SIGNAL(textChanged(QString)), + m_proxyModel, SLOT(setFilterFixedString(QString))); + exceptionTable->setModel(m_proxyModel); + + CookieModel *cookieModel = new CookieModel(cookieJar, this); + domainLineEdit->setCompleter(new QCompleter(cookieModel, domainLineEdit)); + + connect(domainLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(textChanged(const QString &))); + connect(blockButton, SIGNAL(clicked()), this, SLOT(block())); + connect(allowButton, SIGNAL(clicked()), this, SLOT(allow())); + connect(allowForSessionButton, SIGNAL(clicked()), this, SLOT(allowForSession())); + + QFont f = font(); + f.setPointSize(10); + QFontMetrics fm(f); + int height = fm.height() + fm.height()/3; + exceptionTable->verticalHeader()->setDefaultSectionSize(height); + exceptionTable->verticalHeader()->setMinimumSectionSize(-1); + for (int i = 0; i < m_exceptionsModel->columnCount(); ++i){ + int header = exceptionTable->horizontalHeader()->sectionSizeHint(i); + switch (i) { + case 0: + header = fm.width(QLatin1String("averagebiglonghost.domain.com")); + break; + case 1: + header = fm.width(QLatin1String("Allow For Session")); + break; + } + int buffer = fm.width(QLatin1String("xx")); + header += buffer; + exceptionTable->horizontalHeader()->resizeSection(i, header); + } +} + +void CookiesExceptionsDialog::textChanged(const QString &text) +{ + bool enabled = !text.isEmpty(); + blockButton->setEnabled(enabled); + allowButton->setEnabled(enabled); + allowForSessionButton->setEnabled(enabled); +} + +void CookiesExceptionsDialog::block() +{ + if (domainLineEdit->text().isEmpty()) + return; + m_exceptionsModel->m_blockedCookies.append(domainLineEdit->text()); + m_cookieJar->setBlockedCookies(m_exceptionsModel->m_blockedCookies); + m_exceptionsModel->reset(); +} + +void CookiesExceptionsDialog::allow() +{ + if (domainLineEdit->text().isEmpty()) + return; + m_exceptionsModel->m_allowedCookies.append(domainLineEdit->text()); + m_cookieJar->setAllowedCookies(m_exceptionsModel->m_allowedCookies); + m_exceptionsModel->reset(); +} + +void CookiesExceptionsDialog::allowForSession() +{ + if (domainLineEdit->text().isEmpty()) + return; + m_exceptionsModel->m_sessionCookies.append(domainLineEdit->text()); + m_cookieJar->setAllowForSessionCookies(m_exceptionsModel->m_sessionCookies); + m_exceptionsModel->reset(); +} + diff --git a/examples/gestures/browser/cookiejar.h b/examples/gestures/browser/cookiejar.h new file mode 100644 index 0000000..2d53627 --- /dev/null +++ b/examples/gestures/browser/cookiejar.h @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COOKIEJAR_H +#define COOKIEJAR_H + +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QSortFilterProxyModel; +class QKeyEvent; +QT_END_NAMESPACE + +class AutoSaver; + +class CookieJar : public QNetworkCookieJar +{ + friend class CookieModel; + Q_OBJECT + Q_PROPERTY(AcceptPolicy acceptPolicy READ acceptPolicy WRITE setAcceptPolicy) + Q_PROPERTY(KeepPolicy keepPolicy READ keepPolicy WRITE setKeepPolicy) + Q_PROPERTY(QStringList blockedCookies READ blockedCookies WRITE setBlockedCookies) + Q_PROPERTY(QStringList allowedCookies READ allowedCookies WRITE setAllowedCookies) + Q_PROPERTY(QStringList allowForSessionCookies READ allowForSessionCookies WRITE setAllowForSessionCookies) + Q_ENUMS(KeepPolicy) + Q_ENUMS(AcceptPolicy) + +signals: + void cookiesChanged(); + +public: + enum AcceptPolicy { + AcceptAlways, + AcceptNever, + AcceptOnlyFromSitesNavigatedTo + }; + + enum KeepPolicy { + KeepUntilExpire, + KeepUntilExit, + KeepUntilTimeLimit + }; + + CookieJar(QObject *parent = 0); + ~CookieJar(); + + QList cookiesForUrl(const QUrl &url) const; + bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); + + AcceptPolicy acceptPolicy() const; + void setAcceptPolicy(AcceptPolicy policy); + + KeepPolicy keepPolicy() const; + void setKeepPolicy(KeepPolicy policy); + + QStringList blockedCookies() const; + QStringList allowedCookies() const; + QStringList allowForSessionCookies() const; + + void setBlockedCookies(const QStringList &list); + void setAllowedCookies(const QStringList &list); + void setAllowForSessionCookies(const QStringList &list); + +public slots: + void clear(); + void loadSettings(); + +private slots: + void save(); + +private: + void purgeOldCookies(); + void load(); + bool m_loaded; + AutoSaver *m_saveTimer; + + AcceptPolicy m_acceptCookies; + KeepPolicy m_keepCookies; + + QStringList m_exceptions_block; + QStringList m_exceptions_allow; + QStringList m_exceptions_allowForSession; +}; + +class CookieModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + CookieModel(CookieJar *jar, QObject *parent = 0); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private slots: + void cookiesChanged(); + +private: + CookieJar *m_cookieJar; +}; + +#include "ui_cookies.h" +#include "ui_cookiesexceptions.h" + +class CookiesDialog : public QDialog, public Ui_CookiesDialog +{ + Q_OBJECT + +public: + CookiesDialog(CookieJar *cookieJar, QWidget *parent = 0); + +private: + QSortFilterProxyModel *m_proxyModel; +}; + +class CookieExceptionsModel : public QAbstractTableModel +{ + Q_OBJECT + friend class CookiesExceptionsDialog; + +public: + CookieExceptionsModel(CookieJar *cookieJar, QObject *parent = 0); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + CookieJar *m_cookieJar; + + // Domains we allow, Domains we block, Domains we allow for this session + QStringList m_allowedCookies; + QStringList m_blockedCookies; + QStringList m_sessionCookies; +}; + +class CookiesExceptionsDialog : public QDialog, public Ui_CookiesExceptionsDialog +{ + Q_OBJECT + +public: + CookiesExceptionsDialog(CookieJar *cookieJar, QWidget *parent = 0); + +private slots: + void block(); + void allow(); + void allowForSession(); + void textChanged(const QString &text); + +private: + CookieExceptionsModel *m_exceptionsModel; + QSortFilterProxyModel *m_proxyModel; + CookieJar *m_cookieJar; +}; + +#endif // COOKIEJAR_H + diff --git a/examples/gestures/browser/cookies.ui b/examples/gestures/browser/cookies.ui new file mode 100644 index 0000000..c4bccc5 --- /dev/null +++ b/examples/gestures/browser/cookies.ui @@ -0,0 +1,106 @@ + + CookiesDialog + + + + 0 + 0 + 550 + 370 + + + + Cookies + + + + + + Qt::Horizontal + + + + 252 + 20 + + + + + + + + + + + + + + + + &Remove + + + + + + + Remove &All Cookies + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + SearchLineEdit + QLineEdit +

searchlineedit.h
+ + + EditTableView + QTableView +
edittableview.h
+
+
+ + + + buttonBox + accepted() + CookiesDialog + accept() + + + 472 + 329 + + + 461 + 356 + + + + + diff --git a/examples/gestures/browser/cookiesexceptions.ui b/examples/gestures/browser/cookiesexceptions.ui new file mode 100644 index 0000000..3d9ef62 --- /dev/null +++ b/examples/gestures/browser/cookiesexceptions.ui @@ -0,0 +1,184 @@ + + CookiesExceptionsDialog + + + + 0 + 0 + 466 + 446 + + + + Cookie Exceptions + + + + + + New Exception + + + + + + + + Domain: + + + + + + + + + + + + + + Qt::Horizontal + + + + 81 + 25 + + + + + + + + false + + + Block + + + + + + + false + + + Allow For Session + + + + + + + false + + + Allow + + + + + + + + + + + + Exceptions + + + + + + Qt::Horizontal + + + + 252 + 20 + + + + + + + + + + + + + + &Remove + + + + + + + Remove &All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + SearchLineEdit + QLineEdit +
searchlineedit.h
+
+ + EditTableView + QTableView +
edittableview.h
+
+
+ + + + buttonBox + accepted() + CookiesExceptionsDialog + accept() + + + 381 + 428 + + + 336 + 443 + + + + +
diff --git a/examples/gestures/browser/downloaditem.ui b/examples/gestures/browser/downloaditem.ui new file mode 100644 index 0000000..4a0a0fd --- /dev/null +++ b/examples/gestures/browser/downloaditem.ui @@ -0,0 +1,134 @@ + + DownloadItem + + + + 0 + 0 + 423 + 110 + + + + Form + + + + 0 + + + + + + 0 + 0 + + + + Ico + + + + + + + + + + 0 + 0 + + + + Filename + + + + + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + Qt::Vertical + + + + 17 + 1 + + + + + + + + false + + + Try Again + + + + + + + Stop + + + + + + + Open + + + + + + + Qt::Vertical + + + + 17 + 5 + + + + + + + + + + + SqueezeLabel + QWidget +
squeezelabel.h
+
+
+ + +
diff --git a/examples/gestures/browser/downloadmanager.cpp b/examples/gestures/browser/downloadmanager.cpp new file mode 100644 index 0000000..1e327c7 --- /dev/null +++ b/examples/gestures/browser/downloadmanager.cpp @@ -0,0 +1,579 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "downloadmanager.h" + +#include "autosaver.h" +#include "browserapplication.h" +#include "networkaccessmanager.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +/*! + DownloadItem is a widget that is displayed in the download manager list. + It moves the data from the QNetworkReply into the QFile as well + as update the information/progressbar and report errors. + */ +DownloadItem::DownloadItem(QNetworkReply *reply, bool requestFileName, QWidget *parent) + : QWidget(parent) + , m_reply(reply) + , m_requestFileName(requestFileName) + , m_bytesReceived(0) +{ + setupUi(this); + QPalette p = downloadInfoLabel->palette(); + p.setColor(QPalette::Text, Qt::darkGray); + downloadInfoLabel->setPalette(p); + progressBar->setMaximum(0); + tryAgainButton->hide(); + connect(stopButton, SIGNAL(clicked()), this, SLOT(stop())); + connect(openButton, SIGNAL(clicked()), this, SLOT(open())); + connect(tryAgainButton, SIGNAL(clicked()), this, SLOT(tryAgain())); + + init(); +} + +void DownloadItem::init() +{ + if (!m_reply) + return; + + // attach to the m_reply + m_url = m_reply->url(); + m_reply->setParent(this); + connect(m_reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead())); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(error(QNetworkReply::NetworkError))); + connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), + this, SLOT(downloadProgress(qint64, qint64))); + connect(m_reply, SIGNAL(metaDataChanged()), + this, SLOT(metaDataChanged())); + connect(m_reply, SIGNAL(finished()), + this, SLOT(finished())); + + // reset info + downloadInfoLabel->clear(); + progressBar->setValue(0); + getFileName(); + + // start timer for the download estimation + m_downloadTime.start(); + + if (m_reply->error() != QNetworkReply::NoError) { + error(m_reply->error()); + finished(); + } +} + +void DownloadItem::getFileName() +{ + QSettings settings; + settings.beginGroup(QLatin1String("downloadmanager")); + QString defaultLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); + QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), defaultLocation).toString(); + if (!downloadDirectory.isEmpty()) + downloadDirectory += QLatin1Char('/'); + + QString defaultFileName = saveFileName(downloadDirectory); + QString fileName = defaultFileName; + if (m_requestFileName) { + fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName); + if (fileName.isEmpty()) { + m_reply->close(); + fileNameLabel->setText(tr("Download canceled: %1").arg(QFileInfo(defaultFileName).fileName())); + return; + } + } + m_output.setFileName(fileName); + fileNameLabel->setText(QFileInfo(m_output.fileName()).fileName()); + if (m_requestFileName) + downloadReadyRead(); +} + +QString DownloadItem::saveFileName(const QString &directory) const +{ + // Move this function into QNetworkReply to also get file name sent from the server + QString path = m_url.path(); + QFileInfo info(path); + QString baseName = info.completeBaseName(); + QString endName = info.suffix(); + + if (baseName.isEmpty()) { + baseName = QLatin1String("unnamed_download"); + qDebug() << "DownloadManager:: downloading unknown file:" << m_url; + } + QString name = directory + baseName + QLatin1Char('.') + endName; + if (QFile::exists(name)) { + // already exists, don't overwrite + int i = 1; + do { + name = directory + baseName + QLatin1Char('-') + QString::number(i++) + QLatin1Char('.') + endName; + } while (QFile::exists(name)); + } + return name; +} + + +void DownloadItem::stop() +{ + setUpdatesEnabled(false); + stopButton->setEnabled(false); + stopButton->hide(); + tryAgainButton->setEnabled(true); + tryAgainButton->show(); + setUpdatesEnabled(true); + m_reply->abort(); +} + +void DownloadItem::open() +{ + QFileInfo info(m_output); + QUrl url = QUrl::fromLocalFile(info.absolutePath()); + QDesktopServices::openUrl(url); +} + +void DownloadItem::tryAgain() +{ + if (!tryAgainButton->isEnabled()) + return; + + tryAgainButton->setEnabled(false); + tryAgainButton->setVisible(false); + stopButton->setEnabled(true); + stopButton->setVisible(true); + progressBar->setVisible(true); + + QNetworkReply *r = BrowserApplication::networkAccessManager()->get(QNetworkRequest(m_url)); + if (m_reply) + m_reply->deleteLater(); + if (m_output.exists()) + m_output.remove(); + m_reply = r; + init(); + emit statusChanged(); +} + +void DownloadItem::downloadReadyRead() +{ + if (m_requestFileName && m_output.fileName().isEmpty()) + return; + if (!m_output.isOpen()) { + // in case someone else has already put a file there + if (!m_requestFileName) + getFileName(); + if (!m_output.open(QIODevice::WriteOnly)) { + downloadInfoLabel->setText(tr("Error opening save file: %1") + .arg(m_output.errorString())); + stopButton->click(); + emit statusChanged(); + return; + } + emit statusChanged(); + } + if (-1 == m_output.write(m_reply->readAll())) { + downloadInfoLabel->setText(tr("Error saving: %1") + .arg(m_output.errorString())); + stopButton->click(); + } +} + +void DownloadItem::error(QNetworkReply::NetworkError) +{ + qDebug() << "DownloadItem::error" << m_reply->errorString() << m_url; + downloadInfoLabel->setText(tr("Network Error: %1").arg(m_reply->errorString())); + tryAgainButton->setEnabled(true); + tryAgainButton->setVisible(true); +} + +void DownloadItem::metaDataChanged() +{ + qDebug() << "DownloadItem::metaDataChanged: not handled."; +} + +void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + m_bytesReceived = bytesReceived; + if (bytesTotal == -1) { + progressBar->setValue(0); + progressBar->setMaximum(0); + } else { + progressBar->setValue(bytesReceived); + progressBar->setMaximum(bytesTotal); + } + updateInfoLabel(); +} + +void DownloadItem::updateInfoLabel() +{ + if (m_reply->error() == QNetworkReply::NoError) + return; + + qint64 bytesTotal = progressBar->maximum(); + bool running = !downloadedSuccessfully(); + + // update info label + double speed = m_bytesReceived * 1000.0 / m_downloadTime.elapsed(); + double timeRemaining = ((double)(bytesTotal - m_bytesReceived)) / speed; + QString timeRemainingString = tr("seconds"); + if (timeRemaining > 60) { + timeRemaining = timeRemaining / 60; + timeRemainingString = tr("minutes"); + } + timeRemaining = floor(timeRemaining); + + // When downloading the eta should never be 0 + if (timeRemaining == 0) + timeRemaining = 1; + + QString info; + if (running) { + QString remaining; + if (bytesTotal != 0) + remaining = tr("- %4 %5 remaining") + .arg(timeRemaining) + .arg(timeRemainingString); + info = QString(tr("%1 of %2 (%3/sec) %4")) + .arg(dataString(m_bytesReceived)) + .arg(bytesTotal == 0 ? tr("?") : dataString(bytesTotal)) + .arg(dataString((int)speed)) + .arg(remaining); + } else { + if (m_bytesReceived == bytesTotal) + info = dataString(m_output.size()); + else + info = tr("%1 of %2 - Stopped") + .arg(dataString(m_bytesReceived)) + .arg(dataString(bytesTotal)); + } + downloadInfoLabel->setText(info); +} + +QString DownloadItem::dataString(int size) const +{ + QString unit; + if (size < 1024) { + unit = tr("bytes"); + } else if (size < 1024*1024) { + size /= 1024; + unit = tr("kB"); + } else { + size /= 1024*1024; + unit = tr("MB"); + } + return QString(QLatin1String("%1 %2")).arg(size).arg(unit); +} + +bool DownloadItem::downloading() const +{ + return (progressBar->isVisible()); +} + +bool DownloadItem::downloadedSuccessfully() const +{ + return (stopButton->isHidden() && tryAgainButton->isHidden()); +} + +void DownloadItem::finished() +{ + progressBar->hide(); + stopButton->setEnabled(false); + stopButton->hide(); + m_output.close(); + updateInfoLabel(); + emit statusChanged(); +} + +/*! + DownloadManager is a Dialog that contains a list of DownloadItems + + It is a basic download manager. It only downloads the file, doesn't do BitTorrent, + extract zipped files or anything fancy. + */ +DownloadManager::DownloadManager(QWidget *parent) + : QDialog(parent) + , m_autoSaver(new AutoSaver(this)) + , m_manager(BrowserApplication::networkAccessManager()) + , m_iconProvider(0) + , m_removePolicy(Never) +{ + setupUi(this); + downloadsView->setShowGrid(false); + downloadsView->verticalHeader()->hide(); + downloadsView->horizontalHeader()->hide(); + downloadsView->setAlternatingRowColors(true); + downloadsView->horizontalHeader()->setStretchLastSection(true); + m_model = new DownloadModel(this); + downloadsView->setModel(m_model); + connect(cleanupButton, SIGNAL(clicked()), this, SLOT(cleanup())); + load(); +} + +DownloadManager::~DownloadManager() +{ + m_autoSaver->changeOccurred(); + m_autoSaver->saveIfNeccessary(); + if (m_iconProvider) + delete m_iconProvider; +} + +int DownloadManager::activeDownloads() const +{ + int count = 0; + for (int i = 0; i < m_downloads.count(); ++i) { + if (m_downloads.at(i)->stopButton->isEnabled()) + ++count; + } + return count; +} + +void DownloadManager::download(const QNetworkRequest &request, bool requestFileName) +{ + if (request.url().isEmpty()) + return; + handleUnsupportedContent(m_manager->get(request), requestFileName); +} + +void DownloadManager::handleUnsupportedContent(QNetworkReply *reply, bool requestFileName) +{ + if (!reply || reply->url().isEmpty()) + return; + QVariant header = reply->header(QNetworkRequest::ContentLengthHeader); + bool ok; + int size = header.toInt(&ok); + if (ok && size == 0) + return; + + qDebug() << "DownloadManager::handleUnsupportedContent" << reply->url() << "requestFileName" << requestFileName; + DownloadItem *item = new DownloadItem(reply, requestFileName, this); + addItem(item); +} + +void DownloadManager::addItem(DownloadItem *item) +{ + connect(item, SIGNAL(statusChanged()), this, SLOT(updateRow())); + int row = m_downloads.count(); + m_model->beginInsertRows(QModelIndex(), row, row); + m_downloads.append(item); + m_model->endInsertRows(); + updateItemCount(); + if (row == 0) + show(); + downloadsView->setIndexWidget(m_model->index(row, 0), item); + QIcon icon = style()->standardIcon(QStyle::SP_FileIcon); + item->fileIcon->setPixmap(icon.pixmap(48, 48)); + downloadsView->setRowHeight(row, item->sizeHint().height()); +} + +void DownloadManager::updateRow() +{ + DownloadItem *item = qobject_cast(sender()); + int row = m_downloads.indexOf(item); + if (-1 == row) + return; + if (!m_iconProvider) + m_iconProvider = new QFileIconProvider(); + QIcon icon = m_iconProvider->icon(item->m_output.fileName()); + if (icon.isNull()) + icon = style()->standardIcon(QStyle::SP_FileIcon); + item->fileIcon->setPixmap(icon.pixmap(48, 48)); + downloadsView->setRowHeight(row, item->minimumSizeHint().height()); + + bool remove = false; + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (!item->downloading() + && globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + remove = true; + + if (item->downloadedSuccessfully() + && removePolicy() == DownloadManager::SuccessFullDownload) { + remove = true; + } + if (remove) + m_model->removeRow(row); + + cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0); +} + +DownloadManager::RemovePolicy DownloadManager::removePolicy() const +{ + return m_removePolicy; +} + +void DownloadManager::setRemovePolicy(RemovePolicy policy) +{ + if (policy == m_removePolicy) + return; + m_removePolicy = policy; + m_autoSaver->changeOccurred(); +} + +void DownloadManager::save() const +{ + QSettings settings; + settings.beginGroup(QLatin1String("downloadmanager")); + QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy")); + settings.setValue(QLatin1String("removeDownloadsPolicy"), QLatin1String(removePolicyEnum.valueToKey(m_removePolicy))); + settings.setValue(QLatin1String("size"), size()); + if (m_removePolicy == Exit) + return; + + for (int i = 0; i < m_downloads.count(); ++i) { + QString key = QString(QLatin1String("download_%1_")).arg(i); + settings.setValue(key + QLatin1String("url"), m_downloads[i]->m_url); + settings.setValue(key + QLatin1String("location"), QFileInfo(m_downloads[i]->m_output).filePath()); + settings.setValue(key + QLatin1String("done"), m_downloads[i]->downloadedSuccessfully()); + } + int i = m_downloads.count(); + QString key = QString(QLatin1String("download_%1_")).arg(i); + while (settings.contains(key + QLatin1String("url"))) { + settings.remove(key + QLatin1String("url")); + settings.remove(key + QLatin1String("location")); + settings.remove(key + QLatin1String("done")); + key = QString(QLatin1String("download_%1_")).arg(++i); + } +} + +void DownloadManager::load() +{ + QSettings settings; + settings.beginGroup(QLatin1String("downloadmanager")); + QSize size = settings.value(QLatin1String("size")).toSize(); + if (size.isValid()) + resize(size); + QByteArray value = settings.value(QLatin1String("removeDownloadsPolicy"), QLatin1String("Never")).toByteArray(); + QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy")); + m_removePolicy = removePolicyEnum.keyToValue(value) == -1 ? + Never : + static_cast(removePolicyEnum.keyToValue(value)); + + int i = 0; + QString key = QString(QLatin1String("download_%1_")).arg(i); + while (settings.contains(key + QLatin1String("url"))) { + QUrl url = settings.value(key + QLatin1String("url")).toUrl(); + QString fileName = settings.value(key + QLatin1String("location")).toString(); + bool done = settings.value(key + QLatin1String("done"), true).toBool(); + if (!url.isEmpty() && !fileName.isEmpty()) { + DownloadItem *item = new DownloadItem(0, this); + item->m_output.setFileName(fileName); + item->fileNameLabel->setText(QFileInfo(item->m_output.fileName()).fileName()); + item->m_url = url; + item->stopButton->setVisible(false); + item->stopButton->setEnabled(false); + item->tryAgainButton->setVisible(!done); + item->tryAgainButton->setEnabled(!done); + item->progressBar->setVisible(!done); + addItem(item); + } + key = QString(QLatin1String("download_%1_")).arg(++i); + } + cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0); +} + +void DownloadManager::cleanup() +{ + if (m_downloads.isEmpty()) + return; + m_model->removeRows(0, m_downloads.count()); + updateItemCount(); + if (m_downloads.isEmpty() && m_iconProvider) { + delete m_iconProvider; + m_iconProvider = 0; + } + m_autoSaver->changeOccurred(); +} + +void DownloadManager::updateItemCount() +{ + int count = m_downloads.count(); + itemCount->setText(count == 1 ? tr("1 Download") : tr("%1 Downloads").arg(count)); +} + +DownloadModel::DownloadModel(DownloadManager *downloadManager, QObject *parent) + : QAbstractListModel(parent) + , m_downloadManager(downloadManager) +{ +} + +QVariant DownloadModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= rowCount(index.parent())) + return QVariant(); + if (role == Qt::ToolTipRole) + if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully()) + return m_downloadManager->m_downloads.at(index.row())->downloadInfoLabel->text(); + return QVariant(); +} + +int DownloadModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : m_downloadManager->m_downloads.count(); +} + +bool DownloadModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + + int lastRow = row + count - 1; + for (int i = lastRow; i >= row; --i) { + if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully() + || m_downloadManager->m_downloads.at(i)->tryAgainButton->isEnabled()) { + beginRemoveRows(parent, i, i); + m_downloadManager->m_downloads.takeAt(i)->deleteLater(); + endRemoveRows(); + } + } + m_downloadManager->m_autoSaver->changeOccurred(); + return true; +} + diff --git a/examples/gestures/browser/downloadmanager.h b/examples/gestures/browser/downloadmanager.h new file mode 100644 index 0000000..80a451c --- /dev/null +++ b/examples/gestures/browser/downloadmanager.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DOWNLOADMANAGER_H +#define DOWNLOADMANAGER_H + +#include "ui_downloads.h" +#include "ui_downloaditem.h" + +#include + +#include +#include + +class DownloadItem : public QWidget, public Ui_DownloadItem +{ + Q_OBJECT + +signals: + void statusChanged(); + +public: + DownloadItem(QNetworkReply *reply = 0, bool requestFileName = false, QWidget *parent = 0); + bool downloading() const; + bool downloadedSuccessfully() const; + + QUrl m_url; + + QFile m_output; + QNetworkReply *m_reply; + +private slots: + void stop(); + void tryAgain(); + void open(); + + void downloadReadyRead(); + void error(QNetworkReply::NetworkError code); + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void metaDataChanged(); + void finished(); + +private: + void getFileName(); + void init(); + void updateInfoLabel(); + QString dataString(int size) const; + + QString saveFileName(const QString &directory) const; + + bool m_requestFileName; + qint64 m_bytesReceived; + QTime m_downloadTime; +}; + +class AutoSaver; +class DownloadModel; +QT_BEGIN_NAMESPACE +class QFileIconProvider; +QT_END_NAMESPACE + +class DownloadManager : public QDialog, public Ui_DownloadDialog +{ + Q_OBJECT + Q_PROPERTY(RemovePolicy removePolicy READ removePolicy WRITE setRemovePolicy) + Q_ENUMS(RemovePolicy) + +public: + enum RemovePolicy { + Never, + Exit, + SuccessFullDownload + }; + + DownloadManager(QWidget *parent = 0); + ~DownloadManager(); + int activeDownloads() const; + + RemovePolicy removePolicy() const; + void setRemovePolicy(RemovePolicy policy); + +public slots: + void download(const QNetworkRequest &request, bool requestFileName = false); + inline void download(const QUrl &url, bool requestFileName = false) + { download(QNetworkRequest(url), requestFileName); } + void handleUnsupportedContent(QNetworkReply *reply, bool requestFileName = false); + void cleanup(); + +private slots: + void save() const; + void updateRow(); + +private: + void addItem(DownloadItem *item); + void updateItemCount(); + void load(); + + AutoSaver *m_autoSaver; + DownloadModel *m_model; + QNetworkAccessManager *m_manager; + QFileIconProvider *m_iconProvider; + QList m_downloads; + RemovePolicy m_removePolicy; + friend class DownloadModel; +}; + +class DownloadModel : public QAbstractListModel +{ + friend class DownloadManager; + Q_OBJECT + +public: + DownloadModel(DownloadManager *downloadManager, QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + DownloadManager *m_downloadManager; + +}; + +#endif // DOWNLOADMANAGER_H + diff --git a/examples/gestures/browser/downloads.ui b/examples/gestures/browser/downloads.ui new file mode 100644 index 0000000..a2e2569 --- /dev/null +++ b/examples/gestures/browser/downloads.ui @@ -0,0 +1,83 @@ + + DownloadDialog + + + + 0 + 0 + 332 + 252 + + + + Downloads + + + + 0 + + + 0 + + + + + + + + + + false + + + Clean up + + + + + + + Qt::Horizontal + + + + 58 + 24 + + + + + + + + + + 0 Items + + + + + + + Qt::Horizontal + + + + 148 + 20 + + + + + + + + + EditTableView + QTableView +
edittableview.h
+
+
+ + +
diff --git a/examples/gestures/browser/edittableview.cpp b/examples/gestures/browser/edittableview.cpp new file mode 100644 index 0000000..72c367d --- /dev/null +++ b/examples/gestures/browser/edittableview.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "edittableview.h" +#include + +EditTableView::EditTableView(QWidget *parent) + : QTableView(parent) +{ +} + +void EditTableView::keyPressEvent(QKeyEvent *event) +{ + if ((event->key() == Qt::Key_Delete + || event->key() == Qt::Key_Backspace) + && model()) { + removeOne(); + } else { + QAbstractItemView::keyPressEvent(event); + } +} + +void EditTableView::removeOne() +{ + if (!model() || !selectionModel()) + return; + int row = currentIndex().row(); + model()->removeRow(row, rootIndex()); + QModelIndex idx = model()->index(row, 0, rootIndex()); + if (!idx.isValid()) + idx = model()->index(row - 1, 0, rootIndex()); + selectionModel()->select(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); +} + +void EditTableView::removeAll() +{ + if (model()) + model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex()); +} + diff --git a/examples/gestures/browser/edittableview.h b/examples/gestures/browser/edittableview.h new file mode 100644 index 0000000..359c548 --- /dev/null +++ b/examples/gestures/browser/edittableview.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EDITTABLEVIEW_H +#define EDITTABLEVIEW_H + +#include + +class EditTableView : public QTableView +{ + Q_OBJECT + +public: + EditTableView(QWidget *parent = 0); + void keyPressEvent(QKeyEvent *event); + +public slots: + void removeOne(); + void removeAll(); +}; + +#endif // EDITTABLEVIEW_H + diff --git a/examples/gestures/browser/edittreeview.cpp b/examples/gestures/browser/edittreeview.cpp new file mode 100644 index 0000000..dacb28e --- /dev/null +++ b/examples/gestures/browser/edittreeview.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "edittreeview.h" + +#include + +EditTreeView::EditTreeView(QWidget *parent) + : QTreeView(parent) +{ +} + +void EditTreeView::keyPressEvent(QKeyEvent *event) +{ + if ((event->key() == Qt::Key_Delete + || event->key() == Qt::Key_Backspace) + && model()) { + removeOne(); + } else { + QAbstractItemView::keyPressEvent(event); + } +} + +void EditTreeView::removeOne() +{ + if (!model()) + return; + QModelIndex ci = currentIndex(); + int row = ci.row(); + model()->removeRow(row, ci.parent()); +} + +void EditTreeView::removeAll() +{ + if (!model()) + return; + model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex()); +} + diff --git a/examples/gestures/browser/edittreeview.h b/examples/gestures/browser/edittreeview.h new file mode 100644 index 0000000..a0b776c --- /dev/null +++ b/examples/gestures/browser/edittreeview.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EDITTREEVIEW_H +#define EDITTREEVIEW_H + +#include + +class EditTreeView : public QTreeView +{ + Q_OBJECT + +public: + EditTreeView(QWidget *parent = 0); + void keyPressEvent(QKeyEvent *event); + +public slots: + void removeOne(); + void removeAll(); +}; + +#endif // EDITTREEVIEW_H + diff --git a/examples/gestures/browser/history.cpp b/examples/gestures/browser/history.cpp new file mode 100644 index 0000000..ef7e834 --- /dev/null +++ b/examples/gestures/browser/history.cpp @@ -0,0 +1,1282 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "history.h" + +#include "autosaver.h" +#include "browserapplication.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +static const unsigned int HISTORY_VERSION = 23; + +HistoryManager::HistoryManager(QObject *parent) + : QWebHistoryInterface(parent) + , m_saveTimer(new AutoSaver(this)) + , m_historyLimit(30) + , m_historyModel(0) + , m_historyFilterModel(0) + , m_historyTreeModel(0) +{ + m_expiredTimer.setSingleShot(true); + connect(&m_expiredTimer, SIGNAL(timeout()), + this, SLOT(checkForExpired())); + connect(this, SIGNAL(entryAdded(const HistoryItem &)), + m_saveTimer, SLOT(changeOccurred())); + connect(this, SIGNAL(entryRemoved(const HistoryItem &)), + m_saveTimer, SLOT(changeOccurred())); + load(); + + m_historyModel = new HistoryModel(this, this); + m_historyFilterModel = new HistoryFilterModel(m_historyModel, this); + m_historyTreeModel = new HistoryTreeModel(m_historyFilterModel, this); + + // QWebHistoryInterface will delete the history manager + QWebHistoryInterface::setDefaultInterface(this); +} + +HistoryManager::~HistoryManager() +{ + m_saveTimer->saveIfNeccessary(); +} + +QList HistoryManager::history() const +{ + return m_history; +} + +bool HistoryManager::historyContains(const QString &url) const +{ + return m_historyFilterModel->historyContains(url); +} + +void HistoryManager::addHistoryEntry(const QString &url) +{ + QUrl cleanUrl(url); + cleanUrl.setPassword(QString()); + cleanUrl.setHost(cleanUrl.host().toLower()); + HistoryItem item(cleanUrl.toString(), QDateTime::currentDateTime()); + addHistoryItem(item); +} + +void HistoryManager::setHistory(const QList &history, bool loadedAndSorted) +{ + m_history = history; + + // verify that it is sorted by date + if (!loadedAndSorted) + qSort(m_history.begin(), m_history.end()); + + checkForExpired(); + + if (loadedAndSorted) { + m_lastSavedUrl = m_history.value(0).url; + } else { + m_lastSavedUrl = QString(); + m_saveTimer->changeOccurred(); + } + emit historyReset(); +} + +HistoryModel *HistoryManager::historyModel() const +{ + return m_historyModel; +} + +HistoryFilterModel *HistoryManager::historyFilterModel() const +{ + return m_historyFilterModel; +} + +HistoryTreeModel *HistoryManager::historyTreeModel() const +{ + return m_historyTreeModel; +} + +void HistoryManager::checkForExpired() +{ + if (m_historyLimit < 0 || m_history.isEmpty()) + return; + + QDateTime now = QDateTime::currentDateTime(); + int nextTimeout = 0; + + while (!m_history.isEmpty()) { + QDateTime checkForExpired = m_history.last().dateTime; + checkForExpired.setDate(checkForExpired.date().addDays(m_historyLimit)); + if (now.daysTo(checkForExpired) > 7) { + // check at most in a week to prevent int overflows on the timer + nextTimeout = 7 * 86400; + } else { + nextTimeout = now.secsTo(checkForExpired); + } + if (nextTimeout > 0) + break; + HistoryItem item = m_history.takeLast(); + // remove from saved file also + m_lastSavedUrl = QString(); + emit entryRemoved(item); + } + + if (nextTimeout > 0) + m_expiredTimer.start(nextTimeout * 1000); +} + +void HistoryManager::addHistoryItem(const HistoryItem &item) +{ + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return; + + m_history.prepend(item); + emit entryAdded(item); + if (m_history.count() == 1) + checkForExpired(); +} + +void HistoryManager::updateHistoryItem(const QUrl &url, const QString &title) +{ + for (int i = 0; i < m_history.count(); ++i) { + if (url == m_history.at(i).url) { + m_history[i].title = title; + m_saveTimer->changeOccurred(); + if (m_lastSavedUrl.isEmpty()) + m_lastSavedUrl = m_history.at(i).url; + emit entryUpdated(i); + break; + } + } +} + +int HistoryManager::historyLimit() const +{ + return m_historyLimit; +} + +void HistoryManager::setHistoryLimit(int limit) +{ + if (m_historyLimit == limit) + return; + m_historyLimit = limit; + checkForExpired(); + m_saveTimer->changeOccurred(); +} + +void HistoryManager::clear() +{ + m_history.clear(); + m_lastSavedUrl = QString(); + m_saveTimer->changeOccurred(); + m_saveTimer->saveIfNeccessary(); + historyReset(); +} + +void HistoryManager::loadSettings() +{ + // load settings + QSettings settings; + settings.beginGroup(QLatin1String("history")); + m_historyLimit = settings.value(QLatin1String("historyLimit"), 30).toInt(); +} + +void HistoryManager::load() +{ + loadSettings(); + + QFile historyFile(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + + QLatin1String("/history")); + if (!historyFile.exists()) + return; + if (!historyFile.open(QFile::ReadOnly)) { + qWarning() << "Unable to open history file" << historyFile.fileName(); + return; + } + + QList list; + QDataStream in(&historyFile); + // Double check that the history file is sorted as it is read in + bool needToSort = false; + HistoryItem lastInsertedItem; + QByteArray data; + QDataStream stream; + QBuffer buffer; + stream.setDevice(&buffer); + while (!historyFile.atEnd()) { + in >> data; + buffer.close(); + buffer.setBuffer(&data); + buffer.open(QIODevice::ReadOnly); + quint32 ver; + stream >> ver; + if (ver != HISTORY_VERSION) + continue; + HistoryItem item; + stream >> item.url; + stream >> item.dateTime; + stream >> item.title; + + if (!item.dateTime.isValid()) + continue; + + if (item == lastInsertedItem) { + if (lastInsertedItem.title.isEmpty() && !list.isEmpty()) + list[0].title = item.title; + continue; + } + + if (!needToSort && !list.isEmpty() && lastInsertedItem < item) + needToSort = true; + + list.prepend(item); + lastInsertedItem = item; + } + if (needToSort) + qSort(list.begin(), list.end()); + + setHistory(list, true); + + // If we had to sort re-write the whole history sorted + if (needToSort) { + m_lastSavedUrl = QString(); + m_saveTimer->changeOccurred(); + } +} + +void HistoryManager::save() +{ + QSettings settings; + settings.beginGroup(QLatin1String("history")); + settings.setValue(QLatin1String("historyLimit"), m_historyLimit); + + bool saveAll = m_lastSavedUrl.isEmpty(); + int first = m_history.count() - 1; + if (!saveAll) { + // find the first one to save + for (int i = 0; i < m_history.count(); ++i) { + if (m_history.at(i).url == m_lastSavedUrl) { + first = i - 1; + break; + } + } + } + if (first == m_history.count() - 1) + saveAll = true; + + QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + if (directory.isEmpty()) + directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName(); + if (!QFile::exists(directory)) { + QDir dir; + dir.mkpath(directory); + } + + QFile historyFile(directory + QLatin1String("/history")); + // When saving everything use a temporary file to prevent possible data loss. + QTemporaryFile tempFile; + tempFile.setAutoRemove(false); + bool open = false; + if (saveAll) { + open = tempFile.open(); + } else { + open = historyFile.open(QFile::Append); + } + + if (!open) { + qWarning() << "Unable to open history file for saving" + << (saveAll ? tempFile.fileName() : historyFile.fileName()); + return; + } + + QDataStream out(saveAll ? &tempFile : &historyFile); + for (int i = first; i >= 0; --i) { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + HistoryItem item = m_history.at(i); + stream << HISTORY_VERSION << item.url << item.dateTime << item.title; + out << data; + } + tempFile.close(); + + if (saveAll) { + if (historyFile.exists() && !historyFile.remove()) + qWarning() << "History: error removing old history." << historyFile.errorString(); + if (!tempFile.rename(historyFile.fileName())) + qWarning() << "History: error moving new history over old." << tempFile.errorString() << historyFile.fileName(); + } + m_lastSavedUrl = m_history.value(0).url; +} + +HistoryModel::HistoryModel(HistoryManager *history, QObject *parent) + : QAbstractTableModel(parent) + , m_history(history) +{ + Q_ASSERT(m_history); + connect(m_history, SIGNAL(historyReset()), + this, SLOT(historyReset())); + connect(m_history, SIGNAL(entryRemoved(const HistoryItem &)), + this, SLOT(historyReset())); + + connect(m_history, SIGNAL(entryAdded(const HistoryItem &)), + this, SLOT(entryAdded())); + connect(m_history, SIGNAL(entryUpdated(int)), + this, SLOT(entryUpdated(int))); +} + +void HistoryModel::historyReset() +{ + reset(); +} + +void HistoryModel::entryAdded() +{ + beginInsertRows(QModelIndex(), 0, 0); + endInsertRows(); +} + +void HistoryModel::entryUpdated(int offset) +{ + QModelIndex idx = index(offset, 0); + emit dataChanged(idx, idx); +} + +QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal + && role == Qt::DisplayRole) { + switch (section) { + case 0: return tr("Title"); + case 1: return tr("Address"); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + +QVariant HistoryModel::data(const QModelIndex &index, int role) const +{ + QList lst = m_history->history(); + if (index.row() < 0 || index.row() >= lst.size()) + return QVariant(); + + const HistoryItem &item = lst.at(index.row()); + switch (role) { + case DateTimeRole: + return item.dateTime; + case DateRole: + return item.dateTime.date(); + case UrlRole: + return QUrl(item.url); + case UrlStringRole: + return item.url; + case Qt::DisplayRole: + case Qt::EditRole: { + switch (index.column()) { + case 0: + // when there is no title try to generate one from the url + if (item.title.isEmpty()) { + QString page = QFileInfo(QUrl(item.url).path()).fileName(); + if (!page.isEmpty()) + return page; + return item.url; + } + return item.title; + case 1: + return item.url; + } + } + case Qt::DecorationRole: + if (index.column() == 0) { + return BrowserApplication::instance()->icon(item.url); + } + } + return QVariant(); +} + +int HistoryModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + +int HistoryModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : m_history->history().count(); +} + +bool HistoryModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + int lastRow = row + count - 1; + beginRemoveRows(parent, row, lastRow); + QList lst = m_history->history(); + for (int i = lastRow; i >= row; --i) + lst.removeAt(i); + disconnect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + m_history->setHistory(lst); + connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + endRemoveRows(); + return true; +} + +#define MOVEDROWS 15 + +/* + Maps the first bunch of items of the source model to the root +*/ +HistoryMenuModel::HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent) + , m_treeModel(sourceModel) +{ + setSourceModel(sourceModel); +} + +int HistoryMenuModel::bumpedRows() const +{ + QModelIndex first = m_treeModel->index(0, 0); + if (!first.isValid()) + return 0; + return qMin(m_treeModel->rowCount(first), MOVEDROWS); +} + +int HistoryMenuModel::columnCount(const QModelIndex &parent) const +{ + return m_treeModel->columnCount(mapToSource(parent)); +} + +int HistoryMenuModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) { + int folders = sourceModel()->rowCount(); + int bumpedItems = bumpedRows(); + if (bumpedItems <= MOVEDROWS + && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) + --folders; + return bumpedItems + folders; + } + + if (parent.internalId() == -1) { + if (parent.row() < bumpedRows()) + return 0; + } + + QModelIndex idx = mapToSource(parent); + int defaultCount = sourceModel()->rowCount(idx); + if (idx == sourceModel()->index(0, 0)) + return defaultCount - bumpedRows(); + return defaultCount; +} + +QModelIndex HistoryMenuModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + // currently not used or autotested + Q_ASSERT(false); + int sr = m_treeModel->mapToSource(sourceIndex).row(); + return createIndex(sourceIndex.row(), sourceIndex.column(), sr); +} + +QModelIndex HistoryMenuModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) + return QModelIndex(); + + if (proxyIndex.internalId() == -1) { + int bumpedItems = bumpedRows(); + if (proxyIndex.row() < bumpedItems) + return m_treeModel->index(proxyIndex.row(), proxyIndex.column(), m_treeModel->index(0, 0)); + if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(m_treeModel->index(0, 0))) + --bumpedItems; + return m_treeModel->index(proxyIndex.row() - bumpedItems, proxyIndex.column()); + } + + QModelIndex historyIndex = m_treeModel->sourceModel()->index(proxyIndex.internalId(), proxyIndex.column()); + QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); + return treeIndex; +} + +QModelIndex HistoryMenuModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 + || column < 0 || column >= columnCount(parent) + || parent.column() > 0) + return QModelIndex(); + if (!parent.isValid()) + return createIndex(row, column, -1); + + QModelIndex treeIndexParent = mapToSource(parent); + + int bumpedItems = 0; + if (treeIndexParent == m_treeModel->index(0, 0)) + bumpedItems = bumpedRows(); + QModelIndex treeIndex = m_treeModel->index(row + bumpedItems, column, treeIndexParent); + QModelIndex historyIndex = m_treeModel->mapToSource(treeIndex); + int historyRow = historyIndex.row(); + if (historyRow == -1) + historyRow = treeIndex.row(); + return createIndex(row, column, historyRow); +} + +QModelIndex HistoryMenuModel::parent(const QModelIndex &index) const +{ + int offset = index.internalId(); + if (offset == -1 || !index.isValid()) + return QModelIndex(); + + QModelIndex historyIndex = m_treeModel->sourceModel()->index(index.internalId(), 0); + QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); + QModelIndex treeIndexParent = treeIndex.parent(); + + int sr = m_treeModel->mapToSource(treeIndexParent).row(); + int bumpedItems = bumpedRows(); + if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) + --bumpedItems; + return createIndex(bumpedItems + treeIndexParent.row(), treeIndexParent.column(), sr); +} + + +HistoryMenu::HistoryMenu(QWidget *parent) + : ModelMenu(parent) + , m_history(0) +{ + connect(this, SIGNAL(activated(const QModelIndex &)), + this, SLOT(activated(const QModelIndex &))); + setHoverRole(HistoryModel::UrlStringRole); +} + +void HistoryMenu::activated(const QModelIndex &index) +{ + emit openUrl(index.data(HistoryModel::UrlRole).toUrl()); +} + +bool HistoryMenu::prePopulated() +{ + if (!m_history) { + m_history = BrowserApplication::historyManager(); + m_historyMenuModel = new HistoryMenuModel(m_history->historyTreeModel(), this); + setModel(m_historyMenuModel); + } + // initial actions + for (int i = 0; i < m_initialActions.count(); ++i) + addAction(m_initialActions.at(i)); + if (!m_initialActions.isEmpty()) + addSeparator(); + setFirstSeparator(m_historyMenuModel->bumpedRows()); + + return false; +} + +void HistoryMenu::postPopulated() +{ + if (m_history->history().count() > 0) + addSeparator(); + + QAction *showAllAction = new QAction(tr("Show All History"), this); + connect(showAllAction, SIGNAL(triggered()), this, SLOT(showHistoryDialog())); + addAction(showAllAction); + + QAction *clearAction = new QAction(tr("Clear History"), this); + connect(clearAction, SIGNAL(triggered()), m_history, SLOT(clear())); + addAction(clearAction); +} + +void HistoryMenu::showHistoryDialog() +{ + HistoryDialog *dialog = new HistoryDialog(this); + connect(dialog, SIGNAL(openUrl(const QUrl&)), + this, SIGNAL(openUrl(const QUrl&))); + dialog->show(); +} + +void HistoryMenu::setInitialActions(QList actions) +{ + m_initialActions = actions; + for (int i = 0; i < m_initialActions.count(); ++i) + addAction(m_initialActions.at(i)); +} + +TreeProxyModel::TreeProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + setSortRole(HistoryModel::DateTimeRole); + setFilterCaseSensitivity(Qt::CaseInsensitive); +} + +bool TreeProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + if (!source_parent.isValid()) + return true; + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + +HistoryDialog::HistoryDialog(QWidget *parent, HistoryManager *setHistory) : QDialog(parent) +{ + HistoryManager *history = setHistory; + if (!history) + history = BrowserApplication::historyManager(); + setupUi(this); + tree->setUniformRowHeights(true); + tree->setSelectionBehavior(QAbstractItemView::SelectRows); + tree->setTextElideMode(Qt::ElideMiddle); + QAbstractItemModel *model = history->historyTreeModel(); + TreeProxyModel *proxyModel = new TreeProxyModel(this); + connect(search, SIGNAL(textChanged(QString)), + proxyModel, SLOT(setFilterFixedString(QString))); + connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne())); + connect(removeAllButton, SIGNAL(clicked()), history, SLOT(clear())); + proxyModel->setSourceModel(model); + tree->setModel(proxyModel); + tree->setExpanded(proxyModel->index(0, 0), true); + tree->setAlternatingRowColors(true); + QFontMetrics fm(font()); + int header = fm.width(QLatin1Char('m')) * 40; + tree->header()->resizeSection(0, header); + tree->header()->setStretchLastSection(true); + connect(tree, SIGNAL(activated(const QModelIndex&)), + this, SLOT(open())); + tree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tree, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(customContextMenuRequested(const QPoint &))); +} + +void HistoryDialog::customContextMenuRequested(const QPoint &pos) +{ + QMenu menu; + QModelIndex index = tree->indexAt(pos); + index = index.sibling(index.row(), 0); + if (index.isValid() && !tree->model()->hasChildren(index)) { + menu.addAction(tr("Open"), this, SLOT(open())); + menu.addSeparator(); + menu.addAction(tr("Copy"), this, SLOT(copy())); + } + menu.addAction(tr("Delete"), tree, SLOT(removeOne())); + menu.exec(QCursor::pos()); +} + +void HistoryDialog::open() +{ + QModelIndex index = tree->currentIndex(); + if (!index.parent().isValid()) + return; + emit openUrl(index.data(HistoryModel::UrlRole).toUrl()); +} + +void HistoryDialog::copy() +{ + QModelIndex index = tree->currentIndex(); + if (!index.parent().isValid()) + return; + QString url = index.data(HistoryModel::UrlStringRole).toString(); + + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(url); +} + +HistoryFilterModel::HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent), + m_loaded(false) +{ + setSourceModel(sourceModel); +} + +int HistoryFilterModel::historyLocation(const QString &url) const +{ + load(); + if (!m_historyHash.contains(url)) + return 0; + return sourceModel()->rowCount() - m_historyHash.value(url); +} + +QVariant HistoryFilterModel::data(const QModelIndex &index, int role) const +{ + return QAbstractProxyModel::data(index, role); +} + +void HistoryFilterModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (sourceModel()) { + m_loaded = false; + connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &))); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } +} + +void HistoryFilterModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight)); +} + +QVariant HistoryFilterModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return sourceModel()->headerData(section, orientation, role); +} + +void HistoryFilterModel::sourceReset() +{ + m_loaded = false; + reset(); +} + +int HistoryFilterModel::rowCount(const QModelIndex &parent) const +{ + load(); + if (parent.isValid()) + return 0; + return m_historyHash.count(); +} + +int HistoryFilterModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + +QModelIndex HistoryFilterModel::mapToSource(const QModelIndex &proxyIndex) const +{ + load(); + int sourceRow = sourceModel()->rowCount() - proxyIndex.internalId(); + return sourceModel()->index(sourceRow, proxyIndex.column()); +} + +QModelIndex HistoryFilterModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + load(); + QString url = sourceIndex.data(HistoryModel::UrlStringRole).toString(); + if (!m_historyHash.contains(url)) + return QModelIndex(); + + // This can be done in a binary search, but we can't use qBinary find + // because it can't take: qBinaryFind(m_sourceRow.end(), m_sourceRow.begin(), v); + // so if this is a performance bottlneck then convert to binary search, until then + // the cleaner/easier to read code wins the day. + int realRow = -1; + int sourceModelRow = sourceModel()->rowCount() - sourceIndex.row(); + + for (int i = 0; i < m_sourceRow.count(); ++i) { + if (m_sourceRow.at(i) == sourceModelRow) { + realRow = i; + break; + } + } + if (realRow == -1) + return QModelIndex(); + + return createIndex(realRow, sourceIndex.column(), sourceModel()->rowCount() - sourceIndex.row()); +} + +QModelIndex HistoryFilterModel::index(int row, int column, const QModelIndex &parent) const +{ + load(); + if (row < 0 || row >= rowCount(parent) + || column < 0 || column >= columnCount(parent)) + return QModelIndex(); + + return createIndex(row, column, m_sourceRow[row]); +} + +QModelIndex HistoryFilterModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +void HistoryFilterModel::load() const +{ + if (m_loaded) + return; + m_sourceRow.clear(); + m_historyHash.clear(); + m_historyHash.reserve(sourceModel()->rowCount()); + for (int i = 0; i < sourceModel()->rowCount(); ++i) { + QModelIndex idx = sourceModel()->index(i, 0); + QString url = idx.data(HistoryModel::UrlStringRole).toString(); + if (!m_historyHash.contains(url)) { + m_sourceRow.append(sourceModel()->rowCount() - i); + m_historyHash[url] = sourceModel()->rowCount() - i; + } + } + m_loaded = true; +} + +void HistoryFilterModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(start == end && start == 0); + Q_UNUSED(end); + if (!m_loaded) + return; + QModelIndex idx = sourceModel()->index(start, 0, parent); + QString url = idx.data(HistoryModel::UrlStringRole).toString(); + if (m_historyHash.contains(url)) { + int sourceRow = sourceModel()->rowCount() - m_historyHash[url]; + int realRow = mapFromSource(sourceModel()->index(sourceRow, 0)).row(); + beginRemoveRows(QModelIndex(), realRow, realRow); + m_sourceRow.removeAt(realRow); + m_historyHash.remove(url); + endRemoveRows(); + } + beginInsertRows(QModelIndex(), 0, 0); + m_historyHash.insert(url, sourceModel()->rowCount() - start); + m_sourceRow.insert(0, sourceModel()->rowCount()); + endInsertRows(); +} + +void HistoryFilterModel::sourceRowsRemoved(const QModelIndex &, int start, int end) +{ + Q_UNUSED(start); + Q_UNUSED(end); + sourceReset(); +} + +/* + Removing a continuous block of rows will remove filtered rows too as this is + the users intention. +*/ +bool HistoryFilterModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid()) + return false; + int lastRow = row + count - 1; + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + beginRemoveRows(parent, row, lastRow); + int oldCount = rowCount(); + int start = sourceModel()->rowCount() - m_sourceRow.value(row); + int end = sourceModel()->rowCount() - m_sourceRow.value(lastRow); + sourceModel()->removeRows(start, end - start + 1); + endRemoveRows(); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + m_loaded = false; + if (oldCount - count != rowCount()) + reset(); + return true; +} + +HistoryCompletionModel::HistoryCompletionModel(QObject *parent) + : QAbstractProxyModel(parent) +{ +} + +QVariant HistoryCompletionModel::data(const QModelIndex &index, int role) const +{ + if (sourceModel() + && (role == Qt::EditRole || role == Qt::DisplayRole) + && index.isValid()) { + QModelIndex idx = mapToSource(index); + idx = idx.sibling(idx.row(), 1); + QString urlString = idx.data(HistoryModel::UrlStringRole).toString(); + if (index.row() % 2) { + QUrl url = urlString; + QString s = url.toString(QUrl::RemoveScheme + | QUrl::RemoveUserInfo + | QUrl::StripTrailingSlash); + return s.mid(2); // strip // from the front + } + return urlString; + } + return QAbstractProxyModel::data(index, role); +} + +int HistoryCompletionModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() || !sourceModel()) ? 0 : sourceModel()->rowCount(parent) * 2; +} + +int HistoryCompletionModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 1; +} + +QModelIndex HistoryCompletionModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + int row = sourceIndex.row() * 2; + return index(row, sourceIndex.column()); +} + +QModelIndex HistoryCompletionModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!sourceModel()) + return QModelIndex(); + int row = proxyIndex.row() / 2; + return sourceModel()->index(row, proxyIndex.column()); +} + +QModelIndex HistoryCompletionModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || row >= rowCount(parent) + || column < 0 || column >= columnCount(parent)) + return QModelIndex(); + return createIndex(row, column, 0); +} + +QModelIndex HistoryCompletionModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +void HistoryCompletionModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (newSourceModel) { + connect(newSourceModel, SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + } + + reset(); +} + +void HistoryCompletionModel::sourceReset() +{ + reset(); +} + +HistoryTreeModel::HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent) +{ + setSourceModel(sourceModel); +} + +QVariant HistoryTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return sourceModel()->headerData(section, orientation, role); +} + +QVariant HistoryTreeModel::data(const QModelIndex &index, int role) const +{ + if ((role == Qt::EditRole || role == Qt::DisplayRole)) { + int start = index.internalId(); + if (start == 0) { + int offset = sourceDateRow(index.row()); + if (index.column() == 0) { + QModelIndex idx = sourceModel()->index(offset, 0); + QDate date = idx.data(HistoryModel::DateRole).toDate(); + if (date == QDate::currentDate()) + return tr("Earlier Today"); + return date.toString(QLatin1String("dddd, MMMM d, yyyy")); + } + if (index.column() == 1) { + return tr("%1 items").arg(rowCount(index.sibling(index.row(), 0))); + } + } + } + if (role == Qt::DecorationRole && index.column() == 0 && !index.parent().isValid()) + return QIcon(QLatin1String(":history.png")); + if (role == HistoryModel::DateRole && index.column() == 0 && index.internalId() == 0) { + int offset = sourceDateRow(index.row()); + QModelIndex idx = sourceModel()->index(offset, 0); + return idx.data(HistoryModel::DateRole); + } + + return QAbstractProxyModel::data(index, role); +} + +int HistoryTreeModel::columnCount(const QModelIndex &parent) const +{ + return sourceModel()->columnCount(mapToSource(parent)); +} + +int HistoryTreeModel::rowCount(const QModelIndex &parent) const +{ + if ( parent.internalId() != 0 + || parent.column() > 0 + || !sourceModel()) + return 0; + + // row count OF dates + if (!parent.isValid()) { + if (!m_sourceRowCache.isEmpty()) + return m_sourceRowCache.count(); + QDate currentDate; + int rows = 0; + int totalRows = sourceModel()->rowCount(); + + for (int i = 0; i < totalRows; ++i) { + QDate rowDate = sourceModel()->index(i, 0).data(HistoryModel::DateRole).toDate(); + if (rowDate != currentDate) { + m_sourceRowCache.append(i); + currentDate = rowDate; + ++rows; + } + } + Q_ASSERT(m_sourceRowCache.count() == rows); + return rows; + } + + // row count FOR a date + int start = sourceDateRow(parent.row()); + int end = sourceDateRow(parent.row() + 1); + return (end - start); +} + +// Translate the top level date row into the offset where that date starts +int HistoryTreeModel::sourceDateRow(int row) const +{ + if (row <= 0) + return 0; + + if (m_sourceRowCache.isEmpty()) + rowCount(QModelIndex()); + + if (row >= m_sourceRowCache.count()) { + if (!sourceModel()) + return 0; + return sourceModel()->rowCount(); + } + return m_sourceRowCache.at(row); +} + +QModelIndex HistoryTreeModel::mapToSource(const QModelIndex &proxyIndex) const +{ + int offset = proxyIndex.internalId(); + if (offset == 0) + return QModelIndex(); + int startDateRow = sourceDateRow(offset - 1); + return sourceModel()->index(startDateRow + proxyIndex.row(), proxyIndex.column()); +} + +QModelIndex HistoryTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 + || column < 0 || column >= columnCount(parent) + || parent.column() > 0) + return QModelIndex(); + + if (!parent.isValid()) + return createIndex(row, column, 0); + return createIndex(row, column, parent.row() + 1); +} + +QModelIndex HistoryTreeModel::parent(const QModelIndex &index) const +{ + int offset = index.internalId(); + if (offset == 0 || !index.isValid()) + return QModelIndex(); + return createIndex(offset - 1, 0, 0); +} + +bool HistoryTreeModel::hasChildren(const QModelIndex &parent) const +{ + QModelIndex grandparent = parent.parent(); + if (!grandparent.isValid()) + return true; + return false; +} + +Qt::ItemFlags HistoryTreeModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; +} + +bool HistoryTreeModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || count <= 0 || row + count > rowCount(parent)) + return false; + + if (parent.isValid()) { + // removing pages + int offset = sourceDateRow(parent.row()); + return sourceModel()->removeRows(offset + row, count); + } else { + // removing whole dates + for (int i = row + count - 1; i >= row; --i) { + QModelIndex dateParent = index(i, 0); + int offset = sourceDateRow(dateParent.row()); + if (!sourceModel()->removeRows(offset, rowCount(dateParent))) + return false; + } + } + return true; +} + +void HistoryTreeModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (newSourceModel) { + connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + reset(); +} + +void HistoryTreeModel::sourceReset() +{ + m_sourceRowCache.clear(); + reset(); +} + +void HistoryTreeModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent); // Avoid warnings when compiling release + Q_ASSERT(!parent.isValid()); + if (start != 0 || start != end) { + m_sourceRowCache.clear(); + reset(); + return; + } + + m_sourceRowCache.clear(); + QModelIndex treeIndex = mapFromSource(sourceModel()->index(start, 0)); + QModelIndex treeParent = treeIndex.parent(); + if (rowCount(treeParent) == 1) { + beginInsertRows(QModelIndex(), 0, 0); + endInsertRows(); + } else { + beginInsertRows(treeParent, treeIndex.row(), treeIndex.row()); + endInsertRows(); + } +} + +QModelIndex HistoryTreeModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (!sourceIndex.isValid()) + return QModelIndex(); + + if (m_sourceRowCache.isEmpty()) + rowCount(QModelIndex()); + + QList::iterator it; + it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), sourceIndex.row()); + if (*it != sourceIndex.row()) + --it; + int dateRow = qMax(0, it - m_sourceRowCache.begin()); + int row = sourceIndex.row() - m_sourceRowCache.at(dateRow); + return createIndex(row, sourceIndex.column(), dateRow + 1); +} + +void HistoryTreeModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent); // Avoid warnings when compiling release + Q_ASSERT(!parent.isValid()); + if (m_sourceRowCache.isEmpty()) + return; + for (int i = end; i >= start;) { + QList::iterator it; + it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), i); + // playing it safe + if (it == m_sourceRowCache.end()) { + m_sourceRowCache.clear(); + reset(); + return; + } + + if (*it != i) + --it; + int row = qMax(0, it - m_sourceRowCache.begin()); + int offset = m_sourceRowCache[row]; + QModelIndex dateParent = index(row, 0); + // If we can remove all the rows in the date do that and skip over them + int rc = rowCount(dateParent); + if (i - rc + 1 == offset && start <= i - rc + 1) { + beginRemoveRows(QModelIndex(), row, row); + m_sourceRowCache.removeAt(row); + i -= rc + 1; + } else { + beginRemoveRows(dateParent, i - offset, i - offset); + ++row; + --i; + } + for (int j = row; j < m_sourceRowCache.count(); ++j) + --m_sourceRowCache[j]; + endRemoveRows(); + } +} + diff --git a/examples/gestures/browser/history.h b/examples/gestures/browser/history.h new file mode 100644 index 0000000..34b7c10 --- /dev/null +++ b/examples/gestures/browser/history.h @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HISTORY_H +#define HISTORY_H + +#include "modelmenu.h" + +#include +#include +#include +#include +#include + +#include + +#include + +class HistoryItem +{ +public: + HistoryItem() {} + HistoryItem(const QString &u, + const QDateTime &d = QDateTime(), const QString &t = QString()) + : title(t), url(u), dateTime(d) {} + + inline bool operator==(const HistoryItem &other) const + { return other.title == title + && other.url == url && other.dateTime == dateTime; } + + // history is sorted in reverse + inline bool operator <(const HistoryItem &other) const + { return dateTime > other.dateTime; } + + QString title; + QString url; + QDateTime dateTime; +}; + +class AutoSaver; +class HistoryModel; +class HistoryFilterModel; +class HistoryTreeModel; +class HistoryManager : public QWebHistoryInterface +{ + Q_OBJECT + Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit) + +signals: + void historyReset(); + void entryAdded(const HistoryItem &item); + void entryRemoved(const HistoryItem &item); + void entryUpdated(int offset); + +public: + HistoryManager(QObject *parent = 0); + ~HistoryManager(); + + bool historyContains(const QString &url) const; + void addHistoryEntry(const QString &url); + + void updateHistoryItem(const QUrl &url, const QString &title); + + int historyLimit() const; + void setHistoryLimit(int limit); + + QList history() const; + void setHistory(const QList &history, bool loadedAndSorted = false); + + // History manager keeps around these models for use by the completer and other classes + HistoryModel *historyModel() const; + HistoryFilterModel *historyFilterModel() const; + HistoryTreeModel *historyTreeModel() const; + +public slots: + void clear(); + void loadSettings(); + +private slots: + void save(); + void checkForExpired(); + +protected: + void addHistoryItem(const HistoryItem &item); + +private: + void load(); + + AutoSaver *m_saveTimer; + int m_historyLimit; + QTimer m_expiredTimer; + QList m_history; + QString m_lastSavedUrl; + + HistoryModel *m_historyModel; + HistoryFilterModel *m_historyFilterModel; + HistoryTreeModel *m_historyTreeModel; +}; + +class HistoryModel : public QAbstractTableModel +{ + Q_OBJECT + +public slots: + void historyReset(); + void entryAdded(); + void entryUpdated(int offset); + +public: + enum Roles { + DateRole = Qt::UserRole + 1, + DateTimeRole = Qt::UserRole + 2, + UrlRole = Qt::UserRole + 3, + UrlStringRole = Qt::UserRole + 4 + }; + + HistoryModel(HistoryManager *history, QObject *parent = 0); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + HistoryManager *m_history; +}; + +/*! + Proxy model that will remove any duplicate entries. + Both m_sourceRow and m_historyHash store their offsets not from + the front of the list, but as offsets from the back. + */ +class HistoryFilterModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent = 0); + + inline bool historyContains(const QString &url) const + { load(); return m_historyHash.contains(url); } + int historyLocation(const QString &url) const; + + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + void setSourceModel(QAbstractItemModel *sourceModel); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index= QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private slots: + void sourceReset(); + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &, int, int); + +private: + void load() const; + + mutable QList m_sourceRow; + mutable QHash m_historyHash; + mutable bool m_loaded; +}; + +/* + The history menu + - Removes the first twenty entries and puts them as children of the top level. + - If there are less then twenty entries then the first folder is also removed. + + The mapping is done by knowing that HistoryTreeModel is over a table + We store that row offset in our index's private data. +*/ +class HistoryMenuModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = 0); + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex & sourceIndex) const; + QModelIndex mapToSource(const QModelIndex & proxyIndex) const; + QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index = QModelIndex()) const; + + int bumpedRows() const; + +private: + HistoryTreeModel *m_treeModel; +}; + +// Menu that is dynamically populated from the history +class HistoryMenu : public ModelMenu +{ + Q_OBJECT + +signals: + void openUrl(const QUrl &url); + +public: + HistoryMenu(QWidget *parent = 0); + void setInitialActions(QList actions); + +protected: + bool prePopulated(); + void postPopulated(); + +private slots: + void activated(const QModelIndex &index); + void showHistoryDialog(); + +private: + HistoryManager *m_history; + HistoryMenuModel *m_historyMenuModel; + QList m_initialActions; +}; + +// proxy model for the history model that +// exposes each url http://www.foo.com and it url starting at the host www.foo.com +class HistoryCompletionModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryCompletionModel(QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index= QModelIndex()) const; + void setSourceModel(QAbstractItemModel *sourceModel); + +private slots: + void sourceReset(); + +}; + +// proxy model for the history model that converts the list +// into a tree, one top level node per day. +// Used in the HistoryDialog. +class HistoryTreeModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index= QModelIndex()) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void setSourceModel(QAbstractItemModel *sourceModel); + +private slots: + void sourceReset(); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + +private: + int sourceDateRow(int row) const; + mutable QList m_sourceRowCache; + +}; + +// A modified QSortFilterProxyModel that always accepts the root nodes in the tree +// so filtering is only done on the children. +// Used in the HistoryDialog +class TreeProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + TreeProxyModel(QObject *parent = 0); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; +}; + +#include "ui_history.h" + +class HistoryDialog : public QDialog, public Ui_HistoryDialog +{ + Q_OBJECT + +signals: + void openUrl(const QUrl &url); + +public: + HistoryDialog(QWidget *parent = 0, HistoryManager *history = 0); + +private slots: + void customContextMenuRequested(const QPoint &pos); + void open(); + void copy(); + +}; + +#endif // HISTORY_H + diff --git a/examples/gestures/browser/history.ui b/examples/gestures/browser/history.ui new file mode 100644 index 0000000..0944940 --- /dev/null +++ b/examples/gestures/browser/history.ui @@ -0,0 +1,106 @@ + + HistoryDialog + + + + 0 + 0 + 758 + 450 + + + + History + + + + + + Qt::Horizontal + + + + 252 + 20 + + + + + + + + + + + + + + + + &Remove + + + + + + + Remove &All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + SearchLineEdit + QLineEdit +
searchlineedit.h
+
+ + EditTreeView + QTreeView +
edittreeview.h
+
+
+ + + + buttonBox + accepted() + HistoryDialog + accept() + + + 472 + 329 + + + 461 + 356 + + + + +
diff --git a/examples/gestures/browser/main.cpp b/examples/gestures/browser/main.cpp new file mode 100644 index 0000000..d551ab4 --- /dev/null +++ b/examples/gestures/browser/main.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "browserapplication.h" + +int main(int argc, char **argv) +{ + Q_INIT_RESOURCE(data); + BrowserApplication application(argc, argv); + if (!application.isTheOnlyBrowser()) + return 0; + application.newMainWindow(); + return application.exec(); +} + diff --git a/examples/gestures/browser/modelmenu.cpp b/examples/gestures/browser/modelmenu.cpp new file mode 100644 index 0000000..980b62b --- /dev/null +++ b/examples/gestures/browser/modelmenu.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "modelmenu.h" + +#include +#include + +ModelMenu::ModelMenu(QWidget * parent) + : QMenu(parent) + , m_maxRows(7) + , m_firstSeparator(-1) + , m_maxWidth(-1) + , m_hoverRole(0) + , m_separatorRole(0) + , m_model(0) +{ + connect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShow())); +} + +bool ModelMenu::prePopulated() +{ + return false; +} + +void ModelMenu::postPopulated() +{ +} + +void ModelMenu::setModel(QAbstractItemModel *model) +{ + m_model = model; +} + +QAbstractItemModel *ModelMenu::model() const +{ + return m_model; +} + +void ModelMenu::setMaxRows(int max) +{ + m_maxRows = max; +} + +int ModelMenu::maxRows() const +{ + return m_maxRows; +} + +void ModelMenu::setFirstSeparator(int offset) +{ + m_firstSeparator = offset; +} + +int ModelMenu::firstSeparator() const +{ + return m_firstSeparator; +} + +void ModelMenu::setRootIndex(const QModelIndex &index) +{ + m_root = index; +} + +QModelIndex ModelMenu::rootIndex() const +{ + return m_root; +} + +void ModelMenu::setHoverRole(int role) +{ + m_hoverRole = role; +} + +int ModelMenu::hoverRole() const +{ + return m_hoverRole; +} + +void ModelMenu::setSeparatorRole(int role) +{ + m_separatorRole = role; +} + +int ModelMenu::separatorRole() const +{ + return m_separatorRole; +} + +Q_DECLARE_METATYPE(QModelIndex) +void ModelMenu::aboutToShow() +{ + if (QMenu *menu = qobject_cast(sender())) { + QVariant v = menu->menuAction()->data(); + if (v.canConvert()) { + QModelIndex idx = qvariant_cast(v); + createMenu(idx, -1, menu, menu); + disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShow())); + return; + } + } + + clear(); + if (prePopulated()) + addSeparator(); + int max = m_maxRows; + if (max != -1) + max += m_firstSeparator; + createMenu(m_root, max, this, this); + postPopulated(); +} + +void ModelMenu::createMenu(const QModelIndex &parent, int max, QMenu *parentMenu, QMenu *menu) +{ + if (!menu) { + QString title = parent.data().toString(); + menu = new QMenu(title, this); + QIcon icon = qvariant_cast(parent.data(Qt::DecorationRole)); + menu->setIcon(icon); + parentMenu->addMenu(menu); + QVariant v; + v.setValue(parent); + menu->menuAction()->setData(v); + connect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShow())); + return; + } + + int end = m_model->rowCount(parent); + if (max != -1) + end = qMin(max, end); + + connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(triggered(QAction*))); + connect(menu, SIGNAL(hovered(QAction*)), this, SLOT(hovered(QAction*))); + + for (int i = 0; i < end; ++i) { + QModelIndex idx = m_model->index(i, 0, parent); + if (m_model->hasChildren(idx)) { + createMenu(idx, -1, menu); + } else { + if (m_separatorRole != 0 + && idx.data(m_separatorRole).toBool()) + addSeparator(); + else + menu->addAction(makeAction(idx)); + } + if (menu == this && i == m_firstSeparator - 1) + addSeparator(); + } +} + +QAction *ModelMenu::makeAction(const QModelIndex &index) +{ + QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); + QAction *action = makeAction(icon, index.data().toString(), this); + QVariant v; + v.setValue(index); + action->setData(v); + return action; +} + +QAction *ModelMenu::makeAction(const QIcon &icon, const QString &text, QObject *parent) +{ + QFontMetrics fm(font()); + if (-1 == m_maxWidth) + m_maxWidth = fm.width(QLatin1Char('m')) * 30; + QString smallText = fm.elidedText(text, Qt::ElideMiddle, m_maxWidth); + return new QAction(icon, smallText, parent); +} + +void ModelMenu::triggered(QAction *action) +{ + QVariant v = action->data(); + if (v.canConvert()) { + QModelIndex idx = qvariant_cast(v); + emit activated(idx); + } +} + +void ModelMenu::hovered(QAction *action) +{ + QVariant v = action->data(); + if (v.canConvert()) { + QModelIndex idx = qvariant_cast(v); + QString hoveredString = idx.data(m_hoverRole).toString(); + if (!hoveredString.isEmpty()) + emit hovered(hoveredString); + } +} + diff --git a/examples/gestures/browser/modelmenu.h b/examples/gestures/browser/modelmenu.h new file mode 100644 index 0000000..b888f05 --- /dev/null +++ b/examples/gestures/browser/modelmenu.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MODELMENU_H +#define MODELMENU_H + +#include +#include + +// A QMenu that is dynamically populated from a QAbstractItemModel +class ModelMenu : public QMenu +{ + Q_OBJECT + +signals: + void activated(const QModelIndex &index); + void hovered(const QString &text); + +public: + ModelMenu(QWidget *parent = 0); + + void setModel(QAbstractItemModel *model); + QAbstractItemModel *model() const; + + void setMaxRows(int max); + int maxRows() const; + + void setFirstSeparator(int offset); + int firstSeparator() const; + + void setRootIndex(const QModelIndex &index); + QModelIndex rootIndex() const; + + void setHoverRole(int role); + int hoverRole() const; + + void setSeparatorRole(int role); + int separatorRole() const; + + QAction *makeAction(const QIcon &icon, const QString &text, QObject *parent); + +protected: + // add any actions before the tree, return true if any actions are added. + virtual bool prePopulated(); + // add any actions after the tree + virtual void postPopulated(); + // put all of the children of parent into menu up to max + void createMenu(const QModelIndex &parent, int max, QMenu *parentMenu = 0, QMenu *menu = 0); + +private slots: + void aboutToShow(); + void triggered(QAction *action); + void hovered(QAction *action); + +private: + QAction *makeAction(const QModelIndex &index); + int m_maxRows; + int m_firstSeparator; + int m_maxWidth; + int m_hoverRole; + int m_separatorRole; + QAbstractItemModel *m_model; + QPersistentModelIndex m_root; +}; + +#endif // MODELMENU_H + diff --git a/examples/gestures/browser/networkaccessmanager.cpp b/examples/gestures/browser/networkaccessmanager.cpp new file mode 100644 index 0000000..f777937 --- /dev/null +++ b/examples/gestures/browser/networkaccessmanager.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "networkaccessmanager.h" + +#include "browserapplication.h" +#include "browsermainwindow.h" +#include "ui_passworddialog.h" +#include "ui_proxy.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +NetworkAccessManager::NetworkAccessManager(QObject *parent) + : QNetworkAccessManager(parent) +{ + connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*))); +#ifndef QT_NO_OPENSSL + connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList&)), + SLOT(sslErrors(QNetworkReply*, const QList&))); +#endif + loadSettings(); + + QNetworkDiskCache *diskCache = new QNetworkDiskCache(this); + QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation); + diskCache->setCacheDirectory(location); + setCache(diskCache); +} + +void NetworkAccessManager::loadSettings() +{ + QSettings settings; + settings.beginGroup(QLatin1String("proxy")); + QNetworkProxy proxy; + if (settings.value(QLatin1String("enabled"), false).toBool()) { + if (settings.value(QLatin1String("type"), 0).toInt() == 0) + proxy = QNetworkProxy::Socks5Proxy; + else + proxy = QNetworkProxy::HttpProxy; + proxy.setHostName(settings.value(QLatin1String("hostName")).toString()); + proxy.setPort(settings.value(QLatin1String("port"), 1080).toInt()); + proxy.setUser(settings.value(QLatin1String("userName")).toString()); + proxy.setPassword(settings.value(QLatin1String("password")).toString()); + } + setProxy(proxy); +} + +void NetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *auth) +{ + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + + QDialog dialog(mainWindow); + dialog.setWindowFlags(Qt::Sheet); + + Ui::PasswordDialog passwordDialog; + passwordDialog.setupUi(&dialog); + + passwordDialog.iconLabel->setText(QString()); + passwordDialog.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32)); + + QString introMessage = tr("Enter username and password for \"%1\" at %2"); + introMessage = introMessage.arg(Qt::escape(reply->url().toString())).arg(Qt::escape(reply->url().toString())); + passwordDialog.introLabel->setText(introMessage); + passwordDialog.introLabel->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) { + auth->setUser(passwordDialog.userNameLineEdit->text()); + auth->setPassword(passwordDialog.passwordLineEdit->text()); + } +} + +void NetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth) +{ + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + + QDialog dialog(mainWindow); + dialog.setWindowFlags(Qt::Sheet); + + Ui::ProxyDialog proxyDialog; + proxyDialog.setupUi(&dialog); + + proxyDialog.iconLabel->setText(QString()); + proxyDialog.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32)); + + QString introMessage = tr("Connect to proxy \"%1\" using:"); + introMessage = introMessage.arg(Qt::escape(proxy.hostName())); + proxyDialog.introLabel->setText(introMessage); + proxyDialog.introLabel->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) { + auth->setUser(proxyDialog.userNameLineEdit->text()); + auth->setPassword(proxyDialog.passwordLineEdit->text()); + } +} + +#ifndef QT_NO_OPENSSL +void NetworkAccessManager::sslErrors(QNetworkReply *reply, const QList &error) +{ + // check if SSL certificate has been trusted already + QString replyHost = reply->url().host() + ":" + reply->url().port(); + if(! sslTrustedHostList.contains(replyHost)) { + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + + QStringList errorStrings; + for (int i = 0; i < error.count(); ++i) + errorStrings += error.at(i).errorString(); + QString errors = errorStrings.join(QLatin1String("\n")); + int ret = QMessageBox::warning(mainWindow, QCoreApplication::applicationName(), + tr("SSL Errors:\n\n%1\n\n%2\n\n" + "Do you want to ignore these errors for this host?").arg(reply->url().toString()).arg(errors), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (ret == QMessageBox::Yes) { + reply->ignoreSslErrors(); + sslTrustedHostList.append(replyHost); + } + } +} +#endif diff --git a/examples/gestures/browser/networkaccessmanager.h b/examples/gestures/browser/networkaccessmanager.h new file mode 100644 index 0000000..6c938ac --- /dev/null +++ b/examples/gestures/browser/networkaccessmanager.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NETWORKACCESSMANAGER_H +#define NETWORKACCESSMANAGER_H + +#include + +class NetworkAccessManager : public QNetworkAccessManager +{ + Q_OBJECT + +public: + NetworkAccessManager(QObject *parent = 0); + +private: + QList sslTrustedHostList; + +public slots: + void loadSettings(); + +private slots: + void authenticationRequired(QNetworkReply *reply, QAuthenticator *auth); + void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); +#ifndef QT_NO_OPENSSL + void sslErrors(QNetworkReply *reply, const QList &error); +#endif +}; + +#endif // NETWORKACCESSMANAGER_H diff --git a/examples/gestures/browser/passworddialog.ui b/examples/gestures/browser/passworddialog.ui new file mode 100644 index 0000000..7c16658 --- /dev/null +++ b/examples/gestures/browser/passworddialog.ui @@ -0,0 +1,111 @@ + + PasswordDialog + + + + 0 + 0 + 399 + 148 + + + + Authentication Required + + + + + + + + DUMMY ICON + + + + + + + + 0 + 0 + + + + INTRO TEXT DUMMY + + + + + + + + + Username: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PasswordDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PasswordDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/examples/gestures/browser/proxy.ui b/examples/gestures/browser/proxy.ui new file mode 100644 index 0000000..62a8be6 --- /dev/null +++ b/examples/gestures/browser/proxy.ui @@ -0,0 +1,104 @@ + + ProxyDialog + + + + 0 + 0 + 369 + 144 + + + + Proxy Authentication + + + + + + ICON + + + + + + + Connect to proxy + + + true + + + + + + + Username: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ProxyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ProxyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/examples/gestures/browser/searchlineedit.cpp b/examples/gestures/browser/searchlineedit.cpp new file mode 100644 index 0000000..09d80fb --- /dev/null +++ b/examples/gestures/browser/searchlineedit.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "searchlineedit.h" + +#include +#include +#include +#include +#include + +ClearButton::ClearButton(QWidget *parent) + : QAbstractButton(parent) +{ + setCursor(Qt::ArrowCursor); + setToolTip(tr("Clear")); + setVisible(false); + setFocusPolicy(Qt::NoFocus); +} + +void ClearButton::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter painter(this); + int height = this->height(); + + painter.setRenderHint(QPainter::Antialiasing, true); + QColor color = palette().color(QPalette::Mid); + painter.setBrush(isDown() + ? palette().color(QPalette::Dark) + : palette().color(QPalette::Mid)); + painter.setPen(painter.brush().color()); + int size = width(); + int offset = size / 5; + int radius = size - offset * 2; + painter.drawEllipse(offset, offset, radius, radius); + + painter.setPen(palette().color(QPalette::Base)); + int border = offset * 2; + painter.drawLine(border, border, width() - border, height - border); + painter.drawLine(border, height - border, width() - border, border); +} + +void ClearButton::textChanged(const QString &text) +{ + setVisible(!text.isEmpty()); +} + +/* + Search icon on the left hand side of the search widget + When a menu is set a down arrow appears + */ +class SearchButton : public QAbstractButton { +public: + SearchButton(QWidget *parent = 0); + void paintEvent(QPaintEvent *event); + QMenu *m_menu; + +protected: + void mousePressEvent(QMouseEvent *event); +}; + +SearchButton::SearchButton(QWidget *parent) + : QAbstractButton(parent), + m_menu(0) +{ + setObjectName(QLatin1String("SearchButton")); + setCursor(Qt::ArrowCursor); + setFocusPolicy(Qt::NoFocus); +} + +void SearchButton::mousePressEvent(QMouseEvent *event) +{ + if (m_menu && event->button() == Qt::LeftButton) { + QWidget *p = parentWidget(); + if (p) { + QPoint r = p->mapToGlobal(QPoint(0, p->height())); + m_menu->exec(QPoint(r.x() + height() / 2, r.y())); + } + event->accept(); + } + QAbstractButton::mousePressEvent(event); +} + +void SearchButton::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainterPath myPath; + + int radius = (height() / 5) * 2; + QRect circle(height() / 3 - 1, height() / 4, radius, radius); + myPath.addEllipse(circle); + + myPath.arcMoveTo(circle, 300); + QPointF c = myPath.currentPosition(); + int diff = height() / 7; + myPath.lineTo(qMin(width() - 2, (int)c.x() + diff), c.y() + diff); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setPen(QPen(Qt::darkGray, 2)); + painter.drawPath(myPath); + + if (m_menu) { + QPainterPath dropPath; + dropPath.arcMoveTo(circle, 320); + QPointF c = dropPath.currentPosition(); + c = QPointF(c.x() + 3.5, c.y() + 0.5); + dropPath.moveTo(c); + dropPath.lineTo(c.x() + 4, c.y()); + dropPath.lineTo(c.x() + 2, c.y() + 2); + dropPath.closeSubpath(); + painter.setPen(Qt::darkGray); + painter.setBrush(Qt::darkGray); + painter.setRenderHint(QPainter::Antialiasing, false); + painter.drawPath(dropPath); + } + painter.end(); +} + +/* + SearchLineEdit is an enhanced QLineEdit + - A Search icon on the left with optional menu + - When there is no text and doesn't have focus an "inactive text" is displayed + - When there is text a clear button is displayed on the right hand side + */ +SearchLineEdit::SearchLineEdit(QWidget *parent) : ExLineEdit(parent), + m_searchButton(new SearchButton(this)) +{ + connect(lineEdit(), SIGNAL(textChanged(const QString &)), + this, SIGNAL(textChanged(const QString &))); + setLeftWidget(m_searchButton); + m_inactiveText = tr("Search"); + + QSizePolicy policy = sizePolicy(); + setSizePolicy(QSizePolicy::Preferred, policy.verticalPolicy()); +} + +void SearchLineEdit::paintEvent(QPaintEvent *event) +{ + if (lineEdit()->text().isEmpty() && !hasFocus() && !m_inactiveText.isEmpty()) { + ExLineEdit::paintEvent(event); + QStyleOptionFrameV2 panel; + initStyleOption(&panel); + QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this); + QFontMetrics fm = fontMetrics(); + int horizontalMargin = lineEdit()->x(); + QRect lineRect(horizontalMargin + r.x(), r.y() + (r.height() - fm.height() + 1) / 2, + r.width() - 2 * horizontalMargin, fm.height()); + QPainter painter(this); + painter.setPen(palette().brush(QPalette::Disabled, QPalette::Text).color()); + painter.drawText(lineRect, Qt::AlignLeft|Qt::AlignVCenter, m_inactiveText); + } else { + ExLineEdit::paintEvent(event); + } +} + +void SearchLineEdit::resizeEvent(QResizeEvent *event) +{ + updateGeometries(); + ExLineEdit::resizeEvent(event); +} + +void SearchLineEdit::updateGeometries() +{ + int menuHeight = height(); + int menuWidth = menuHeight + 1; + if (!m_searchButton->m_menu) + menuWidth = (menuHeight / 5) * 4; + m_searchButton->resize(QSize(menuWidth, menuHeight)); +} + +QString SearchLineEdit::inactiveText() const +{ + return m_inactiveText; +} + +void SearchLineEdit::setInactiveText(const QString &text) +{ + m_inactiveText = text; +} + +void SearchLineEdit::setMenu(QMenu *menu) +{ + if (m_searchButton->m_menu) + m_searchButton->m_menu->deleteLater(); + m_searchButton->m_menu = menu; + updateGeometries(); +} + +QMenu *SearchLineEdit::menu() const +{ + if (!m_searchButton->m_menu) { + m_searchButton->m_menu = new QMenu(m_searchButton); + if (isVisible()) + (const_cast(this))->updateGeometries(); + } + return m_searchButton->m_menu; +} + diff --git a/examples/gestures/browser/searchlineedit.h b/examples/gestures/browser/searchlineedit.h new file mode 100644 index 0000000..4fc887c --- /dev/null +++ b/examples/gestures/browser/searchlineedit.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SEARCHLINEEDIT_H +#define SEARCHLINEEDIT_H + +#include "urllineedit.h" + +#include +#include + +QT_BEGIN_NAMESPACE +class QMenu; +QT_END_NAMESPACE + +class SearchButton; + +/* + Clear button on the right hand side of the search widget. + Hidden by default + "A circle with an X in it" + */ +class ClearButton : public QAbstractButton +{ + Q_OBJECT + +public: + ClearButton(QWidget *parent = 0); + void paintEvent(QPaintEvent *event); + +public slots: + void textChanged(const QString &text); +}; + + +class SearchLineEdit : public ExLineEdit +{ + Q_OBJECT + Q_PROPERTY(QString inactiveText READ inactiveText WRITE setInactiveText) + +signals: + void textChanged(const QString &text); + +public: + SearchLineEdit(QWidget *parent = 0); + + QString inactiveText() const; + void setInactiveText(const QString &text); + + QMenu *menu() const; + void setMenu(QMenu *menu); + +protected: + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + +private: + void updateGeometries(); + + SearchButton *m_searchButton; + QString m_inactiveText; +}; + +#endif // SEARCHLINEEDIT_H + diff --git a/examples/gestures/browser/settings.cpp b/examples/gestures/browser/settings.cpp new file mode 100644 index 0000000..8f9f408 --- /dev/null +++ b/examples/gestures/browser/settings.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "settings.h" + +#include "browserapplication.h" +#include "browsermainwindow.h" +#include "cookiejar.h" +#include "history.h" +#include "networkaccessmanager.h" +#include "webview.h" + +#include +#include +#include + +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + connect(exceptionsButton, SIGNAL(clicked()), this, SLOT(showExceptions())); + connect(setHomeToCurrentPageButton, SIGNAL(clicked()), this, SLOT(setHomeToCurrentPage())); + connect(cookiesButton, SIGNAL(clicked()), this, SLOT(showCookies())); + connect(standardFontButton, SIGNAL(clicked()), this, SLOT(chooseFont())); + connect(fixedFontButton, SIGNAL(clicked()), this, SLOT(chooseFixedFont())); + + loadDefaults(); + loadFromSettings(); +} + +void SettingsDialog::loadDefaults() +{ + QWebSettings *defaultSettings = QWebSettings::globalSettings(); + QString standardFontFamily = defaultSettings->fontFamily(QWebSettings::StandardFont); + int standardFontSize = defaultSettings->fontSize(QWebSettings::DefaultFontSize); + standardFont = QFont(standardFontFamily, standardFontSize); + standardLabel->setText(QString(QLatin1String("%1 %2")).arg(standardFont.family()).arg(standardFont.pointSize())); + + QString fixedFontFamily = defaultSettings->fontFamily(QWebSettings::FixedFont); + int fixedFontSize = defaultSettings->fontSize(QWebSettings::DefaultFixedFontSize); + fixedFont = QFont(fixedFontFamily, fixedFontSize); + fixedLabel->setText(QString(QLatin1String("%1 %2")).arg(fixedFont.family()).arg(fixedFont.pointSize())); + + downloadsLocation->setText(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation)); + + enableJavascript->setChecked(defaultSettings->testAttribute(QWebSettings::JavascriptEnabled)); + enablePlugins->setChecked(defaultSettings->testAttribute(QWebSettings::PluginsEnabled)); +} + +void SettingsDialog::loadFromSettings() +{ + QSettings settings; + settings.beginGroup(QLatin1String("MainWindow")); + QString defaultHome = QLatin1String("http://qtsoftware.com"); + homeLineEdit->setText(settings.value(QLatin1String("home"), defaultHome).toString()); + settings.endGroup(); + + settings.beginGroup(QLatin1String("history")); + int historyExpire = settings.value(QLatin1String("historyExpire")).toInt(); + int idx = 0; + switch (historyExpire) { + case 1: idx = 0; break; + case 7: idx = 1; break; + case 14: idx = 2; break; + case 30: idx = 3; break; + case 365: idx = 4; break; + case -1: idx = 5; break; + default: + idx = 5; + } + expireHistory->setCurrentIndex(idx); + settings.endGroup(); + + settings.beginGroup(QLatin1String("downloadmanager")); + QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), downloadsLocation->text()).toString(); + downloadsLocation->setText(downloadDirectory); + settings.endGroup(); + + settings.beginGroup(QLatin1String("general")); + openLinksIn->setCurrentIndex(settings.value(QLatin1String("openLinksIn"), openLinksIn->currentIndex()).toInt()); + + settings.endGroup(); + + // Appearance + settings.beginGroup(QLatin1String("websettings")); + fixedFont = qVariantValue(settings.value(QLatin1String("fixedFont"), fixedFont)); + standardFont = qVariantValue(settings.value(QLatin1String("standardFont"), standardFont)); + + standardLabel->setText(QString(QLatin1String("%1 %2")).arg(standardFont.family()).arg(standardFont.pointSize())); + fixedLabel->setText(QString(QLatin1String("%1 %2")).arg(fixedFont.family()).arg(fixedFont.pointSize())); + + enableJavascript->setChecked(settings.value(QLatin1String("enableJavascript"), enableJavascript->isChecked()).toBool()); + enablePlugins->setChecked(settings.value(QLatin1String("enablePlugins"), enablePlugins->isChecked()).toBool()); + userStyleSheet->setText(settings.value(QLatin1String("userStyleSheet")).toUrl().toString()); + settings.endGroup(); + + // Privacy + settings.beginGroup(QLatin1String("cookies")); + + CookieJar *jar = BrowserApplication::cookieJar(); + QByteArray value = settings.value(QLatin1String("acceptCookies"), QLatin1String("AcceptOnlyFromSitesNavigatedTo")).toByteArray(); + QMetaEnum acceptPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("AcceptPolicy")); + CookieJar::AcceptPolicy acceptCookies = acceptPolicyEnum.keyToValue(value) == -1 ? + CookieJar::AcceptOnlyFromSitesNavigatedTo : + static_cast(acceptPolicyEnum.keyToValue(value)); + switch(acceptCookies) { + case CookieJar::AcceptAlways: + acceptCombo->setCurrentIndex(0); + break; + case CookieJar::AcceptNever: + acceptCombo->setCurrentIndex(1); + break; + case CookieJar::AcceptOnlyFromSitesNavigatedTo: + acceptCombo->setCurrentIndex(2); + break; + } + + value = settings.value(QLatin1String("keepCookiesUntil"), QLatin1String("Expire")).toByteArray(); + QMetaEnum keepPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("KeepPolicy")); + CookieJar::KeepPolicy keepCookies = keepPolicyEnum.keyToValue(value) == -1 ? + CookieJar::KeepUntilExpire : + static_cast(keepPolicyEnum.keyToValue(value)); + switch(keepCookies) { + case CookieJar::KeepUntilExpire: + keepUntilCombo->setCurrentIndex(0); + break; + case CookieJar::KeepUntilExit: + keepUntilCombo->setCurrentIndex(1); + break; + case CookieJar::KeepUntilTimeLimit: + keepUntilCombo->setCurrentIndex(2); + break; + } + settings.endGroup(); + + + // Proxy + settings.beginGroup(QLatin1String("proxy")); + proxySupport->setChecked(settings.value(QLatin1String("enabled"), false).toBool()); + proxyType->setCurrentIndex(settings.value(QLatin1String("type"), 0).toInt()); + proxyHostName->setText(settings.value(QLatin1String("hostName")).toString()); + proxyPort->setValue(settings.value(QLatin1String("port"), 1080).toInt()); + proxyUserName->setText(settings.value(QLatin1String("userName")).toString()); + proxyPassword->setText(settings.value(QLatin1String("password")).toString()); + settings.endGroup(); +} + +void SettingsDialog::saveToSettings() +{ + QSettings settings; + settings.beginGroup(QLatin1String("MainWindow")); + settings.setValue(QLatin1String("home"), homeLineEdit->text()); + settings.endGroup(); + + settings.beginGroup(QLatin1String("general")); + settings.setValue(QLatin1String("openLinksIn"), openLinksIn->currentIndex()); + settings.endGroup(); + + settings.beginGroup(QLatin1String("history")); + int historyExpire = expireHistory->currentIndex(); + int idx = -1; + switch (historyExpire) { + case 0: idx = 1; break; + case 1: idx = 7; break; + case 2: idx = 14; break; + case 3: idx = 30; break; + case 4: idx = 365; break; + case 5: idx = -1; break; + } + settings.setValue(QLatin1String("historyExpire"), idx); + settings.endGroup(); + + // Appearance + settings.beginGroup(QLatin1String("websettings")); + settings.setValue(QLatin1String("fixedFont"), fixedFont); + settings.setValue(QLatin1String("standardFont"), standardFont); + settings.setValue(QLatin1String("enableJavascript"), enableJavascript->isChecked()); + settings.setValue(QLatin1String("enablePlugins"), enablePlugins->isChecked()); + QString userStyleSheetString = userStyleSheet->text(); + if (QFile::exists(userStyleSheetString)) + settings.setValue(QLatin1String("userStyleSheet"), QUrl::fromLocalFile(userStyleSheetString)); + else + settings.setValue(QLatin1String("userStyleSheet"), QUrl(userStyleSheetString)); + settings.endGroup(); + + //Privacy + settings.beginGroup(QLatin1String("cookies")); + + CookieJar::KeepPolicy keepCookies; + switch(acceptCombo->currentIndex()) { + default: + case 0: + keepCookies = CookieJar::KeepUntilExpire; + break; + case 1: + keepCookies = CookieJar::KeepUntilExit; + break; + case 2: + keepCookies = CookieJar::KeepUntilTimeLimit; + break; + } + CookieJar *jar = BrowserApplication::cookieJar(); + QMetaEnum acceptPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("AcceptPolicy")); + settings.setValue(QLatin1String("acceptCookies"), QLatin1String(acceptPolicyEnum.valueToKey(keepCookies))); + + CookieJar::KeepPolicy keepPolicy; + switch(keepUntilCombo->currentIndex()) { + default: + case 0: + keepPolicy = CookieJar::KeepUntilExpire; + break; + case 1: + keepPolicy = CookieJar::KeepUntilExit; + break; + case 2: + keepPolicy = CookieJar::KeepUntilTimeLimit; + break; + } + + QMetaEnum keepPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("KeepPolicy")); + settings.setValue(QLatin1String("keepCookiesUntil"), QLatin1String(keepPolicyEnum.valueToKey(keepPolicy))); + + settings.endGroup(); + + // proxy + settings.beginGroup(QLatin1String("proxy")); + settings.setValue(QLatin1String("enabled"), proxySupport->isChecked()); + settings.setValue(QLatin1String("type"), proxyType->currentIndex()); + settings.setValue(QLatin1String("hostName"), proxyHostName->text()); + settings.setValue(QLatin1String("port"), proxyPort->text()); + settings.setValue(QLatin1String("userName"), proxyUserName->text()); + settings.setValue(QLatin1String("password"), proxyPassword->text()); + settings.endGroup(); + + BrowserApplication::instance()->loadSettings(); + BrowserApplication::networkAccessManager()->loadSettings(); + BrowserApplication::cookieJar()->loadSettings(); + BrowserApplication::historyManager()->loadSettings(); +} + +void SettingsDialog::accept() +{ + saveToSettings(); + QDialog::accept(); +} + +void SettingsDialog::showCookies() +{ + CookiesDialog *dialog = new CookiesDialog(BrowserApplication::cookieJar(), this); + dialog->exec(); +} + +void SettingsDialog::showExceptions() +{ + CookiesExceptionsDialog *dialog = new CookiesExceptionsDialog(BrowserApplication::cookieJar(), this); + dialog->exec(); +} + +void SettingsDialog::chooseFont() +{ + bool ok; + QFont font = QFontDialog::getFont(&ok, standardFont, this); + if ( ok ) { + standardFont = font; + standardLabel->setText(QString(QLatin1String("%1 %2")).arg(font.family()).arg(font.pointSize())); + } +} + +void SettingsDialog::chooseFixedFont() +{ + bool ok; + QFont font = QFontDialog::getFont(&ok, fixedFont, this); + if ( ok ) { + fixedFont = font; + fixedLabel->setText(QString(QLatin1String("%1 %2")).arg(font.family()).arg(font.pointSize())); + } +} + +void SettingsDialog::setHomeToCurrentPage() +{ + BrowserMainWindow *mw = static_cast(parent()); + WebView *webView = mw->currentTab(); + if (webView) + homeLineEdit->setText(webView->url().toString()); +} + diff --git a/examples/gestures/browser/settings.h b/examples/gestures/browser/settings.h new file mode 100644 index 0000000..a125dc2 --- /dev/null +++ b/examples/gestures/browser/settings.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include +#include "ui_settings.h" + +class SettingsDialog : public QDialog, public Ui_Settings +{ + Q_OBJECT + +public: + SettingsDialog(QWidget *parent = 0); + void accept(); + +private slots: + void loadDefaults(); + void loadFromSettings(); + void saveToSettings(); + + void setHomeToCurrentPage(); + void showCookies(); + void showExceptions(); + + void chooseFont(); + void chooseFixedFont(); + +private: + QFont standardFont; + QFont fixedFont; +}; + +#endif // SETTINGS_H + diff --git a/examples/gestures/browser/settings.ui b/examples/gestures/browser/settings.ui new file mode 100644 index 0000000..3491ce0 --- /dev/null +++ b/examples/gestures/browser/settings.ui @@ -0,0 +1,614 @@ + + Settings + + + + 0 + 0 + 657 + 322 + + + + Settings + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + 0 + + + + + 0 + 0 + 627 + 243 + + + + General + + + + + + Home: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Set to current page + + + + + + + Qt::Horizontal + + + + 280 + 18 + + + + + + + + Remove history items: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + After one day + + + + + After one week + + + + + After two weeks + + + + + After one month + + + + + After one year + + + + + Manually + + + + + + + + Save downloads to: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Open links from applications: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + In a tab in the current window + + + + + In a new window + + + + + + + + Qt::Vertical + + + + 391 + 262 + + + + + + + + + + 0 + 0 + 627 + 243 + + + + Appearance + + + + + + Standard font: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + Times 16 + + + Qt::AlignCenter + + + + + + + Select... + + + + + + + Fixed-width font: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::StyledPanel + + + Courier 13 + + + Qt::AlignCenter + + + + + + + Select... + + + + + + + Qt::Vertical + + + + 20 + 93 + + + + + + + + + + 0 + 0 + 627 + 243 + + + + Privacy + + + + + + Web Content + + + + + + Enable Plugins + + + true + + + + + + + Enable Javascript + + + true + + + + + + + + + + Cookies + + + + + + Accept Cookies: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Always + + + + + Never + + + + + Only from sites you navigate to + + + + + + + + Exceptions... + + + + + + + Keep until: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + They expire + + + + + I exit the application + + + + + At most 90 days + + + + + + + + Cookies... + + + + + + + + + + Qt::Vertical + + + + 371 + 177 + + + + + + + + + + 0 + 0 + 627 + 243 + + + + Proxy + + + + + + Enable proxy + + + true + + + + + + Type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Socks5 + + + + + Http + + + + + + + + Host: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Port: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 10000 + + + 1080 + + + + + + + Qt::Horizontal + + + + 293 + 20 + + + + + + + + User Name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Password: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QLineEdit::Password + + + + + + + Qt::Vertical + + + + 20 + 8 + + + + + + + + + + + + Advanced + + + + + + Style Sheet: + + + + + + + + + + Qt::Vertical + + + + 20 + 176 + + + + + + + + + + + + + + buttonBox + accepted() + Settings + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Settings + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/examples/gestures/browser/squeezelabel.cpp b/examples/gestures/browser/squeezelabel.cpp new file mode 100644 index 0000000..f56db2d --- /dev/null +++ b/examples/gestures/browser/squeezelabel.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "squeezelabel.h" + +SqueezeLabel::SqueezeLabel(QWidget *parent) : QLabel(parent) +{ +} + +void SqueezeLabel::paintEvent(QPaintEvent *event) +{ + QFontMetrics fm = fontMetrics(); + if (fm.width(text()) > contentsRect().width()) { + QString elided = fm.elidedText(text(), Qt::ElideMiddle, width()); + QString oldText = text(); + setText(elided); + QLabel::paintEvent(event); + setText(oldText); + } else { + QLabel::paintEvent(event); + } +} + diff --git a/examples/gestures/browser/squeezelabel.h b/examples/gestures/browser/squeezelabel.h new file mode 100644 index 0000000..f23b9a9 --- /dev/null +++ b/examples/gestures/browser/squeezelabel.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SQUEEZELABEL_H +#define SQUEEZELABEL_H + +#include + +class SqueezeLabel : public QLabel +{ + Q_OBJECT + +public: + SqueezeLabel(QWidget *parent = 0); + +protected: + void paintEvent(QPaintEvent *event); + +}; + +#endif // SQUEEZELABEL_H + diff --git a/examples/gestures/browser/tabwidget.cpp b/examples/gestures/browser/tabwidget.cpp new file mode 100644 index 0000000..0c26350 --- /dev/null +++ b/examples/gestures/browser/tabwidget.cpp @@ -0,0 +1,830 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tabwidget.h" + +#include "browserapplication.h" +#include "browsermainwindow.h" +#include "history.h" +#include "urllineedit.h" +#include "webview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +TabBar::TabBar(QWidget *parent) + : QTabBar(parent) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + setAcceptDrops(true); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(contextMenuRequested(const QPoint &))); + + QString alt = QLatin1String("Alt+%1"); + for (int i = 1; i <= 10; ++i) { + int key = i; + if (key == 10) + key = 0; + QShortcut *shortCut = new QShortcut(alt.arg(key), this); + m_tabShortcuts.append(shortCut); + connect(shortCut, SIGNAL(activated()), this, SLOT(selectTabAction())); + } + setTabsClosable(true); + connect(this, SIGNAL(tabCloseRequested(int)), + this, SIGNAL(closeTab(int))); + setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab); + setMovable(true); +} + +void TabBar::selectTabAction() +{ + if (QShortcut *shortCut = qobject_cast(sender())) { + int index = m_tabShortcuts.indexOf(shortCut); + if (index == 0) + index = 10; + setCurrentIndex(index); + } +} + +void TabBar::contextMenuRequested(const QPoint &position) +{ + QMenu menu; + menu.addAction(tr("New &Tab"), this, SIGNAL(newTab()), QKeySequence::AddTab); + int index = tabAt(position); + if (-1 != index) { + QAction *action = menu.addAction(tr("Clone Tab"), + this, SLOT(cloneTab())); + action->setData(index); + + menu.addSeparator(); + + action = menu.addAction(tr("&Close Tab"), + this, SLOT(closeTab()), QKeySequence::Close); + action->setData(index); + + action = menu.addAction(tr("Close &Other Tabs"), + this, SLOT(closeOtherTabs())); + action->setData(index); + + menu.addSeparator(); + + action = menu.addAction(tr("Reload Tab"), + this, SLOT(reloadTab()), QKeySequence::Refresh); + action->setData(index); + } else { + menu.addSeparator(); + } + menu.addAction(tr("Reload All Tabs"), this, SIGNAL(reloadAllTabs())); + menu.exec(QCursor::pos()); +} + +void TabBar::cloneTab() +{ + if (QAction *action = qobject_cast(sender())) { + int index = action->data().toInt(); + emit cloneTab(index); + } +} + +void TabBar::closeTab() +{ + if (QAction *action = qobject_cast(sender())) { + int index = action->data().toInt(); + emit closeTab(index); + } +} + +void TabBar::closeOtherTabs() +{ + if (QAction *action = qobject_cast(sender())) { + int index = action->data().toInt(); + emit closeOtherTabs(index); + } +} + +void TabBar::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + m_dragStartPos = event->pos(); + QTabBar::mousePressEvent(event); +} + +void TabBar::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() == Qt::LeftButton) { + int diffX = event->pos().x() - m_dragStartPos.x(); + int diffY = event->pos().y() - m_dragStartPos.y(); + if ((event->pos() - m_dragStartPos).manhattanLength() > QApplication::startDragDistance() + && diffX < 3 && diffX > -3 + && diffY < -10) { + QDrag *drag = new QDrag(this); + QMimeData *mimeData = new QMimeData; + QList urls; + int index = tabAt(event->pos()); + QUrl url = tabData(index).toUrl(); + urls.append(url); + mimeData->setUrls(urls); + mimeData->setText(tabText(index)); + mimeData->setData(QLatin1String("action"), "tab-reordering"); + drag->setMimeData(mimeData); + drag->exec(); + } + } + QTabBar::mouseMoveEvent(event); +} + +// When index is -1 index chooses the current tab +void TabWidget::reloadTab(int index) +{ + if (index < 0) + index = currentIndex(); + if (index < 0 || index >= count()) + return; + + QWidget *widget = this->widget(index); + if (WebView *tab = qobject_cast(widget)) + tab->reload(); +} + +void TabBar::reloadTab() +{ + if (QAction *action = qobject_cast(sender())) { + int index = action->data().toInt(); + emit reloadTab(index); + } +} + +TabWidget::TabWidget(QWidget *parent) + : QTabWidget(parent) + , m_recentlyClosedTabsAction(0) + , m_newTabAction(0) + , m_closeTabAction(0) + , m_nextTabAction(0) + , m_previousTabAction(0) + , m_recentlyClosedTabsMenu(0) + , m_lineEditCompleter(0) + , m_lineEdits(0) + , m_tabBar(new TabBar(this)) +{ + setElideMode(Qt::ElideRight); + + connect(m_tabBar, SIGNAL(newTab()), this, SLOT(newTab())); + connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(closeTab(int))); + connect(m_tabBar, SIGNAL(cloneTab(int)), this, SLOT(cloneTab(int))); + connect(m_tabBar, SIGNAL(closeOtherTabs(int)), this, SLOT(closeOtherTabs(int))); + connect(m_tabBar, SIGNAL(reloadTab(int)), this, SLOT(reloadTab(int))); + connect(m_tabBar, SIGNAL(reloadAllTabs()), this, SLOT(reloadAllTabs())); + connect(m_tabBar, SIGNAL(tabMoved(int, int)), this, SLOT(moveTab(int, int))); + setTabBar(m_tabBar); + setDocumentMode(true); + + // Actions + m_newTabAction = new QAction(QIcon(QLatin1String(":addtab.png")), tr("New &Tab"), this); + m_newTabAction->setShortcuts(QKeySequence::AddTab); + m_newTabAction->setIconVisibleInMenu(false); + connect(m_newTabAction, SIGNAL(triggered()), this, SLOT(newTab())); + + m_closeTabAction = new QAction(QIcon(QLatin1String(":closetab.png")), tr("&Close Tab"), this); + m_closeTabAction->setShortcuts(QKeySequence::Close); + m_closeTabAction->setIconVisibleInMenu(false); + connect(m_closeTabAction, SIGNAL(triggered()), this, SLOT(closeTab())); + + m_nextTabAction = new QAction(tr("Show Next Tab"), this); + QList shortcuts; + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BraceRight)); + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_PageDown)); + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BracketRight)); + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Less)); + m_nextTabAction->setShortcuts(shortcuts); + connect(m_nextTabAction, SIGNAL(triggered()), this, SLOT(nextTab())); + + m_previousTabAction = new QAction(tr("Show Previous Tab"), this); + shortcuts.clear(); + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BraceLeft)); + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_PageUp)); + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BracketLeft)); + shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Greater)); + m_previousTabAction->setShortcuts(shortcuts); + connect(m_previousTabAction, SIGNAL(triggered()), this, SLOT(previousTab())); + + m_recentlyClosedTabsMenu = new QMenu(this); + connect(m_recentlyClosedTabsMenu, SIGNAL(aboutToShow()), + this, SLOT(aboutToShowRecentTabsMenu())); + connect(m_recentlyClosedTabsMenu, SIGNAL(triggered(QAction *)), + this, SLOT(aboutToShowRecentTriggeredAction(QAction *))); + m_recentlyClosedTabsAction = new QAction(tr("Recently Closed Tabs"), this); + m_recentlyClosedTabsAction->setMenu(m_recentlyClosedTabsMenu); + m_recentlyClosedTabsAction->setEnabled(false); + + connect(this, SIGNAL(currentChanged(int)), + this, SLOT(currentChanged(int))); + + m_lineEdits = new QStackedWidget(this); +} + +void TabWidget::clear() +{ + // clear the recently closed tabs + m_recentlyClosedTabs.clear(); + // clear the line edit history + for (int i = 0; i < m_lineEdits->count(); ++i) { + QLineEdit *qLineEdit = lineEdit(i); + qLineEdit->setText(qLineEdit->text()); + } +} + +void TabWidget::moveTab(int fromIndex, int toIndex) +{ + QWidget *lineEdit = m_lineEdits->widget(fromIndex); + m_lineEdits->removeWidget(lineEdit); + m_lineEdits->insertWidget(toIndex, lineEdit); +} + +void TabWidget::addWebAction(QAction *action, QWebPage::WebAction webAction) +{ + if (!action) + return; + m_actions.append(new WebActionMapper(action, webAction, this)); +} + +void TabWidget::currentChanged(int index) +{ + WebView *webView = this->webView(index); + if (!webView) + return; + + Q_ASSERT(m_lineEdits->count() == count()); + + WebView *oldWebView = this->webView(m_lineEdits->currentIndex()); + if (oldWebView) { + disconnect(oldWebView, SIGNAL(statusBarMessage(const QString&)), + this, SIGNAL(showStatusBarMessage(const QString&))); + disconnect(oldWebView->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), + this, SIGNAL(linkHovered(const QString&))); + disconnect(oldWebView, SIGNAL(loadProgress(int)), + this, SIGNAL(loadProgress(int))); + } + + connect(webView, SIGNAL(statusBarMessage(const QString&)), + this, SIGNAL(showStatusBarMessage(const QString&))); + connect(webView->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), + this, SIGNAL(linkHovered(const QString&))); + connect(webView, SIGNAL(loadProgress(int)), + this, SIGNAL(loadProgress(int))); + + for (int i = 0; i < m_actions.count(); ++i) { + WebActionMapper *mapper = m_actions[i]; + mapper->updateCurrent(webView->page()); + } + emit setCurrentTitle(webView->title()); + m_lineEdits->setCurrentIndex(index); + emit loadProgress(webView->progress()); + emit showStatusBarMessage(webView->lastStatusBarText()); + if (webView->url().isEmpty()) + m_lineEdits->currentWidget()->setFocus(); + else + webView->setFocus(); +} + +QAction *TabWidget::newTabAction() const +{ + return m_newTabAction; +} + +QAction *TabWidget::closeTabAction() const +{ + return m_closeTabAction; +} + +QAction *TabWidget::recentlyClosedTabsAction() const +{ + return m_recentlyClosedTabsAction; +} + +QAction *TabWidget::nextTabAction() const +{ + return m_nextTabAction; +} + +QAction *TabWidget::previousTabAction() const +{ + return m_previousTabAction; +} + +QWidget *TabWidget::lineEditStack() const +{ + return m_lineEdits; +} + +QLineEdit *TabWidget::currentLineEdit() const +{ + return lineEdit(m_lineEdits->currentIndex()); +} + +WebView *TabWidget::currentWebView() const +{ + return webView(currentIndex()); +} + +QLineEdit *TabWidget::lineEdit(int index) const +{ + UrlLineEdit *urlLineEdit = qobject_cast(m_lineEdits->widget(index)); + if (urlLineEdit) + return urlLineEdit->lineEdit(); + return 0; +} + +WebView *TabWidget::webView(int index) const +{ + QWidget *widget = this->widget(index); + if (WebView *webView = qobject_cast(widget)) { + return webView; + } else { + // optimization to delay creating the first webview + if (count() == 1) { + TabWidget *that = const_cast(this); + that->setUpdatesEnabled(false); + that->newTab(); + that->closeTab(0); + that->setUpdatesEnabled(true); + return currentWebView(); + } + } + return 0; +} + +int TabWidget::webViewIndex(WebView *webView) const +{ + int index = indexOf(webView); + return index; +} + +WebView *TabWidget::newTab(bool makeCurrent) +{ + // line edit + UrlLineEdit *urlLineEdit = new UrlLineEdit; + QLineEdit *lineEdit = urlLineEdit->lineEdit(); + if (!m_lineEditCompleter && count() > 0) { + HistoryCompletionModel *completionModel = new HistoryCompletionModel(this); + completionModel->setSourceModel(BrowserApplication::historyManager()->historyFilterModel()); + m_lineEditCompleter = new QCompleter(completionModel, this); + // Should this be in Qt by default? + QAbstractItemView *popup = m_lineEditCompleter->popup(); + QListView *listView = qobject_cast(popup); + if (listView) + listView->setUniformItemSizes(true); + } + lineEdit->setCompleter(m_lineEditCompleter); + connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(lineEditReturnPressed())); + m_lineEdits->addWidget(urlLineEdit); + m_lineEdits->setSizePolicy(lineEdit->sizePolicy()); + + // optimization to delay creating the more expensive WebView, history, etc + if (count() == 0) { + QWidget *emptyWidget = new QWidget; + QPalette p = emptyWidget->palette(); + p.setColor(QPalette::Window, palette().color(QPalette::Base)); + emptyWidget->setPalette(p); + emptyWidget->setAutoFillBackground(true); + disconnect(this, SIGNAL(currentChanged(int)), + this, SLOT(currentChanged(int))); + addTab(emptyWidget, tr("(Untitled)")); + connect(this, SIGNAL(currentChanged(int)), + this, SLOT(currentChanged(int))); + return 0; + } + + // webview + WebView *webView = new WebView; + urlLineEdit->setWebView(webView); + connect(webView, SIGNAL(loadStarted()), + this, SLOT(webViewLoadStarted())); + connect(webView, SIGNAL(loadFinished(bool)), + this, SLOT(webViewIconChanged())); + connect(webView, SIGNAL(iconChanged()), + this, SLOT(webViewIconChanged())); + connect(webView, SIGNAL(titleChanged(const QString &)), + this, SLOT(webViewTitleChanged(const QString &))); + connect(webView, SIGNAL(urlChanged(const QUrl &)), + this, SLOT(webViewUrlChanged(const QUrl &))); + connect(webView->page(), SIGNAL(windowCloseRequested()), + this, SLOT(windowCloseRequested())); + connect(webView->page(), SIGNAL(geometryChangeRequested(const QRect &)), + this, SIGNAL(geometryChangeRequested(const QRect &))); + connect(webView->page(), SIGNAL(printRequested(QWebFrame *)), + this, SIGNAL(printRequested(QWebFrame *))); + connect(webView->page(), SIGNAL(menuBarVisibilityChangeRequested(bool)), + this, SIGNAL(menuBarVisibilityChangeRequested(bool))); + connect(webView->page(), SIGNAL(statusBarVisibilityChangeRequested(bool)), + this, SIGNAL(statusBarVisibilityChangeRequested(bool))); + connect(webView->page(), SIGNAL(toolBarVisibilityChangeRequested(bool)), + this, SIGNAL(toolBarVisibilityChangeRequested(bool))); + addTab(webView, tr("(Untitled)")); + if (makeCurrent) + setCurrentWidget(webView); + + // webview actions + for (int i = 0; i < m_actions.count(); ++i) { + WebActionMapper *mapper = m_actions[i]; + mapper->addChild(webView->page()->action(mapper->webAction())); + } + + if (count() == 1) + currentChanged(currentIndex()); + emit tabsChanged(); + return webView; +} + +void TabWidget::reloadAllTabs() +{ + for (int i = 0; i < count(); ++i) { + QWidget *tabWidget = widget(i); + if (WebView *tab = qobject_cast(tabWidget)) { + tab->reload(); + } + } +} + +void TabWidget::lineEditReturnPressed() +{ + if (QLineEdit *lineEdit = qobject_cast(sender())) { + emit loadPage(lineEdit->text()); + if (m_lineEdits->currentWidget() == lineEdit) + currentWebView()->setFocus(); + } +} + +void TabWidget::windowCloseRequested() +{ + WebPage *webPage = qobject_cast(sender()); + WebView *webView = qobject_cast(webPage->view()); + int index = webViewIndex(webView); + if (index >= 0) { + if (count() == 1) + webView->webPage()->mainWindow()->close(); + else + closeTab(index); + } +} + +void TabWidget::closeOtherTabs(int index) +{ + if (-1 == index) + return; + for (int i = count() - 1; i > index; --i) + closeTab(i); + for (int i = index - 1; i >= 0; --i) + closeTab(i); +} + +// When index is -1 index chooses the current tab +void TabWidget::cloneTab(int index) +{ + if (index < 0) + index = currentIndex(); + if (index < 0 || index >= count()) + return; + WebView *tab = newTab(false); + tab->setUrl(webView(index)->url()); +} + +// When index is -1 index chooses the current tab +void TabWidget::closeTab(int index) +{ + if (index < 0) + index = currentIndex(); + if (index < 0 || index >= count()) + return; + + bool hasFocus = false; + if (WebView *tab = webView(index)) { + if (tab->isModified()) { + QMessageBox closeConfirmation(tab); + closeConfirmation.setWindowFlags(Qt::Sheet); + closeConfirmation.setWindowTitle(tr("Do you really want to close this page?")); + closeConfirmation.setInformativeText(tr("You have modified this page and when closing it you would lose the modification.\n" + "Do you really want to close this page?\n")); + closeConfirmation.setIcon(QMessageBox::Question); + closeConfirmation.addButton(QMessageBox::Yes); + closeConfirmation.addButton(QMessageBox::No); + closeConfirmation.setEscapeButton(QMessageBox::No); + if (closeConfirmation.exec() == QMessageBox::No) + return; + } + hasFocus = tab->hasFocus(); + + m_recentlyClosedTabsAction->setEnabled(true); + m_recentlyClosedTabs.prepend(tab->url()); + if (m_recentlyClosedTabs.size() >= TabWidget::m_recentlyClosedTabsSize) + m_recentlyClosedTabs.removeLast(); + } + QWidget *lineEdit = m_lineEdits->widget(index); + m_lineEdits->removeWidget(lineEdit); + lineEdit->deleteLater(); + QWidget *webView = widget(index); + removeTab(index); + webView->deleteLater(); + emit tabsChanged(); + if (hasFocus && count() > 0) + currentWebView()->setFocus(); + if (count() == 0) + emit lastTabClosed(); +} + +void TabWidget::webViewLoadStarted() +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) { + QIcon icon(QLatin1String(":loading.gif")); + setTabIcon(index, icon); + } +} + +void TabWidget::webViewIconChanged() +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) { + QIcon icon = BrowserApplication::instance()->icon(webView->url()); + setTabIcon(index, icon); + } +} + +void TabWidget::webViewTitleChanged(const QString &title) +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) { + setTabText(index, title); + } + if (currentIndex() == index) + emit setCurrentTitle(title); + BrowserApplication::historyManager()->updateHistoryItem(webView->url(), title); +} + +void TabWidget::webViewUrlChanged(const QUrl &url) +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) { + m_tabBar->setTabData(index, url); + } + emit tabsChanged(); +} + +void TabWidget::aboutToShowRecentTabsMenu() +{ + m_recentlyClosedTabsMenu->clear(); + for (int i = 0; i < m_recentlyClosedTabs.count(); ++i) { + QAction *action = new QAction(m_recentlyClosedTabsMenu); + action->setData(m_recentlyClosedTabs.at(i)); + QIcon icon = BrowserApplication::instance()->icon(m_recentlyClosedTabs.at(i)); + action->setIcon(icon); + action->setText(m_recentlyClosedTabs.at(i).toString()); + m_recentlyClosedTabsMenu->addAction(action); + } +} + +void TabWidget::aboutToShowRecentTriggeredAction(QAction *action) +{ + QUrl url = action->data().toUrl(); + loadUrlInCurrentTab(url); +} + +void TabWidget::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (!childAt(event->pos()) + // Remove the line below when QTabWidget does not have a one pixel frame + && event->pos().y() < (tabBar()->y() + tabBar()->height())) { + newTab(); + return; + } + QTabWidget::mouseDoubleClickEvent(event); +} + +void TabWidget::contextMenuEvent(QContextMenuEvent *event) +{ + if (!childAt(event->pos())) { + m_tabBar->contextMenuRequested(event->pos()); + return; + } + QTabWidget::contextMenuEvent(event); +} + +void TabWidget::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::MidButton && !childAt(event->pos()) + // Remove the line below when QTabWidget does not have a one pixel frame + && event->pos().y() < (tabBar()->y() + tabBar()->height())) { + QUrl url(QApplication::clipboard()->text(QClipboard::Selection)); + if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) { + WebView *webView = newTab(); + webView->setUrl(url); + } + } +} + +void TabWidget::loadUrlInCurrentTab(const QUrl &url) +{ + WebView *webView = currentWebView(); + if (webView) { + webView->loadUrl(url); + webView->setFocus(); + } +} + +void TabWidget::nextTab() +{ + int next = currentIndex() + 1; + if (next == count()) + next = 0; + setCurrentIndex(next); +} + +void TabWidget::previousTab() +{ + int next = currentIndex() - 1; + if (next < 0) + next = count() - 1; + setCurrentIndex(next); +} + +static const qint32 TabWidgetMagic = 0xaa; + +QByteArray TabWidget::saveState() const +{ + int version = 1; + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + stream << qint32(TabWidgetMagic); + stream << qint32(version); + + QStringList tabs; + for (int i = 0; i < count(); ++i) { + if (WebView *tab = qobject_cast(widget(i))) { + tabs.append(tab->url().toString()); + } else { + tabs.append(QString::null); + } + } + stream << tabs; + stream << currentIndex(); + return data; +} + +bool TabWidget::restoreState(const QByteArray &state) +{ + int version = 1; + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + if (stream.atEnd()) + return false; + + qint32 marker; + qint32 v; + stream >> marker; + stream >> v; + if (marker != TabWidgetMagic || v != version) + return false; + + QStringList openTabs; + stream >> openTabs; + + for (int i = 0; i < openTabs.count(); ++i) { + if (i != 0) + newTab(); + loadPage(openTabs.at(i)); + } + + int currentTab; + stream >> currentTab; + setCurrentIndex(currentTab); + + return true; +} + +WebActionMapper::WebActionMapper(QAction *root, QWebPage::WebAction webAction, QObject *parent) + : QObject(parent) + , m_currentParent(0) + , m_root(root) + , m_webAction(webAction) +{ + if (!m_root) + return; + connect(m_root, SIGNAL(triggered()), this, SLOT(rootTriggered())); + connect(root, SIGNAL(destroyed(QObject *)), this, SLOT(rootDestroyed())); + root->setEnabled(false); +} + +void WebActionMapper::rootDestroyed() +{ + m_root = 0; +} + +void WebActionMapper::currentDestroyed() +{ + updateCurrent(0); +} + +void WebActionMapper::addChild(QAction *action) +{ + if (!action) + return; + connect(action, SIGNAL(changed()), this, SLOT(childChanged())); +} + +QWebPage::WebAction WebActionMapper::webAction() const +{ + return m_webAction; +} + +void WebActionMapper::rootTriggered() +{ + if (m_currentParent) { + QAction *gotoAction = m_currentParent->action(m_webAction); + gotoAction->trigger(); + } +} + +void WebActionMapper::childChanged() +{ + if (QAction *source = qobject_cast(sender())) { + if (m_root + && m_currentParent + && source->parent() == m_currentParent) { + m_root->setChecked(source->isChecked()); + m_root->setEnabled(source->isEnabled()); + } + } +} + +void WebActionMapper::updateCurrent(QWebPage *currentParent) +{ + if (m_currentParent) + disconnect(m_currentParent, SIGNAL(destroyed(QObject *)), + this, SLOT(currentDestroyed())); + + m_currentParent = currentParent; + if (!m_root) + return; + if (!m_currentParent) { + m_root->setEnabled(false); + m_root->setChecked(false); + return; + } + QAction *source = m_currentParent->action(m_webAction); + m_root->setChecked(source->isChecked()); + m_root->setEnabled(source->isEnabled()); + connect(m_currentParent, SIGNAL(destroyed(QObject *)), + this, SLOT(currentDestroyed())); +} + diff --git a/examples/gestures/browser/tabwidget.h b/examples/gestures/browser/tabwidget.h new file mode 100644 index 0000000..3f4e9f0 --- /dev/null +++ b/examples/gestures/browser/tabwidget.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TABWIDGET_H +#define TABWIDGET_H + +#include + +#include +/* + Tab bar with a few more features such as a context menu and shortcuts + */ +class TabBar : public QTabBar +{ + Q_OBJECT + +signals: + void newTab(); + void cloneTab(int index); + void closeTab(int index); + void closeOtherTabs(int index); + void reloadTab(int index); + void reloadAllTabs(); + void tabMoveRequested(int fromIndex, int toIndex); + +public: + TabBar(QWidget *parent = 0); + +protected: + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + +private slots: + void selectTabAction(); + void cloneTab(); + void closeTab(); + void closeOtherTabs(); + void reloadTab(); + void contextMenuRequested(const QPoint &position); + +private: + QList m_tabShortcuts; + friend class TabWidget; + + QPoint m_dragStartPos; + int m_dragCurrentIndex; +}; + +#include + +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE +class WebView; +/*! + A proxy object that connects a single browser action + to one child webpage action at a time. + + Example usage: used to keep the main window stop action in sync with + the current tabs webview's stop action. + */ +class WebActionMapper : public QObject +{ + Q_OBJECT + +public: + WebActionMapper(QAction *root, QWebPage::WebAction webAction, QObject *parent); + QWebPage::WebAction webAction() const; + void addChild(QAction *action); + void updateCurrent(QWebPage *currentParent); + +private slots: + void rootTriggered(); + void childChanged(); + void rootDestroyed(); + void currentDestroyed(); + +private: + QWebPage *m_currentParent; + QAction *m_root; + QWebPage::WebAction m_webAction; +}; + +#include +#include +QT_BEGIN_NAMESPACE +class QCompleter; +class QLineEdit; +class QMenu; +class QStackedWidget; +QT_END_NAMESPACE +/*! + TabWidget that contains WebViews and a stack widget of associated line edits. + + Connects up the current tab's signals to this class's signal and uses WebActionMapper + to proxy the actions. + */ +class TabWidget : public QTabWidget +{ + Q_OBJECT + +signals: + // tab widget signals + void loadPage(const QString &url); + void tabsChanged(); + void lastTabClosed(); + + // current tab signals + void setCurrentTitle(const QString &url); + void showStatusBarMessage(const QString &message); + void linkHovered(const QString &link); + void loadProgress(int progress); + void geometryChangeRequested(const QRect &geometry); + void menuBarVisibilityChangeRequested(bool visible); + void statusBarVisibilityChangeRequested(bool visible); + void toolBarVisibilityChangeRequested(bool visible); + void printRequested(QWebFrame *frame); + +public: + TabWidget(QWidget *parent = 0); + void clear(); + void addWebAction(QAction *action, QWebPage::WebAction webAction); + + QAction *newTabAction() const; + QAction *closeTabAction() const; + QAction *recentlyClosedTabsAction() const; + QAction *nextTabAction() const; + QAction *previousTabAction() const; + + QWidget *lineEditStack() const; + QLineEdit *currentLineEdit() const; + WebView *currentWebView() const; + WebView *webView(int index) const; + QLineEdit *lineEdit(int index) const; + int webViewIndex(WebView *webView) const; + + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + +protected: + void mouseDoubleClickEvent(QMouseEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +public slots: + void loadUrlInCurrentTab(const QUrl &url); + WebView *newTab(bool makeCurrent = true); + void cloneTab(int index = -1); + void closeTab(int index = -1); + void closeOtherTabs(int index); + void reloadTab(int index = -1); + void reloadAllTabs(); + void nextTab(); + void previousTab(); + +private slots: + void currentChanged(int index); + void aboutToShowRecentTabsMenu(); + void aboutToShowRecentTriggeredAction(QAction *action); + void webViewLoadStarted(); + void webViewIconChanged(); + void webViewTitleChanged(const QString &title); + void webViewUrlChanged(const QUrl &url); + void lineEditReturnPressed(); + void windowCloseRequested(); + void moveTab(int fromIndex, int toIndex); + +private: + QAction *m_recentlyClosedTabsAction; + QAction *m_newTabAction; + QAction *m_closeTabAction; + QAction *m_nextTabAction; + QAction *m_previousTabAction; + + QMenu *m_recentlyClosedTabsMenu; + static const int m_recentlyClosedTabsSize = 10; + QList m_recentlyClosedTabs; + QList m_actions; + + QCompleter *m_lineEditCompleter; + QStackedWidget *m_lineEdits; + TabBar *m_tabBar; +}; + +#endif // TABWIDGET_H + diff --git a/examples/gestures/browser/toolbarsearch.cpp b/examples/gestures/browser/toolbarsearch.cpp new file mode 100644 index 0000000..f26459c --- /dev/null +++ b/examples/gestures/browser/toolbarsearch.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "toolbarsearch.h" +#include "autosaver.h" + +#include +#include + +#include +#include +#include + +#include + +/* + ToolbarSearch is a very basic search widget that also contains a small history. + Searches are turned into urls that use Google to perform search + */ +ToolbarSearch::ToolbarSearch(QWidget *parent) + : SearchLineEdit(parent) + , m_autosaver(new AutoSaver(this)) + , m_maxSavedSearches(10) + , m_stringListModel(new QStringListModel(this)) +{ + QMenu *m = menu(); + connect(m, SIGNAL(aboutToShow()), this, SLOT(aboutToShowMenu())); + connect(m, SIGNAL(triggered(QAction*)), this, SLOT(triggeredMenuAction(QAction*))); + + QCompleter *completer = new QCompleter(m_stringListModel, this); + completer->setCompletionMode(QCompleter::InlineCompletion); + lineEdit()->setCompleter(completer); + + connect(lineEdit(), SIGNAL(returnPressed()), SLOT(searchNow())); + setInactiveText(tr("Google")); + load(); +} + +ToolbarSearch::~ToolbarSearch() +{ + m_autosaver->saveIfNeccessary(); +} + +void ToolbarSearch::save() +{ + QSettings settings; + settings.beginGroup(QLatin1String("toolbarsearch")); + settings.setValue(QLatin1String("recentSearches"), m_stringListModel->stringList()); + settings.setValue(QLatin1String("maximumSaved"), m_maxSavedSearches); + settings.endGroup(); +} + +void ToolbarSearch::load() +{ + QSettings settings; + settings.beginGroup(QLatin1String("toolbarsearch")); + QStringList list = settings.value(QLatin1String("recentSearches")).toStringList(); + m_maxSavedSearches = settings.value(QLatin1String("maximumSaved"), m_maxSavedSearches).toInt(); + m_stringListModel->setStringList(list); + settings.endGroup(); +} + +void ToolbarSearch::searchNow() +{ + QString searchText = lineEdit()->text(); + QStringList newList = m_stringListModel->stringList(); + if (newList.contains(searchText)) + newList.removeAt(newList.indexOf(searchText)); + newList.prepend(searchText); + if (newList.size() >= m_maxSavedSearches) + newList.removeLast(); + + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (!globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) { + m_stringListModel->setStringList(newList); + m_autosaver->changeOccurred(); + } + + QUrl url(QLatin1String("http://www.google.com/search")); + url.addQueryItem(QLatin1String("q"), searchText); + url.addQueryItem(QLatin1String("ie"), QLatin1String("UTF-8")); + url.addQueryItem(QLatin1String("oe"), QLatin1String("UTF-8")); + url.addQueryItem(QLatin1String("client"), QLatin1String("qtdemobrowser")); + emit search(url); +} + +void ToolbarSearch::aboutToShowMenu() +{ + lineEdit()->selectAll(); + QMenu *m = menu(); + m->clear(); + QStringList list = m_stringListModel->stringList(); + if (list.isEmpty()) { + m->addAction(tr("No Recent Searches")); + return; + } + + QAction *recent = m->addAction(tr("Recent Searches")); + recent->setEnabled(false); + for (int i = 0; i < list.count(); ++i) { + QString text = list.at(i); + m->addAction(text)->setData(text); + } + m->addSeparator(); + m->addAction(tr("Clear Recent Searches"), this, SLOT(clear())); +} + +void ToolbarSearch::triggeredMenuAction(QAction *action) +{ + QVariant v = action->data(); + if (v.canConvert()) { + QString text = v.toString(); + lineEdit()->setText(text); + searchNow(); + } +} + +void ToolbarSearch::clear() +{ + m_stringListModel->setStringList(QStringList()); + m_autosaver->changeOccurred();; +} + diff --git a/examples/gestures/browser/toolbarsearch.h b/examples/gestures/browser/toolbarsearch.h new file mode 100644 index 0000000..1826d84 --- /dev/null +++ b/examples/gestures/browser/toolbarsearch.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOOLBARSEARCH_H +#define TOOLBARSEARCH_H + +#include "searchlineedit.h" + +QT_BEGIN_NAMESPACE +class QUrl; +class QAction; +class QStringListModel; +QT_END_NAMESPACE + +class AutoSaver; + +class ToolbarSearch : public SearchLineEdit +{ + Q_OBJECT + +signals: + void search(const QUrl &url); + +public: + ToolbarSearch(QWidget *parent = 0); + ~ToolbarSearch(); + +public slots: + void clear(); + void searchNow(); + +private slots: + void save(); + void aboutToShowMenu(); + void triggeredMenuAction(QAction *action); + +private: + void load(); + + AutoSaver *m_autosaver; + int m_maxSavedSearches; + QStringListModel *m_stringListModel; +}; + +#endif // TOOLBARSEARCH_H + diff --git a/examples/gestures/browser/urllineedit.cpp b/examples/gestures/browser/urllineedit.cpp new file mode 100644 index 0000000..019ca8b --- /dev/null +++ b/examples/gestures/browser/urllineedit.cpp @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "urllineedit.h" + +#include "browserapplication.h" +#include "searchlineedit.h" +#include "webview.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +ExLineEdit::ExLineEdit(QWidget *parent) + : QWidget(parent) + , m_leftWidget(0) + , m_lineEdit(new QLineEdit(this)) + , m_clearButton(0) +{ + setFocusPolicy(m_lineEdit->focusPolicy()); + setAttribute(Qt::WA_InputMethodEnabled); + setSizePolicy(m_lineEdit->sizePolicy()); + setBackgroundRole(m_lineEdit->backgroundRole()); + setMouseTracking(true); + setAcceptDrops(true); + setAttribute(Qt::WA_MacShowFocusRect, true); + QPalette p = m_lineEdit->palette(); + setPalette(p); + + // line edit + m_lineEdit->setFrame(false); + m_lineEdit->setFocusProxy(this); + m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); + QPalette clearPalette = m_lineEdit->palette(); + clearPalette.setBrush(QPalette::Base, QBrush(Qt::transparent)); + m_lineEdit->setPalette(clearPalette); + + // clearButton + m_clearButton = new ClearButton(this); + connect(m_clearButton, SIGNAL(clicked()), + m_lineEdit, SLOT(clear())); + connect(m_lineEdit, SIGNAL(textChanged(const QString&)), + m_clearButton, SLOT(textChanged(const QString&))); +} + +void ExLineEdit::setLeftWidget(QWidget *widget) +{ + m_leftWidget = widget; +} + +QWidget *ExLineEdit::leftWidget() const +{ + return m_leftWidget; +} + +void ExLineEdit::resizeEvent(QResizeEvent *event) +{ + Q_ASSERT(m_leftWidget); + updateGeometries(); + QWidget::resizeEvent(event); +} + +void ExLineEdit::updateGeometries() +{ + QStyleOptionFrameV2 panel; + initStyleOption(&panel); + QRect rect = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this); + + int height = rect.height(); + int width = rect.width(); + + int m_leftWidgetHeight = m_leftWidget->height(); + m_leftWidget->setGeometry(rect.x() + 2, rect.y() + (height - m_leftWidgetHeight)/2, + m_leftWidget->width(), m_leftWidget->height()); + + int clearButtonWidth = this->height(); + m_lineEdit->setGeometry(m_leftWidget->x() + m_leftWidget->width(), 0, + width - clearButtonWidth - m_leftWidget->width(), this->height()); + + m_clearButton->setGeometry(this->width() - clearButtonWidth, 0, + clearButtonWidth, this->height()); +} + +void ExLineEdit::initStyleOption(QStyleOptionFrameV2 *option) const +{ + option->initFrom(this); + option->rect = contentsRect(); + option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this); + option->midLineWidth = 0; + option->state |= QStyle::State_Sunken; + if (m_lineEdit->isReadOnly()) + option->state |= QStyle::State_ReadOnly; +#ifdef QT_KEYPAD_NAVIGATION + if (hasEditFocus()) + option->state |= QStyle::State_HasEditFocus; +#endif + option->features = QStyleOptionFrameV2::None; +} + +QSize ExLineEdit::sizeHint() const +{ + m_lineEdit->setFrame(true); + QSize size = m_lineEdit->sizeHint(); + m_lineEdit->setFrame(false); + return size; +} + +void ExLineEdit::focusInEvent(QFocusEvent *event) +{ + m_lineEdit->event(event); + QWidget::focusInEvent(event); +} + +void ExLineEdit::focusOutEvent(QFocusEvent *event) +{ + m_lineEdit->event(event); + + if (m_lineEdit->completer()) { + connect(m_lineEdit->completer(), SIGNAL(activated(QString)), + m_lineEdit, SLOT(setText(QString))); + connect(m_lineEdit->completer(), SIGNAL(highlighted(QString)), + m_lineEdit, SLOT(_q_completionHighlighted(QString))); + } + QWidget::focusOutEvent(event); +} + +void ExLineEdit::keyPressEvent(QKeyEvent *event) +{ + m_lineEdit->event(event); +} + +bool ExLineEdit::event(QEvent *event) +{ + if (event->type() == QEvent::ShortcutOverride) + return m_lineEdit->event(event); + return QWidget::event(event); +} + +void ExLineEdit::paintEvent(QPaintEvent *) +{ + QPainter p(this); + QStyleOptionFrameV2 panel; + initStyleOption(&panel); + style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this); +} + +QVariant ExLineEdit::inputMethodQuery(Qt::InputMethodQuery property) const +{ + return m_lineEdit->inputMethodQuery(property); +} + +void ExLineEdit::inputMethodEvent(QInputMethodEvent *e) +{ + m_lineEdit->event(e); +} + + +class UrlIconLabel : public QLabel +{ + +public: + UrlIconLabel(QWidget *parent); + + WebView *m_webView; + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +private: + QPoint m_dragStartPos; + +}; + +UrlIconLabel::UrlIconLabel(QWidget *parent) + : QLabel(parent) + , m_webView(0) +{ + setMinimumWidth(16); + setMinimumHeight(16); +} + +void UrlIconLabel::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + m_dragStartPos = event->pos(); + QLabel::mousePressEvent(event); +} + +void UrlIconLabel::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() == Qt::LeftButton + && (event->pos() - m_dragStartPos).manhattanLength() > QApplication::startDragDistance() + && m_webView) { + QDrag *drag = new QDrag(this); + QMimeData *mimeData = new QMimeData; + mimeData->setText(QString::fromUtf8(m_webView->url().toEncoded())); + QList urls; + urls.append(m_webView->url()); + mimeData->setUrls(urls); + drag->setMimeData(mimeData); + drag->exec(); + } +} + +UrlLineEdit::UrlLineEdit(QWidget *parent) + : ExLineEdit(parent) + , m_webView(0) + , m_iconLabel(0) +{ + // icon + m_iconLabel = new UrlIconLabel(this); + m_iconLabel->resize(16, 16); + setLeftWidget(m_iconLabel); + m_defaultBaseColor = palette().color(QPalette::Base); + + webViewIconChanged(); +} + +void UrlLineEdit::setWebView(WebView *webView) +{ + Q_ASSERT(!m_webView); + m_webView = webView; + m_iconLabel->m_webView = webView; + connect(webView, SIGNAL(urlChanged(const QUrl &)), + this, SLOT(webViewUrlChanged(const QUrl &))); + connect(webView, SIGNAL(loadFinished(bool)), + this, SLOT(webViewIconChanged())); + connect(webView, SIGNAL(iconChanged()), + this, SLOT(webViewIconChanged())); + connect(webView, SIGNAL(loadProgress(int)), + this, SLOT(update())); +} + +void UrlLineEdit::webViewUrlChanged(const QUrl &url) +{ + m_lineEdit->setText(QString::fromUtf8(url.toEncoded())); + m_lineEdit->setCursorPosition(0); +} + +void UrlLineEdit::webViewIconChanged() +{ + QUrl url = (m_webView) ? m_webView->url() : QUrl(); + QIcon icon = BrowserApplication::instance()->icon(url); + QPixmap pixmap(icon.pixmap(16, 16)); + m_iconLabel->setPixmap(pixmap); +} + +QLinearGradient UrlLineEdit::generateGradient(const QColor &color) const +{ + QLinearGradient gradient(0, 0, 0, height()); + gradient.setColorAt(0, m_defaultBaseColor); + gradient.setColorAt(0.15, color.lighter(120)); + gradient.setColorAt(0.5, color); + gradient.setColorAt(0.85, color.lighter(120)); + gradient.setColorAt(1, m_defaultBaseColor); + return gradient; +} + +void UrlLineEdit::focusOutEvent(QFocusEvent *event) +{ + if (m_lineEdit->text().isEmpty() && m_webView) + m_lineEdit->setText(QString::fromUtf8(m_webView->url().toEncoded())); + ExLineEdit::focusOutEvent(event); +} + +void UrlLineEdit::paintEvent(QPaintEvent *event) +{ + QPalette p = palette(); + if (m_webView && m_webView->url().scheme() == QLatin1String("https")) { + QColor lightYellow(248, 248, 210); + p.setBrush(QPalette::Base, generateGradient(lightYellow)); + } else { + p.setBrush(QPalette::Base, m_defaultBaseColor); + } + setPalette(p); + ExLineEdit::paintEvent(event); + + QPainter painter(this); + QStyleOptionFrameV2 panel; + initStyleOption(&panel); + QRect backgroundRect = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this); + if (m_webView && !hasFocus()) { + int progress = m_webView->progress(); + QColor loadingColor = QColor(116, 192, 250); + painter.setBrush(generateGradient(loadingColor)); + painter.setPen(Qt::transparent); + int mid = backgroundRect.width() / 100 * progress; + QRect progressRect(backgroundRect.x(), backgroundRect.y(), mid, backgroundRect.height()); + painter.drawRect(progressRect); + } +} diff --git a/examples/gestures/browser/urllineedit.h b/examples/gestures/browser/urllineedit.h new file mode 100644 index 0000000..ee05d3d --- /dev/null +++ b/examples/gestures/browser/urllineedit.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef URLLINEEDIT_H +#define URLLINEEDIT_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QLineEdit; +QT_END_NAMESPACE + +class ClearButton; +class ExLineEdit : public QWidget +{ + Q_OBJECT + +public: + ExLineEdit(QWidget *parent = 0); + + inline QLineEdit *lineEdit() const { return m_lineEdit; } + + void setLeftWidget(QWidget *widget); + QWidget *leftWidget() const; + + QSize sizeHint() const; + + QVariant inputMethodQuery(Qt::InputMethodQuery property) const; +protected: + void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); + void keyPressEvent(QKeyEvent *event); + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + void inputMethodEvent(QInputMethodEvent *e); + bool event(QEvent *event); + +protected: + void updateGeometries(); + void initStyleOption(QStyleOptionFrameV2 *option) const; + + QWidget *m_leftWidget; + QLineEdit *m_lineEdit; + ClearButton *m_clearButton; +}; + +class UrlIconLabel; +class WebView; +class UrlLineEdit : public ExLineEdit +{ + Q_OBJECT + +public: + UrlLineEdit(QWidget *parent = 0); + void setWebView(WebView *webView); + +protected: + void paintEvent(QPaintEvent *event); + void focusOutEvent(QFocusEvent *event); + +private slots: + void webViewUrlChanged(const QUrl &url); + void webViewIconChanged(); + +private: + QLinearGradient generateGradient(const QColor &color) const; + WebView *m_webView; + UrlIconLabel *m_iconLabel; + QColor m_defaultBaseColor; + +}; + + +#endif // URLLINEEDIT_H + diff --git a/examples/gestures/browser/webview.cpp b/examples/gestures/browser/webview.cpp new file mode 100644 index 0000000..38fb43d --- /dev/null +++ b/examples/gestures/browser/webview.cpp @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "browserapplication.h" +#include "browsermainwindow.h" +#include "cookiejar.h" +#include "downloadmanager.h" +#include "networkaccessmanager.h" +#include "tabwidget.h" +#include "webview.h" + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +WebPage::WebPage(QObject *parent) + : QWebPage(parent) + , m_keyboardModifiers(Qt::NoModifier) + , m_pressedButtons(Qt::NoButton) + , m_openInNewTab(false) +{ + setNetworkAccessManager(BrowserApplication::networkAccessManager()); + connect(this, SIGNAL(unsupportedContent(QNetworkReply *)), + this, SLOT(handleUnsupportedContent(QNetworkReply *))); +} + +BrowserMainWindow *WebPage::mainWindow() +{ + QObject *w = this->parent(); + while (w) { + if (BrowserMainWindow *mw = qobject_cast(w)) + return mw; + w = w->parent(); + } + return BrowserApplication::instance()->mainWindow(); +} + +bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type) +{ + // ctrl open in new tab + // ctrl-shift open in new tab and select + // ctrl-alt open in new window + if (type == QWebPage::NavigationTypeLinkClicked + && (m_keyboardModifiers & Qt::ControlModifier + || m_pressedButtons == Qt::MidButton)) { + bool newWindow = (m_keyboardModifiers & Qt::AltModifier); + WebView *webView; + if (newWindow) { + BrowserApplication::instance()->newMainWindow(); + BrowserMainWindow *newMainWindow = BrowserApplication::instance()->mainWindow(); + webView = newMainWindow->currentTab(); + newMainWindow->raise(); + newMainWindow->activateWindow(); + webView->setFocus(); + } else { + bool selectNewTab = (m_keyboardModifiers & Qt::ShiftModifier); + webView = mainWindow()->tabWidget()->newTab(selectNewTab); + } + webView->load(request); + m_keyboardModifiers = Qt::NoModifier; + m_pressedButtons = Qt::NoButton; + return false; + } + if (frame == mainFrame()) { + m_loadingUrl = request.url(); + emit loadingUrl(m_loadingUrl); + } + return QWebPage::acceptNavigationRequest(frame, request, type); +} + +QWebPage *WebPage::createWindow(QWebPage::WebWindowType type) +{ + Q_UNUSED(type); + if (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton) + m_openInNewTab = true; + if (m_openInNewTab) { + m_openInNewTab = false; + return mainWindow()->tabWidget()->newTab()->page(); + } + BrowserApplication::instance()->newMainWindow(); + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + return mainWindow->currentTab()->page(); +} + +#if !defined(QT_NO_UITOOLS) +QObject *WebPage::createPlugin(const QString &classId, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues) +{ + Q_UNUSED(url); + Q_UNUSED(paramNames); + Q_UNUSED(paramValues); + QUiLoader loader; + return loader.createWidget(classId, view()); +} +#endif // !defined(QT_NO_UITOOLS) + +void WebPage::handleUnsupportedContent(QNetworkReply *reply) +{ + if (reply->error() == QNetworkReply::NoError) { + BrowserApplication::downloadManager()->handleUnsupportedContent(reply); + return; + } + + QFile file(QLatin1String(":/notfound.html")); + bool isOpened = file.open(QIODevice::ReadOnly); + Q_ASSERT(isOpened); + QString title = tr("Error loading page: %1").arg(reply->url().toString()); + QString html = QString(QLatin1String(file.readAll())) + .arg(title) + .arg(reply->errorString()) + .arg(reply->url().toString()); + + QBuffer imageBuffer; + imageBuffer.open(QBuffer::ReadWrite); + QIcon icon = view()->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, view()); + QPixmap pixmap = icon.pixmap(QSize(32,32)); + if (pixmap.save(&imageBuffer, "PNG")) { + html.replace(QLatin1String("IMAGE_BINARY_DATA_HERE"), + QString(QLatin1String(imageBuffer.buffer().toBase64()))); + } + + QList frames; + frames.append(mainFrame()); + while (!frames.isEmpty()) { + QWebFrame *frame = frames.takeFirst(); + if (frame->url() == reply->url()) { + frame->setHtml(html, reply->url()); + return; + } + QList children = frame->childFrames(); + foreach(QWebFrame *frame, children) + frames.append(frame); + } + if (m_loadingUrl == reply->url()) { + mainFrame()->setHtml(html, reply->url()); + } +} + + +WebView::WebView(QWidget* parent) + : QWebView(parent) + , m_progress(0) + , m_page(new WebPage(this)) +{ + grabGesture(Qt::PanGesture); + setPage(m_page); + connect(page(), SIGNAL(statusBarMessage(const QString&)), + SLOT(setStatusBarText(const QString&))); + connect(this, SIGNAL(loadProgress(int)), + this, SLOT(setProgress(int))); + connect(this, SIGNAL(loadFinished(bool)), + this, SLOT(loadFinished())); + connect(page(), SIGNAL(loadingUrl(const QUrl&)), + this, SIGNAL(urlChanged(const QUrl &))); + connect(page(), SIGNAL(downloadRequested(const QNetworkRequest &)), + this, SLOT(downloadRequested(const QNetworkRequest &))); + page()->setForwardUnsupportedContent(true); + +} + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ + QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos()); + if (!r.linkUrl().isEmpty()) { + QMenu menu(this); + menu.addAction(pageAction(QWebPage::OpenLinkInNewWindow)); + menu.addAction(tr("Open in New Tab"), this, SLOT(openLinkInNewTab())); + menu.addSeparator(); + menu.addAction(pageAction(QWebPage::DownloadLinkToDisk)); + // Add link to bookmarks... + menu.addSeparator(); + menu.addAction(pageAction(QWebPage::CopyLinkToClipboard)); + if (page()->settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled)) + menu.addAction(pageAction(QWebPage::InspectElement)); + menu.exec(mapToGlobal(event->pos())); + return; + } + QWebView::contextMenuEvent(event); +} + +void WebView::wheelEvent(QWheelEvent *event) +{ + if (QApplication::keyboardModifiers() & Qt::ControlModifier) { + int numDegrees = event->delta() / 8; + int numSteps = numDegrees / 15; + setTextSizeMultiplier(textSizeMultiplier() + numSteps * 0.1); + event->accept(); + return; + } + QWebView::wheelEvent(event); +} + +void WebView::openLinkInNewTab() +{ + m_page->m_openInNewTab = true; + pageAction(QWebPage::OpenLinkInNewWindow)->trigger(); +} + +void WebView::setProgress(int progress) +{ + m_progress = progress; +} + +void WebView::loadFinished() +{ + if (100 != m_progress) { + qWarning() << "Recieved finished signal while progress is still:" << progress() + << "Url:" << url(); + } + m_progress = 0; +} + +void WebView::loadUrl(const QUrl &url) +{ + m_initialUrl = url; + load(url); +} + +QString WebView::lastStatusBarText() const +{ + return m_statusBarText; +} + +QUrl WebView::url() const +{ + QUrl url = QWebView::url(); + if (!url.isEmpty()) + return url; + + return m_initialUrl; +} + +void WebView::mousePressEvent(QMouseEvent *event) +{ + m_page->m_pressedButtons = event->buttons(); + m_page->m_keyboardModifiers = event->modifiers(); + QWebView::mousePressEvent(event); +} + +void WebView::mouseReleaseEvent(QMouseEvent *event) +{ + QWebView::mouseReleaseEvent(event); + if (!event->isAccepted() && (m_page->m_pressedButtons & Qt::MidButton)) { + QUrl url(QApplication::clipboard()->text(QClipboard::Selection)); + if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) { + setUrl(url); + } + } +} + +void WebView::setStatusBarText(const QString &string) +{ + m_statusBarText = string; +} + +void WebView::downloadRequested(const QNetworkRequest &request) +{ + BrowserApplication::downloadManager()->download(request); +} + +bool WebView::event(QEvent *event) +{ + if (event->type() == QEvent::Gesture) + { + gestureEvent(static_cast(event)); + return true; + } + return QWebView::event(event); +} + +void WebView::gestureEvent(QGestureEvent *event) +{ + if (const QGesture *g = event->gesture(Qt::PanGesture)) { + if (QWebFrame *frame = page()->mainFrame()) { + QPoint offset = g->pos() - g->lastPos(); + frame->setScrollPosition(frame->scrollPosition() - offset); + } + event->accept(); + } +} diff --git a/examples/gestures/browser/webview.h b/examples/gestures/browser/webview.h new file mode 100644 index 0000000..037d117 --- /dev/null +++ b/examples/gestures/browser/webview.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WEBVIEW_H +#define WEBVIEW_H + +#include + +QT_BEGIN_NAMESPACE +class QAuthenticator; +class QMouseEvent; +class QNetworkProxy; +class QNetworkReply; +class QSslError; +QT_END_NAMESPACE + +class BrowserMainWindow; +class WebPage : public QWebPage { + Q_OBJECT + +signals: + void loadingUrl(const QUrl &url); + +public: + WebPage(QObject *parent = 0); + BrowserMainWindow *mainWindow(); + +protected: + bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type); + QWebPage *createWindow(QWebPage::WebWindowType type); +#if !defined(QT_NO_UITOOLS) + QObject *createPlugin(const QString &classId, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues); +#endif + +private slots: + void handleUnsupportedContent(QNetworkReply *reply); + +private: + friend class WebView; + + // set the webview mousepressedevent + Qt::KeyboardModifiers m_keyboardModifiers; + Qt::MouseButtons m_pressedButtons; + bool m_openInNewTab; + QUrl m_loadingUrl; +}; + +class WebView : public QWebView { + Q_OBJECT + +public: + WebView(QWidget *parent = 0); + WebPage *webPage() const { return m_page; } + + void loadUrl(const QUrl &url); + QUrl url() const; + + QString lastStatusBarText() const; + inline int progress() const { return m_progress; } + +protected: + bool event(QEvent *event); + void gestureEvent(QGestureEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + void wheelEvent(QWheelEvent *event); + +private slots: + void setProgress(int progress); + void loadFinished(); + void setStatusBarText(const QString &string); + void downloadRequested(const QNetworkRequest &request); + void openLinkInNewTab(); + +private: + QString m_statusBarText; + QUrl m_initialUrl; + int m_progress; + WebPage *m_page; +}; + +#endif diff --git a/examples/gestures/browser/xbel.cpp b/examples/gestures/browser/xbel.cpp new file mode 100644 index 0000000..c1a528d --- /dev/null +++ b/examples/gestures/browser/xbel.cpp @@ -0,0 +1,320 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xbel.h" + +#include + +BookmarkNode::BookmarkNode(BookmarkNode::Type type, BookmarkNode *parent) : + expanded(false) + , m_parent(parent) + , m_type(type) +{ + if (parent) + parent->add(this); +} + +BookmarkNode::~BookmarkNode() +{ + if (m_parent) + m_parent->remove(this); + qDeleteAll(m_children); + m_parent = 0; + m_type = BookmarkNode::Root; +} + +bool BookmarkNode::operator==(const BookmarkNode &other) +{ + if (url != other.url + || title != other.title + || desc != other.desc + || expanded != other.expanded + || m_type != other.m_type + || m_children.count() != other.m_children.count()) + return false; + + for (int i = 0; i < m_children.count(); ++i) + if (!((*(m_children[i])) == (*(other.m_children[i])))) + return false; + return true; +} + +BookmarkNode::Type BookmarkNode::type() const +{ + return m_type; +} + +void BookmarkNode::setType(Type type) +{ + m_type = type; +} + +QList BookmarkNode::children() const +{ + return m_children; +} + +BookmarkNode *BookmarkNode::parent() const +{ + return m_parent; +} + +void BookmarkNode::add(BookmarkNode *child, int offset) +{ + Q_ASSERT(child->m_type != Root); + if (child->m_parent) + child->m_parent->remove(child); + child->m_parent = this; + if (-1 == offset) + offset = m_children.size(); + m_children.insert(offset, child); +} + +void BookmarkNode::remove(BookmarkNode *child) +{ + child->m_parent = 0; + m_children.removeAll(child); +} + + +XbelReader::XbelReader() +{ +} + +BookmarkNode *XbelReader::read(const QString &fileName) +{ + QFile file(fileName); + if (!file.exists()) { + return new BookmarkNode(BookmarkNode::Root); + } + file.open(QFile::ReadOnly); + return read(&file); +} + +BookmarkNode *XbelReader::read(QIODevice *device) +{ + BookmarkNode *root = new BookmarkNode(BookmarkNode::Root); + setDevice(device); + while (!atEnd()) { + readNext(); + if (isStartElement()) { + QString version = attributes().value(QLatin1String("version")).toString(); + if (name() == QLatin1String("xbel") + && (version.isEmpty() || version == QLatin1String("1.0"))) { + readXBEL(root); + } else { + raiseError(QObject::tr("The file is not an XBEL version 1.0 file.")); + } + } + } + return root; +} + +void XbelReader::readXBEL(BookmarkNode *parent) +{ + Q_ASSERT(isStartElement() && name() == QLatin1String("xbel")); + + while (!atEnd()) { + readNext(); + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == QLatin1String("folder")) + readFolder(parent); + else if (name() == QLatin1String("bookmark")) + readBookmarkNode(parent); + else if (name() == QLatin1String("separator")) + readSeparator(parent); + else + skipUnknownElement(); + } + } +} + +void XbelReader::readFolder(BookmarkNode *parent) +{ + Q_ASSERT(isStartElement() && name() == QLatin1String("folder")); + + BookmarkNode *folder = new BookmarkNode(BookmarkNode::Folder, parent); + folder->expanded = (attributes().value(QLatin1String("folded")) == QLatin1String("no")); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == QLatin1String("title")) + readTitle(folder); + else if (name() == QLatin1String("desc")) + readDescription(folder); + else if (name() == QLatin1String("folder")) + readFolder(folder); + else if (name() == QLatin1String("bookmark")) + readBookmarkNode(folder); + else if (name() == QLatin1String("separator")) + readSeparator(folder); + else + skipUnknownElement(); + } + } +} + +void XbelReader::readTitle(BookmarkNode *parent) +{ + Q_ASSERT(isStartElement() && name() == QLatin1String("title")); + parent->title = readElementText(); +} + +void XbelReader::readDescription(BookmarkNode *parent) +{ + Q_ASSERT(isStartElement() && name() == QLatin1String("desc")); + parent->desc = readElementText(); +} + +void XbelReader::readSeparator(BookmarkNode *parent) +{ + new BookmarkNode(BookmarkNode::Separator, parent); + // empty elements have a start and end element + readNext(); +} + +void XbelReader::readBookmarkNode(BookmarkNode *parent) +{ + Q_ASSERT(isStartElement() && name() == QLatin1String("bookmark")); + BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark, parent); + bookmark->url = attributes().value(QLatin1String("href")).toString(); + while (!atEnd()) { + readNext(); + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == QLatin1String("title")) + readTitle(bookmark); + else if (name() == QLatin1String("desc")) + readDescription(bookmark); + else + skipUnknownElement(); + } + } + if (bookmark->title.isEmpty()) + bookmark->title = QObject::tr("Unknown title"); +} + +void XbelReader::skipUnknownElement() +{ + Q_ASSERT(isStartElement()); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + skipUnknownElement(); + } +} + + +XbelWriter::XbelWriter() +{ + setAutoFormatting(true); +} + +bool XbelWriter::write(const QString &fileName, const BookmarkNode *root) +{ + QFile file(fileName); + if (!root || !file.open(QFile::WriteOnly)) + return false; + return write(&file, root); +} + +bool XbelWriter::write(QIODevice *device, const BookmarkNode *root) +{ + setDevice(device); + + writeStartDocument(); + writeDTD(QLatin1String("")); + writeStartElement(QLatin1String("xbel")); + writeAttribute(QLatin1String("version"), QLatin1String("1.0")); + if (root->type() == BookmarkNode::Root) { + for (int i = 0; i < root->children().count(); ++i) + writeItem(root->children().at(i)); + } else { + writeItem(root); + } + + writeEndDocument(); + return true; +} + +void XbelWriter::writeItem(const BookmarkNode *parent) +{ + switch (parent->type()) { + case BookmarkNode::Folder: + writeStartElement(QLatin1String("folder")); + writeAttribute(QLatin1String("folded"), parent->expanded ? QLatin1String("no") : QLatin1String("yes")); + writeTextElement(QLatin1String("title"), parent->title); + for (int i = 0; i < parent->children().count(); ++i) + writeItem(parent->children().at(i)); + writeEndElement(); + break; + case BookmarkNode::Bookmark: + writeStartElement(QLatin1String("bookmark")); + if (!parent->url.isEmpty()) + writeAttribute(QLatin1String("href"), parent->url); + writeTextElement(QLatin1String("title"), parent->title); + if (!parent->desc.isEmpty()) + writeAttribute(QLatin1String("desc"), parent->desc); + writeEndElement(); + break; + case BookmarkNode::Separator: + writeEmptyElement(QLatin1String("separator")); + break; + default: + break; + } +} + diff --git a/examples/gestures/browser/xbel.h b/examples/gestures/browser/xbel.h new file mode 100644 index 0000000..7b8b017 --- /dev/null +++ b/examples/gestures/browser/xbel.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XBEL_H +#define XBEL_H + +#include +#include + +class BookmarkNode +{ +public: + enum Type { + Root, + Folder, + Bookmark, + Separator + }; + + BookmarkNode(Type type = Root, BookmarkNode *parent = 0); + ~BookmarkNode(); + bool operator==(const BookmarkNode &other); + + Type type() const; + void setType(Type type); + QList children() const; + BookmarkNode *parent() const; + + void add(BookmarkNode *child, int offset = -1); + void remove(BookmarkNode *child); + + QString url; + QString title; + QString desc; + bool expanded; + +private: + BookmarkNode *m_parent; + Type m_type; + QList m_children; + +}; + +class XbelReader : public QXmlStreamReader +{ +public: + XbelReader(); + BookmarkNode *read(const QString &fileName); + BookmarkNode *read(QIODevice *device); + +private: + void skipUnknownElement(); + void readXBEL(BookmarkNode *parent); + void readTitle(BookmarkNode *parent); + void readDescription(BookmarkNode *parent); + void readSeparator(BookmarkNode *parent); + void readFolder(BookmarkNode *parent); + void readBookmarkNode(BookmarkNode *parent); +}; + +#include + +class XbelWriter : public QXmlStreamWriter +{ +public: + XbelWriter(); + bool write(const QString &fileName, const BookmarkNode *root); + bool write(QIODevice *device, const BookmarkNode *root); + +private: + void writeItem(const BookmarkNode *parent); +}; + +#endif // XBEL_H + -- cgit v0.12 From e8f57e06eb21f51a6283f7a18d66aa8ca2c6b38a Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 23 Apr 2009 16:36:16 +0200 Subject: Added inertial to the browser demo. --- examples/gestures/browser/webview.cpp | 30 +++++++++++++++++++++++++++--- examples/gestures/browser/webview.h | 2 ++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/examples/gestures/browser/webview.cpp b/examples/gestures/browser/webview.cpp index 38fb43d..95a96e7 100644 --- a/examples/gestures/browser/webview.cpp +++ b/examples/gestures/browser/webview.cpp @@ -316,10 +316,34 @@ bool WebView::event(QEvent *event) void WebView::gestureEvent(QGestureEvent *event) { if (const QGesture *g = event->gesture(Qt::PanGesture)) { - if (QWebFrame *frame = page()->mainFrame()) { - QPoint offset = g->pos() - g->lastPos(); - frame->setScrollPosition(frame->scrollPosition() - offset); + if (g->state() == Qt::GestureUpdated) { + if (QWebFrame *frame = page()->mainFrame()) { + QPoint offset = g->pos() - g->lastPos(); + frame->setScrollPosition(frame->scrollPosition() - offset); + } + speed = g->pos() - g->lastPos(); + } else if (g->state() == Qt::GestureStarted) { + startTimer(20); + } else { } event->accept(); } } + +static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64) +{ + int x = qBound(-max, speed.x(), max); + int y = qBound(-max, speed.y(), max); + x = (x == 0) ? x : (x > 0) ? qMax(0, x - a) : qMin(0, x + a); + y = (y == 0) ? y : (y > 0) ? qMax(0, y - a) : qMin(0, y + a); + return QPoint(x, y); +} + +void WebView::timerEvent(QTimerEvent *event) +{ + speed = deaccelerate(speed); + if (speed.isNull()) + killTimer(event->timerId()); + if (QWebFrame *frame = page()->mainFrame()) + frame->setScrollPosition(frame->scrollPosition() - speed); +} diff --git a/examples/gestures/browser/webview.h b/examples/gestures/browser/webview.h index 037d117..15fb40c 100644 --- a/examples/gestures/browser/webview.h +++ b/examples/gestures/browser/webview.h @@ -99,6 +99,7 @@ public: protected: bool event(QEvent *event); void gestureEvent(QGestureEvent *event); + void timerEvent(QTimerEvent *event); void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void contextMenuEvent(QContextMenuEvent *event); @@ -116,6 +117,7 @@ private: QUrl m_initialUrl; int m_progress; WebPage *m_page; + QPoint speed; }; #endif -- cgit v0.12 From c0dc85f18230c338187b3a5f0eb1f17d1f570d05 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 24 Apr 2009 11:34:44 +0200 Subject: Added support to pan frames inside browser window. --- examples/gestures/browser/webview.cpp | 22 ++++++++++++++-------- examples/gestures/browser/webview.h | 3 ++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/examples/gestures/browser/webview.cpp b/examples/gestures/browser/webview.cpp index 95a96e7..310c7d8 100644 --- a/examples/gestures/browser/webview.cpp +++ b/examples/gestures/browser/webview.cpp @@ -186,6 +186,7 @@ WebView::WebView(QWidget* parent) : QWebView(parent) , m_progress(0) , m_page(new WebPage(this)) + , m_currentPanFrame(0) { grabGesture(Qt::PanGesture); setPage(m_page); @@ -317,13 +318,18 @@ void WebView::gestureEvent(QGestureEvent *event) { if (const QGesture *g = event->gesture(Qt::PanGesture)) { if (g->state() == Qt::GestureUpdated) { - if (QWebFrame *frame = page()->mainFrame()) { - QPoint offset = g->pos() - g->lastPos(); - frame->setScrollPosition(frame->scrollPosition() - offset); + if (m_currentPanFrame) { + m_panSpeed = g->pos() - g->lastPos(); + m_currentPanFrame->scroll(-m_panSpeed.x(), -m_panSpeed.y()); } - speed = g->pos() - g->lastPos(); } else if (g->state() == Qt::GestureStarted) { startTimer(20); + m_currentPanFrame = 0; + if (QWebFrame *frame = page()->mainFrame()) { + QWebHitTestResult result = frame->hitTestContent(g->startPos()); + if (!result.isNull()) + m_currentPanFrame = result.frame(); + } } else { } event->accept(); @@ -341,9 +347,9 @@ static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64) void WebView::timerEvent(QTimerEvent *event) { - speed = deaccelerate(speed); - if (speed.isNull()) + m_panSpeed = deaccelerate(m_panSpeed); + if (m_panSpeed.isNull()) killTimer(event->timerId()); - if (QWebFrame *frame = page()->mainFrame()) - frame->setScrollPosition(frame->scrollPosition() - speed); + if (m_currentPanFrame) + m_currentPanFrame->scroll(-m_panSpeed.x(), -m_panSpeed.y()); } diff --git a/examples/gestures/browser/webview.h b/examples/gestures/browser/webview.h index 15fb40c..af137b8 100644 --- a/examples/gestures/browser/webview.h +++ b/examples/gestures/browser/webview.h @@ -117,7 +117,8 @@ private: QUrl m_initialUrl; int m_progress; WebPage *m_page; - QPoint speed; + QPoint m_panSpeed; + QWebFrame *m_currentPanFrame; }; #endif -- cgit v0.12 From cee4fb7bd327f4b1aaaa7e541328b4fe19b12d1a Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 27 Apr 2009 11:36:15 +0200 Subject: Improved finding a scrollable QWebFrame in browser demo. --- examples/gestures/browser/webview.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/gestures/browser/webview.cpp b/examples/gestures/browser/webview.cpp index 310c7d8..2eee119 100644 --- a/examples/gestures/browser/webview.cpp +++ b/examples/gestures/browser/webview.cpp @@ -329,6 +329,13 @@ void WebView::gestureEvent(QGestureEvent *event) QWebHitTestResult result = frame->hitTestContent(g->startPos()); if (!result.isNull()) m_currentPanFrame = result.frame(); + while (m_currentPanFrame && + m_currentPanFrame->scrollBarMinimum(Qt::Vertical) == 0 && + m_currentPanFrame->scrollBarMaximum(Qt::Vertical) == 0 && + m_currentPanFrame->scrollBarMinimum(Qt::Horizontal) == 0 && + m_currentPanFrame->scrollBarMaximum(Qt::Horizontal) == 0) { + m_currentPanFrame = m_currentPanFrame->parentFrame(); + } } } else { } -- cgit v0.12 From ca1add3676fe51f285d16cd0c3bb98ed90e1be65 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 27 Apr 2009 12:04:18 +0200 Subject: Forgot to initialize a variable. --- src/gui/kernel/qgesturemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index b4939c4..3bdd323 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -68,7 +68,7 @@ static const unsigned int MaximumGestureRecognitionTimeout = 2000; QGestureManager::QGestureManager(QObject *parent) : QObject(parent), targetWidget(0), eventDeliveryDelayTimeout(300), delayedPressTimer(0), lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), - state(NotGesture) + lastGestureId(0), state(NotGesture) { qRegisterMetaType(); qRegisterMetaType(); -- cgit v0.12 From 66faa6f11f78b5fa4f3da52e74d6ef501bb19ca5 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 27 Apr 2009 12:05:25 +0200 Subject: Replaced inefficient foreach with a for loop. --- src/gui/kernel/qgesturemanager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 3bdd323..994b2b6 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -141,11 +141,15 @@ bool QGestureManager::filterEvent(QEvent *event) } activeGestures -= newMaybeGestures; activeGestures += startedGestures; - foreach(QGestureRecognizer *r, startedGestures+finishedGestures+notGestures) { - QMap::iterator it = maybeGestures.find(r); - if (it != maybeGestures.end()) { + for(QMap::iterator it = maybeGestures.begin(); + it != maybeGestures.end();) { + QGestureRecognizer *r = it.key(); + if (startedGestures.contains(r) || finishedGestures.contains(r) || + notGestures.contains(r)) { killTimer(it.value()); - maybeGestures.erase(it); + it = maybeGestures.erase(it); + } else { + ++it; } } foreach(QGestureRecognizer *r, newMaybeGestures) { -- cgit v0.12 From b3dbea5da34edb28a007ea7796de30204021a902 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 27 Apr 2009 13:15:59 +0200 Subject: Fixed gesture event propagation to QGraphicsItems. --- src/gui/graphicsview/qgraphicsscene.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 337b6ee..7bac21c 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -247,6 +247,7 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; #ifdef Q_WS_X11 #include #endif +#include QT_BEGIN_NAMESPACE @@ -3962,23 +3963,27 @@ bool QGraphicsScene::event(QEvent *event) // find graphics items that intersects with gestures hot spots. QPolygonF poly; - QMap sceneHotSpots; + QMap sceneHotSpots; foreach(const QString &type, gestureTypes) { QPointF pt = ev->mapToScene(ev->gesture(type)->hotSpot()); - sceneHotSpots.insert(qHash(type), pt); + sceneHotSpots.insert(type, pt); poly << pt; } QList itemsInGestureArea = items(poly, Qt::IntersectsItemBoundingRect); foreach(QGraphicsItem *item, itemsInGestureArea) { - QMap::const_iterator it = sceneHotSpots.begin(), - e = sceneHotSpots.end(); + QMap::const_iterator it = sceneHotSpots.begin(), + e = sceneHotSpots.end(); for(; it != e; ++it) { - if (item->contains(item->mapFromScene(it.value())) && - item->d_ptr->gestures.contains(it.key())) { - d->sendEvent(item, ev); - if (ev->isAccepted()) - break; + if (item->contains(item->mapFromScene(it.value()))) { + const QString gestureName = it.key(); + foreach(int gestureId, item->d_ptr->gestures) { + if (QGestureManager::instance()->gestureNameFromId(gestureId) == gestureName) { + d->sendEvent(item, ev); + if (ev->isAccepted()) + break; + } + } } } } -- cgit v0.12 From ba7485012033b0447a9e77d5612db734f9dcffd6 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 27 Apr 2009 14:06:23 +0200 Subject: Fixed gesture event propagation and offset translation. --- src/gui/kernel/qapplication.cpp | 39 ++++++++++++++++++++------------------ src/gui/kernel/qgesture.h | 1 + src/gui/kernel/qgesturemanager.cpp | 23 +--------------------- 3 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 39aeaa6..ebd62e6 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4032,27 +4032,30 @@ bool QApplication::notify(QObject *receiver, QEvent *e) case QEvent::Gesture: { QWidget *w = static_cast(receiver); - QGestureEvent *g = static_cast(e); - bool eventAccepted = g->isAccepted(); - // Q_ASSERT(g->gesture() != 0); - // const QGesture &gesture = *g->gesture(); - QPoint relPos(0,0); + QGestureEvent *ge = static_cast(e); + QSet eventGestures; + foreach(const QString &gesture, ge->gestureTypes()) + eventGestures << gesture; + bool eventAccepted = ge->isAccepted(); + + QPoint offset; while (w) { - // QGesture qge(gesture.gestureType(), gesture.startPos()+relPos, gesture.lastPos()+relPos, - // gesture.currentPos()+relPos, gesture.direction(), gesture.rect().translated(relPos), - // gesture.hotSpot()+relPos, gesture.state(), gesture.startTime()); - // QGestureEvent ge(&qge, false); - // ### TODO: fix widget-relative positions in gesture event. - QGestureEvent ge = *g; - ge.spont = g->spontaneous(); - res = d->notify_helper(w, w == receiver ? g : &ge); - g->spont = false; - eventAccepted = (w == receiver ? g : &ge)->isAccepted(); - if (res && eventAccepted) - break; + QSet widgetGestures = w->d_func()->gestures; + foreach(int gestureId, widgetGestures) { + if (eventGestures.contains(QGestureManager::instance()->gestureNameFromId(gestureId))) { + foreach(QGesture *gesture, ge->gestures()) + gesture->translate(offset); + offset = QPoint(); + res = d->notify_helper(w, ge); + ge->spont = false; + eventAccepted = ge->isAccepted(); + if (res && eventAccepted) + break; + } + } if (w->isWindow()) break; - relPos += w->pos(); + offset += w->pos(); w = w->parentWidget(); } break; diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 2a92588..da1bc90 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -101,6 +101,7 @@ protected: private: friend class QGestureManager; + friend class QApplication; friend class QGestureRecognizerPan; friend class QDoubleTapGestureRecognizer; friend class QTapAndHoldGestureRecognizer; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 994b2b6..b5b8fb8 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -482,28 +482,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) bool QGestureManager::sendGestureEvent(QWidget *receiver, QGestureEvent *event) { - QSet eventGestures; - foreach(const QString &gesture, event->gestureTypes()) - eventGestures << gesture; - - QPoint offset; - bool found = false; - while (receiver) { - QSet widgetGestures = receiver->d_func()->gestures; - foreach(int gestureId, widgetGestures) { - if (eventGestures.contains(gestureNameFromId(gestureId))) { - found = true; - break; - } - } - if (found) - break; - offset += receiver->pos(); - receiver = receiver->parentWidget(); - } - foreach(QGesture *gesture, event->gestures()) - gesture->translate(offset); - return receiver ? qt_sendSpontaneousEvent(receiver, event) : false; + return qt_sendSpontaneousEvent(receiver, event); } int QGestureManager::eventDeliveryDelay() const -- cgit v0.12 From a321909ced9fe28ea476ab95c7c7592db5090acc Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 29 Apr 2009 14:15:17 +0200 Subject: Generalized gesture filtering code to allow handling not only mouse events in gesture recognizers. --- src/gui/kernel/qapplication.cpp | 35 +++++++++----------------- src/gui/kernel/qgesturemanager.cpp | 15 ++++++++--- src/gui/kernel/qgesturemanager_p.h | 2 +- src/gui/kernel/qgesturerecognizer.cpp | 11 +++++--- src/gui/kernel/qgesturerecognizer.h | 3 ++- src/gui/kernel/qgesturestandardrecognizers.cpp | 6 +++-- 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index ebd62e6..9772e0f 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -3588,6 +3588,18 @@ bool QApplication::notify(QObject *receiver, QEvent *e) #endif // !QT_NO_WHEELEVENT || !QT_NO_TABLETEVENT } + if (!d->grabbedGestures.isEmpty() && e->spontaneous() && receiver->isWidgetType()) { + const QEvent::Type t = e->type(); + if (t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease || t == QEvent::MouseMove + || t == QEvent::MouseButtonDblClick || t == QEvent::Wheel + || t == QEvent::KeyPress || t == QEvent::KeyRelease + || t == QEvent::TabletPress || t == QEvent::TabletRelease || t == QEvent::TabletMove + || t >= QEvent::User) { + if (QGestureManager::instance()->filterEvent(static_cast(receiver), e)) + return true; + } + } + // User input and window activation makes tooltips sleep switch (e->type()) { case QEvent::Wheel: @@ -3715,28 +3727,6 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QPoint relpos = mouse->pos(); if (e->spontaneous()) { - if (!d->grabbedGestures.isEmpty()) { - QWidget *w = static_cast(receiver); - // if we are in gesture mode, we send all mouse events - // directly to gesture recognizer. - (void)QGestureManager::instance(); - if (d->gestureManager->inGestureMode()) { - if (d->gestureManager->filterEvent(e)) - return true; - } else { - if (w && (mouse->type() != QEvent::MouseMove || mouse->buttons() != 0)) { - // find the gesture target widget - QWidget *target = w; - while (target && target->d_func()->gestures.isEmpty()) - target = target->parentWidget(); - if (target) { - d->gestureManager->setGestureTargetWidget(w); - if (d->gestureManager->filterEvent(e)) - return true; - } - } - } - } if (e->type() == QEvent::MouseButtonPress) { QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, Qt::ClickFocus, @@ -5107,7 +5097,6 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) */ void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) { - Q_D(QApplication); QGestureManager::instance()->addRecognizer(recognizer); } diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index b5b8fb8..1635ac4 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -93,8 +93,17 @@ void QGestureManager::removeRecognizer(QGestureRecognizer *recognizer) recognizers.remove(recognizer); } -bool QGestureManager::filterEvent(QEvent *event) +bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) { + if (state != Gesture) { + // find the target widget + while (receiver && receiver->d_func()->gestures.isEmpty()) + receiver = receiver->parentWidget(); + if (!receiver) // no widget in the tree that accepts gestures. + return false; + targetWidget = receiver; + } + QPoint currentPos; switch (event->type()) { case QEvent::MouseButtonPress: @@ -133,7 +142,7 @@ bool QGestureManager::filterEvent(QEvent *event) } else if (result == QGestureRecognizer::MaybeGesture) { DEBUG() << "QGestureManager: maybe gesture: " << r; newMaybeGestures << r; - } else { + } else if (result == QGestureRecognizer::NotGesture) { // if it was maybe gesture, but isn't a gesture anymore. DEBUG() << "QGestureManager: not gesture: " << r; notGestures << r; @@ -230,7 +239,7 @@ bool QGestureManager::filterEvent(QEvent *event) } else if (result == QGestureRecognizer::MaybeGesture) { DEBUG() << "QGestureManager: maybe gesture: " << r; newMaybeGestures << r; - } else { + } else if (result == QGestureRecognizer::NotGesture) { // if it was an active gesture, but isn't a gesture anymore. if (activeGestures.contains(r)) { DEBUG() << "QGestureManager: cancelled gesture: " << r; diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 9d6d500..6de3baa 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -77,7 +77,7 @@ public: void addRecognizer(QGestureRecognizer *recognizer); void removeRecognizer(QGestureRecognizer *recognizer); - bool filterEvent(QEvent *event); + bool filterEvent(QWidget *receiver, QEvent *event); bool inGestureMode(); int makeGestureId(const QString &name); diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp index c6e1f65..6600bb0 100644 --- a/src/gui/kernel/qgesturerecognizer.cpp +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -71,7 +71,9 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); This enum type defines the state of the gesture recognizer. - \value NotGesture Not a gesture. + \value Ignore Gesture recognizer ignores the event. + + \value NotGesture Not a gesture. \value GestureStarted The continuous gesture has started. When the recognizer is in this state, a \l{QGestureEvent}{gesture event} @@ -126,15 +128,16 @@ QGestureRecognizerPrivate::QGestureRecognizerPrivate() /*! Creates a new gesture recognizer object that handles gestures of - the specific \a type as a child of \a parent. + the specific \a gestureType as a child of \a parent. \sa QApplication::addGestureRecognizer(), QApplication::removeGestureRecognizer(), */ -QGestureRecognizer::QGestureRecognizer(const QString &type, QObject *parent) +QGestureRecognizer::QGestureRecognizer(const QString &gestureType, QObject *parent) : QObject(*new QGestureRecognizerPrivate, parent) { - d_func()->customGestureType = type; + Q_D(QGestureRecognizer); + d->customGestureType = gestureType; } /*! diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h index 9e51e7e..8bc8b97 100644 --- a/src/gui/kernel/qgesturerecognizer.h +++ b/src/gui/kernel/qgesturerecognizer.h @@ -58,13 +58,14 @@ class Q_GUI_EXPORT QGestureRecognizer : public QObject public: enum Result { + Ignore, NotGesture, GestureStarted, GestureFinished, MaybeGesture }; - explicit QGestureRecognizer(const QString &type, QObject *parent = 0); + explicit QGestureRecognizer(const QString &gestureType, QObject *parent = 0); QString gestureType() const; diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp index cf2bb41..f6b8b90 100644 --- a/src/gui/kernel/qgesturestandardrecognizers.cpp +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -144,7 +144,7 @@ QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *even } return result; } - return QGestureRecognizer::NotGesture; + return QGestureRecognizer::Ignore; } QGesture* QGestureRecognizerPan::getGesture() @@ -202,10 +202,12 @@ QGestureRecognizer::Result QDoubleTapGestureRecognizer::filterEvent(const QEvent } else if ((pressedPosition - ev->pos()).manhattanLength() < 10) { return QGestureRecognizer::GestureFinished; } + return QGestureRecognizer::NotGesture; } else if (event->type() == QEvent::MouseButtonRelease) { const QMouseEvent *ev = static_cast(event); if (!pressedPosition.isNull() && (pressedPosition - ev->pos()).manhattanLength() < 10) return QGestureRecognizer::MaybeGesture; + return QGestureRecognizer::NotGesture; } else if (event->type() == QEvent::MouseButtonDblClick) { const QMouseEvent *ev = static_cast(event); pressedPosition = ev->pos(); @@ -264,7 +266,7 @@ QGestureRecognizer::Result QTapAndHoldGestureRecognizer::filterEvent(const QEven timer.stop(); return QGestureRecognizer::NotGesture; } - return QGestureRecognizer::NotGesture; + return QGestureRecognizer::Ignore; } void QTapAndHoldGestureRecognizer::timerEvent(QTimerEvent *event) -- cgit v0.12 From 6732e5fb7f35e79ba84c8bbee37541d067f309ec Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 6 May 2009 14:56:41 +0200 Subject: Improved delaying mouse events delivery. --- src/gui/kernel/qgesturemanager.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 1635ac4..fd1eaca 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -354,11 +354,13 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) delayedPressTimer = startTimer(eventDeliveryDelayTimeout); if (!delayedPressTimer) qWarning("QGestureManager: couldn't start delayed press timer!"); + ret = true; } - // if we have postponed a mouse press event, postpone all - // following event - if (delayedPressTimer) + if (delayedPressTimer && event->type() == QEvent::MouseMove) { + // if we have postponed a mouse press event, postpone all + // subsequent mouse move events as well. ret = true; + } lastPos = currentPos; return ret; -- cgit v0.12 From 73a0bca60630abe73deaab49ce310e2537a31abb Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 6 May 2009 17:28:20 +0200 Subject: Oops, we should replay mouse events to the same widget that was supposed to receive them. --- src/gui/kernel/qgesturemanager.cpp | 40 +++++++++++++++++++++++--------------- src/gui/kernel/qgesturemanager_p.h | 2 ++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index fd1eaca..ef4b3d0 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -67,7 +67,7 @@ static const unsigned int MaximumGestureRecognitionTimeout = 2000; QGestureManager::QGestureManager(QObject *parent) : QObject(parent), targetWidget(0), eventDeliveryDelayTimeout(300), - delayedPressTimer(0), lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), + delayedPressTimer(0), lastMousePressReceiver(0), lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), lastGestureId(0), state(NotGesture) { qRegisterMetaType(); @@ -97,11 +97,12 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) { if (state != Gesture) { // find the target widget - while (receiver && receiver->d_func()->gestures.isEmpty()) - receiver = receiver->parentWidget(); - if (!receiver) // no widget in the tree that accepts gestures. + QWidget *w = receiver; + while (w && w->d_func()->gestures.isEmpty()) + w = w->parentWidget(); + if (!w) // no widget in the tree that accepts gestures. return false; - targetWidget = receiver; + targetWidget = w; } QPoint currentPos; @@ -316,6 +317,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) DEBUG() << "QGestureManager: gesture started. Forgetting about postponed mouse press event"; killTimer(delayedPressTimer); delayedPressTimer = 0; + lastMousePressReceiver = 0; } else if (delayedPressTimer && (state == NotGesture || event->type() == QEvent::MouseButtonRelease)) { // not a gesture or released button too fast, so replay press @@ -330,14 +332,16 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) maybeGestures.clear(); state = NotGesture; - Q_ASSERT(targetWidget != 0); - QApplication::sendEvent(targetWidget, &lastMousePressEvent); - if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent *me = static_cast(event); - QMouseEvent move(QEvent::MouseMove, me->pos(), me->globalPos(), me->button(), - me->buttons(), me->modifiers()); - QApplication::sendEvent(targetWidget, &move); - ret = false; + if (lastMousePressReceiver) { + QApplication::sendEvent(lastMousePressReceiver, &lastMousePressEvent); + if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *me = static_cast(event); + QMouseEvent move(QEvent::MouseMove, me->pos(), me->globalPos(), me->button(), + me->buttons(), me->modifiers()); + QApplication::sendEvent(lastMousePressReceiver, &move); + ret = false; + } + lastMousePressReceiver = 0; } killTimer(delayedPressTimer); delayedPressTimer = 0; @@ -347,6 +351,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) // sure whether it is a gesture. DEBUG() << "QGestureManager: postponing mouse press event"; QMouseEvent *me = static_cast(event); + lastMousePressReceiver = receiver; lastMousePressEvent = QMouseEvent(QEvent::MouseButtonPress, me->pos(), me->globalPos(), me->button(), me->buttons(), me->modifiers()); @@ -382,9 +387,12 @@ void QGestureManager::timerEvent(QTimerEvent *event) maybeGestures.clear(); state = NotGesture; - // we neither received a mouse release event nor gesture - // started, so we replay stored mouse press event. - QApplication::sendEvent(targetWidget, &lastMousePressEvent); + if (lastMousePressReceiver) { + // we neither received a mouse release event nor gesture + // started, so we replay stored mouse press event. + QApplication::sendEvent(lastMousePressReceiver, &lastMousePressEvent); + lastMousePressReceiver = 0; + } killTimer(delayedPressTimer); delayedPressTimer = 0; diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 6de3baa..2b86fcd 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -57,6 +57,7 @@ #include "qset.h" #include "qevent.h" #include "qbasictimer.h" +#include "qpointer.h" #include "qgesturerecognizer.h" @@ -105,6 +106,7 @@ private: int eventDeliveryDelayTimeout; int delayedPressTimer; + QPointer lastMousePressReceiver; QMouseEvent lastMousePressEvent; QMap gestureIdMap; -- cgit v0.12 From 9f9904c52d3d598b91e589c435fa96333ef1232f Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 7 May 2009 21:07:50 +0200 Subject: Improved gesture target widget detection. Gesture is now associated with a target widget and whenever several gesture events occur at the same time if they are supposed to be handled by different widgets, each widget will receive only gestures related to itself. For example this makes possible to use gesture framework with multitouch when user interacts with two widgets at the same time. GraphicsView implements is not implemented yet. --- src/gui/kernel/qevent.h | 1 + src/gui/kernel/qgesture_p.h | 2 + src/gui/kernel/qgesturemanager.cpp | 86 +++++++++++++++++++++++++------------- src/gui/kernel/qgesturemanager_p.h | 6 +-- 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 280ca79..666cd3f 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -736,6 +736,7 @@ protected: QSet m_cancelledGestures; friend class QApplication; + friend class QGestureManager; }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index cf4e760..22d6d7f 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -85,6 +85,8 @@ public: QString type; Qt::GestureState state; + QPointer widget; + QRect rect; QPoint hotSpot; QDateTime startTime; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index ef4b3d0..a93248a 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -41,6 +41,7 @@ #include "qgesturemanager_p.h" #include "qgesture.h" +#include "qgesture_p.h" #include "qevent.h" #include "qapplication.h" @@ -66,7 +67,7 @@ bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); static const unsigned int MaximumGestureRecognitionTimeout = 2000; QGestureManager::QGestureManager(QObject *parent) - : QObject(parent), targetWidget(0), eventDeliveryDelayTimeout(300), + : QObject(parent), eventDeliveryDelayTimeout(300), delayedPressTimer(0), lastMousePressReceiver(0), lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), lastGestureId(0), state(NotGesture) { @@ -95,16 +96,6 @@ void QGestureManager::removeRecognizer(QGestureRecognizer *recognizer) bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) { - if (state != Gesture) { - // find the target widget - QWidget *w = receiver; - while (w && w->d_func()->gestures.isEmpty()) - w = w->parentWidget(); - if (!w) // no widget in the tree that accepts gestures. - return false; - targetWidget = w; - } - QPoint currentPos; switch (event->type()) { case QEvent::MouseButtonPress: @@ -127,7 +118,6 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) DEBUG() << "QGestureManager: current event processing state: " << (state == NotGesture ? "NotGesture" : "MaybeGesture"); - Q_ASSERT(targetWidget != 0); QSet stillMaybeGestures; // try other recognizers. foreach(QGestureRecognizer *r, recognizers) { @@ -186,9 +176,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) gestures << gesture; } Q_ASSERT(!gestures.isEmpty()); - QGestureEvent event(gestures); - ret = sendGestureEvent(targetWidget, &event); - ret = ret && event.isAccepted(); + ret = sendGestureEvent(receiver, gestures); if (!activeGestures.isEmpty()) { DEBUG() << "QGestureManager: new state = Gesture"; @@ -251,6 +239,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) } } } + // TODO: make sure that if gesture recognizer ignored the event we dont swallow it. activeGestures -= newMaybeGestures; activeGestures -= cancelledGestures; @@ -291,9 +280,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) foreach(QGestureRecognizer *r, cancelledGestures) cancelledGestureNames << r->gestureType(); if(!gestures.isEmpty()) { - QGestureEvent event(gestures, cancelledGestureNames); - ret = sendGestureEvent(targetWidget, &event); - ret = ret && event.isAccepted(); + ret = sendGestureEvent(receiver, gestures, cancelledGestureNames); } foreach(QGestureRecognizer *r, finishedGestures) @@ -343,6 +330,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) } lastMousePressReceiver = 0; } + lastMousePressReceiver = 0; killTimer(delayedPressTimer); delayedPressTimer = 0; } else if (state == MaybeGesture && event->type() == QEvent::MouseButtonPress @@ -394,6 +382,7 @@ void QGestureManager::timerEvent(QTimerEvent *event) lastMousePressReceiver = 0; } + lastMousePressReceiver = 0; killTimer(delayedPressTimer); delayedPressTimer = 0; } else { @@ -425,11 +414,6 @@ bool QGestureManager::inGestureMode() return state == Gesture; } -void QGestureManager::setGestureTargetWidget(QWidget *widget) -{ - targetWidget = widget; -} - void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) { QGestureRecognizer *recognizer = qobject_cast(sender()); @@ -459,8 +443,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) if (QGesture *gesture = recognizer->getGesture()) gestures << gesture; if(!gestures.isEmpty()) { - QGestureEvent event(gestures); - sendGestureEvent(targetWidget, &event); + //FIXME: sendGestureEvent(targetWidget, gestures); } if (result == QGestureRecognizer::GestureFinished) recognizer->reset(); @@ -469,9 +452,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) case QGestureRecognizer::MaybeGesture: { DEBUG() << "QGestureManager: maybe gesture: " << recognizer; if (activeGestures.contains(recognizer)) { - QGestureEvent event(QList(), - QSet() << recognizer->gestureType()); - sendGestureEvent(targetWidget, &event); + //FIXME: sendGestureEvent(targetWidget, QList(), QSet() << recognizer->gestureType()); } if (!maybeGestures.contains(recognizer)) { int timerId = startTimer(MaximumGestureRecognitionTimeout); @@ -499,9 +480,54 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) } } -bool QGestureManager::sendGestureEvent(QWidget *receiver, QGestureEvent *event) +bool QGestureManager::sendGestureEvent(QWidget *receiver, const QList &gestures, + const QSet &cancelled) { - return qt_sendSpontaneousEvent(receiver, event); + typedef QMap > WidgetGesturesMap; + WidgetGesturesMap widgetGestures; + for(QList::const_iterator it = gestures.begin(), e = gestures.end(); + it != e; ++it) { + QGesture *g = *it; + QGesturePrivate *gd = g->d_func(); + if (g->state() == Qt::GestureStarted) { + // find the target widget + QWidget *w = receiver; + while (w) { + QSet::iterator it = w->d_func()->gestures.begin(), + e = w->d_func()->gestures.end(); + for (; it != e; ++it) { + if (gestureNameFromId(*it) == g->type()) + break; + } + if (it != e) + break; + w = w->parentWidget(); + } + if (!w) // no widget in the tree that accepts this gesture. + continue; + gd->widget = w; + } + if (!gd->widget) { + DEBUG() << "QGestureManager: didn't find a widget to send gesture event (" + << g->type() << ") for tree:" << receiver; + continue; + } + widgetGestures[gd->widget].append(g); + } + + // we return true and stop original from being delivered if any of + // the gesture events were accepted by a receiver. + bool ret = false; + for(WidgetGesturesMap::const_iterator it = widgetGestures.begin(), e = widgetGestures.end(); + it != e; ++it) { + QWidget *receiver = it.key(); + Q_ASSERT(receiver != 0 /*should be taken care above*/); + // TODO: send cancelled gesture event to the widget that received the original gesture! + QGestureEvent event(it.value(), cancelled); + if (qt_sendSpontaneousEvent(receiver, &event) && event.isAccepted()) + ret = true; + } + return ret; } int QGestureManager::eventDeliveryDelay() const diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 2b86fcd..beed323 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -70,8 +70,6 @@ class Q_GUI_EXPORT QGestureManager : public QObject public: QGestureManager(QObject *parent); - void setGestureTargetWidget(QWidget *widget); - int eventDeliveryDelay() const; void setEventDeliveryDelay(int ms); @@ -95,13 +93,13 @@ private slots: void recognizerStateChanged(QGestureRecognizer::Result); private: - bool sendGestureEvent(QWidget *receiver, QGestureEvent *event); + bool sendGestureEvent(QWidget *receiver, const QList &gestures, + const QSet &cancelled = QSet()); QSet activeGestures; QMap maybeGestures; QSet recognizers; - QWidget *targetWidget; QPoint lastPos; int eventDeliveryDelayTimeout; -- cgit v0.12 From 0edf9fc7ede35995ec37197b5fb5ef92a4ccf60f Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 8 May 2009 18:38:01 +0200 Subject: doc fix --- doc/src/qnamespace.qdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/src/qnamespace.qdoc b/doc/src/qnamespace.qdoc index dcad96b..ae0be1a 100644 --- a/doc/src/qnamespace.qdoc +++ b/doc/src/qnamespace.qdoc @@ -2695,9 +2695,9 @@ This enum type describes the state of a gesture. \omitvalue NoGesture - \value GestureStarted The continuous gesture has started. - \value GestureUpdated The gesture continiues. - \value GestureFinished The gesture has finished. + \value GestureStarted A continuous gesture has started. + \value GestureUpdated A gesture continiues. + \value GestureFinished A gesture has finished. \sa QGesture */ -- cgit v0.12 From 81a64345258f2f2ad5cf77355f7ecbd3ebad9734 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 8 May 2009 18:46:44 +0200 Subject: Improved gesture propagation. Each gesture can now be accepted separately and not accepted gestures will be propagated to parent widget that are subscribed to them. Added an internal flag to specify that gesture is a "singleshot" - aka if the gesture is not continious and will not have a GestureStarted state, but only GestureFinished. Asynchronous gestures still need to fixed, as well as QGraphicsView. --- src/gui/kernel/qapplication.cpp | 31 ---------- src/gui/kernel/qevent.cpp | 51 +++++++++++++++- src/gui/kernel/qevent.h | 11 +++- src/gui/kernel/qgesture.cpp | 2 +- src/gui/kernel/qgesture.h | 8 +++ src/gui/kernel/qgesture_p.h | 3 +- src/gui/kernel/qgesturemanager.cpp | 120 +++++++++++++++++++++++++++++-------- src/gui/kernel/qgesturemanager_p.h | 3 +- 8 files changed, 167 insertions(+), 62 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 9772e0f..ba105e0 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4019,37 +4019,6 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } break; #endif - - case QEvent::Gesture: { - QWidget *w = static_cast(receiver); - QGestureEvent *ge = static_cast(e); - QSet eventGestures; - foreach(const QString &gesture, ge->gestureTypes()) - eventGestures << gesture; - bool eventAccepted = ge->isAccepted(); - - QPoint offset; - while (w) { - QSet widgetGestures = w->d_func()->gestures; - foreach(int gestureId, widgetGestures) { - if (eventGestures.contains(QGestureManager::instance()->gestureNameFromId(gestureId))) { - foreach(QGesture *gesture, ge->gestures()) - gesture->translate(offset); - offset = QPoint(); - res = d->notify_helper(w, ge); - ge->spont = false; - eventAccepted = ge->isAccepted(); - if (res && eventAccepted) - break; - } - } - if (w->isWindow()) - break; - offset += w->pos(); - w = w->parentWidget(); - } - break; - } case QEvent::TouchBegin: // Note: TouchUpdate and TouchEnd events are sent to d->currentMultitouchWidget and never propagated { diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index f24f186..fa85bf2 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3532,10 +3532,11 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) are being executed and a list of gesture that were cancelled (\a cancelledGestures). */ -QGestureEvent::QGestureEvent(const QList &gestures, +QGestureEvent::QGestureEvent(const QSet &gestures, const QSet &cancelledGestures) : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) { + setAccepted(false); foreach(QGesture *r, gestures) m_gestures.insert(r->type(), r); } @@ -3606,6 +3607,54 @@ QSet QGestureEvent::cancelledGestures() const return m_cancelledGestures; } +/*! + Sets the accept flag of the all gestures inside the event object, + the equivalent of calling \l{QEvent::accept()}{accept()} or + \l{QEvent::setAccepted()}{setAccepted(true)}. + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::acceptAll() +{ + QHash::iterator it = m_gestures.begin(), + e = m_gestures.end(); + for(; it != e; ++it) + it.value()->accept(); + setAccepted(true); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::accept(Qt::GestureType type) +{ + if (QGesture *g = m_gestures.value(qt_getStandardGestureTypeName(type), 0)) + g->accept(); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::accept(const QString &type) +{ + if (QGesture *g = m_gestures.value(type, 0)) + g->accept(); +} + /*! \class QTouchEvent \brief The QTouchEvent class contains parameters that describe a touch event . diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 666cd3f..ea4f577 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -716,7 +716,7 @@ private: class Q_GUI_EXPORT QGestureEvent : public QEvent { public: - QGestureEvent(const QList &gestures, + QGestureEvent(const QSet &gestures, const QSet &cancelledGestures = QSet()); ~QGestureEvent(); @@ -731,6 +731,15 @@ public: QSet cancelledGestures() const; + void acceptAll(); +#ifndef Q_NO_USING_KEYWORD + using QEvent::accept; +#else + inline void accept() { QEvent::accept(); } +#endif + void accept(Qt::GestureType type); + void accept(const QString &type); + protected: QHash m_gestures; QSet m_cancelledGestures; diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index 4d492f8..7437ed3 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -79,7 +79,7 @@ QString qt_getStandardGestureTypeName(Qt::GestureType type); QGestureRecognizer classes. */ QGesture::QGesture(QObject *parent, const QString &type, Qt::GestureState state) - : QObject(*new QGesturePrivate, parent) + : QObject(*new QGesturePrivate, parent), m_accept(0) { Q_D(QGesture); d->type = type; diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index da1bc90..d220232 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -82,6 +82,12 @@ public: uint duration, Qt::GestureState state); virtual ~QGesture(); + inline void setAccepted(bool accepted) { m_accept = accepted; } + inline bool isAccepted() const { return m_accept; } + + inline void accept() { m_accept = true; } + inline void ignore() { m_accept = false; } + QString type() const; Qt::GestureState state() const; @@ -100,6 +106,8 @@ protected: virtual void translate(const QPoint &offset); private: + ushort m_accept : 1; + friend class QGestureManager; friend class QApplication; friend class QGestureRecognizerPan; diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 22d6d7f..d2d77cc 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -66,7 +66,7 @@ class QGesturePrivate : public QObjectPrivate public: QGesturePrivate() - : duration(0) { } + : state(Qt::NoGesture), singleshot(0), duration(0) { } void init(const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, @@ -86,6 +86,7 @@ public: Qt::GestureState state; QPointer widget; + uint singleshot:1; QRect rect; QPoint hotSpot; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index a93248a..c7341f2 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -166,14 +166,18 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) DEBUG() << "QGestureManager: sending gesture event for: " << activeGestures << " and " << finishedGestures; - QList gestures; + QSet gestures; foreach(QGestureRecognizer *r, finishedGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = true; + } } foreach(QGestureRecognizer *r, activeGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = false; + } } Q_ASSERT(!gestures.isEmpty()); ret = sendGestureEvent(receiver, gestures); @@ -243,7 +247,6 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) activeGestures -= newMaybeGestures; activeGestures -= cancelledGestures; - activeGestures -= finishedGestures; activeGestures += startedGestures; foreach(QGestureRecognizer *r, startedGestures+finishedGestures+notGestures) { QMap::iterator it = maybeGestures.find(r); @@ -260,7 +263,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) maybeGestures.insert(r, timerId); } } - QList gestures; + QSet gestures; if (!finishedGestures.isEmpty() || !activeGestures.isEmpty()) { // another gesture found! ret = true; @@ -268,12 +271,17 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) << activeGestures << " and " << finishedGestures; foreach(QGestureRecognizer *r, finishedGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = activeGestures.contains(r); + } } + activeGestures -= finishedGestures; foreach(QGestureRecognizer *r, activeGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = false; + } } } QSet cancelledGestureNames; @@ -439,7 +447,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) killTimer(maybeGestures.value(recognizer)); maybeGestures.remove(recognizer); } - QList gestures; + QSet gestures; if (QGesture *gesture = recognizer->getGesture()) gestures << gesture; if(!gestures.isEmpty()) { @@ -452,7 +460,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) case QGestureRecognizer::MaybeGesture: { DEBUG() << "QGestureManager: maybe gesture: " << recognizer; if (activeGestures.contains(recognizer)) { - //FIXME: sendGestureEvent(targetWidget, QList(), QSet() << recognizer->gestureType()); + //FIXME: sendGestureEvent(targetWidget, QSet(), QSet() << recognizer->gestureType()); } if (!maybeGestures.contains(recognizer)) { int timerId = startTimer(MaximumGestureRecognitionTimeout); @@ -480,43 +488,63 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) } } -bool QGestureManager::sendGestureEvent(QWidget *receiver, const QList &gestures, +bool QGestureManager::widgetHasGesture(QWidget *widget, QGesture *gesture) const +{ + const QString gestureName = gesture->type(); + QSet::iterator it = widget->d_func()->gestures.begin(), + e = widget->d_func()->gestures.end(); + for (; it != e; ++it) { + if (gestureNameFromId(*it) == gestureName) + return true; + } + return false; +} + +bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet &gestures, const QSet &cancelled) { - typedef QMap > WidgetGesturesMap; + if (gestures.isEmpty()) + return false; + DEBUG() << "QGestureManager::sendGestureEvent: sending to" << receiver + << "gestures:" << gestures << "; cancelled:" << cancelled; + QSet startedGestures; + // grouping gesture objects by receiver widgets. + typedef QMap > WidgetGesturesMap; WidgetGesturesMap widgetGestures; - for(QList::const_iterator it = gestures.begin(), e = gestures.end(); + for(QSet::const_iterator it = gestures.begin(), e = gestures.end(); it != e; ++it) { QGesture *g = *it; QGesturePrivate *gd = g->d_func(); - if (g->state() == Qt::GestureStarted) { + if (!gd->widget && (g->state() == Qt::GestureStarted || g->state() == Qt::GestureFinished)) { + startedGestures.insert(g); // find the target widget QWidget *w = receiver; + QPoint offset; while (w) { - QSet::iterator it = w->d_func()->gestures.begin(), - e = w->d_func()->gestures.end(); - for (; it != e; ++it) { - if (gestureNameFromId(*it) == g->type()) - break; - } - if (it != e) + if (widgetHasGesture(w, g)) break; + if (w->isWindow()) { + w = 0; + break; + } + offset += w->pos(); w = w->parentWidget(); } if (!w) // no widget in the tree that accepts this gesture. continue; gd->widget = w; + g->translate(offset); } if (!gd->widget) { DEBUG() << "QGestureManager: didn't find a widget to send gesture event (" << g->type() << ") for tree:" << receiver; + // TODO: maybe we should reset gesture recognizers when nobody interested in its gestures. continue; } - widgetGestures[gd->widget].append(g); + widgetGestures[gd->widget].insert(g); } - // we return true and stop original from being delivered if any of - // the gesture events were accepted by a receiver. + QSet ignoredGestures; bool ret = false; for(WidgetGesturesMap::const_iterator it = widgetGestures.begin(), e = widgetGestures.end(); it != e; ++it) { @@ -524,10 +552,50 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QList Q_ASSERT(receiver != 0 /*should be taken care above*/); // TODO: send cancelled gesture event to the widget that received the original gesture! QGestureEvent event(it.value(), cancelled); - if (qt_sendSpontaneousEvent(receiver, &event) && event.isAccepted()) - ret = true; + bool processed = qt_sendSpontaneousEvent(receiver, &event); + QSet started = startedGestures.intersect(it.value()); + if (!started.isEmpty() && !(processed && event.isAccepted())) { + // there are started gestures event that weren't + // accepted, so propagating each gesture independently. + QSet::const_iterator it = started.begin(), + e = started.end(); + for(; it != e; ++it) { + QGesture *g = *it; + if (processed && g->isAccepted()) { + ret = true; + continue; + } + // if it wasn't accepted, find the first parent widget + // that is subscribed to the gesture. + QGesturePrivate *gd = g->d_func(); + QWidget *w = gd->widget; + gd->widget = 0; + + if (!w->isWindow()) { + g->translate(w->pos()); + w = w->parentWidget(); + QPoint offset; + while (w) { + if (widgetHasGesture(w, g)) { + DEBUG() << "QGestureManager: sendGestureEvent:" << w << "didn't accept gesture" << g; + ignoredGestures.insert(g); + break; + } + if (w->isWindow()) { + w = 0; + break; + } + offset += w->pos(); + w = w->parentWidget(); + } + if (w) + g->translate(offset); + } + } + } } - return ret; + // try to send all gestures that were ignored to the next parent + return sendGestureEvent(0, ignoredGestures, cancelled) || ret; } int QGestureManager::eventDeliveryDelay() const diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index beed323..963edf9 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -93,7 +93,8 @@ private slots: void recognizerStateChanged(QGestureRecognizer::Result); private: - bool sendGestureEvent(QWidget *receiver, const QList &gestures, + bool widgetHasGesture(QWidget *widget, QGesture *gesture) const; + bool sendGestureEvent(QWidget *receiver, const QSet &gestures, const QSet &cancelled = QSet()); QSet activeGestures; -- cgit v0.12 From fd55642d60b852fbd175a9ea7c1a2457c6f611aa Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 11 May 2009 12:18:08 +0200 Subject: Added accept* method to QGraphicsSceneGestureEvent as well. --- src/gui/graphicsview/qgraphicssceneevent.cpp | 48 ++++++++++++++++++++++++++++ src/gui/graphicsview/qgraphicssceneevent.h | 9 ++++++ 2 files changed, 57 insertions(+) diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 86825b1..9795fda 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1890,6 +1890,54 @@ void QGraphicsSceneGestureEvent::setGestures(const QList &gestures) m_gestures.insert(g->type(), g); } +/*! + Sets the accept flag of the all gestures inside the event object, + the equivalent of calling \l{QEvent::accept()}{accept()} or + \l{QEvent::setAccepted()}{setAccepted(true)}. + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGraphicsSceneGestureEvent::acceptAll() +{ + QHash::iterator it = m_gestures.begin(), + e = m_gestures.end(); + for(; it != e; ++it) + it.value()->accept(); + setAccepted(true); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGraphicsSceneGestureEvent::accept(Qt::GestureType type) +{ + if (QGesture *g = m_gestures.value(qt_getStandardGestureTypeName(type), 0)) + g->accept(); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGraphicsSceneGestureEvent::accept(const QString &type) +{ + if (QGesture *g = m_gestures.value(type, 0)) + g->accept(); +} + class QGraphicsSceneTouchEventPrivate : public QGraphicsSceneEventPrivate { Q_DECLARE_PUBLIC(QGraphicsSceneTouchEvent) diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index 244c25e..4b7065e 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -329,6 +329,15 @@ public: QSet cancelledGestures() const; void setCancelledGestures(const QSet &cancelledGestures); + void acceptAll(); +#ifndef Q_NO_USING_KEYWORD + using QEvent::accept; +#else + inline void accept() { QEvent::accept(); } +#endif + void accept(Qt::GestureType type); + void accept(const QString &type); + QPointF mapToScene(const QPoint &point) const; QPolygonF mapToScene(const QRect &rect) const; QPointF mapToItem(const QPoint &point, QGraphicsItem *item) const; -- cgit v0.12 From 0466a193fd1b8bd7c6f165c3032c47a08a881529 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 11 May 2009 16:49:09 +0200 Subject: Added missing \since 4.6 doc tag --- doc/src/qnamespace.qdoc | 3 +++ src/gui/graphicsview/qgraphicsitem.cpp | 8 ++++++++ src/gui/graphicsview/qgraphicssceneevent.cpp | 5 ++++- src/gui/kernel/qapplication.cpp | 5 +++++ src/gui/kernel/qevent.cpp | 1 + src/gui/kernel/qgesture.cpp | 2 ++ src/gui/kernel/qgesturerecognizer.cpp | 2 ++ src/gui/kernel/qwidget.cpp | 8 ++++++++ 8 files changed, 33 insertions(+), 1 deletion(-) diff --git a/doc/src/qnamespace.qdoc b/doc/src/qnamespace.qdoc index ae0be1a..21aae2d 100644 --- a/doc/src/qnamespace.qdoc +++ b/doc/src/qnamespace.qdoc @@ -2677,6 +2677,7 @@ */ /*! \enum Qt::GestureType + \since 4.6 This enum lists standard gestures. @@ -2691,6 +2692,7 @@ /*! \enum Qt::GestureState + \since 4.6 This enum type describes the state of a gesture. @@ -2704,6 +2706,7 @@ /*! \enum Qt::DirectionType + \since 4.6 This enum type describes directions. This could be used by the gesture recognizers. diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 3d2d1b6..e2b8115 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -5792,6 +5792,8 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const } /*! + \since 4.6 + Subscribes the graphics item to the specified \a gesture type. Returns the id of the gesture. @@ -5804,6 +5806,8 @@ int QGraphicsItem::grabGesture(Qt::GestureType gesture) } /*! + \since 4.6 + Subscribes the graphics item to the specified \a gesture type. Returns the id of the gesture. @@ -5820,6 +5824,8 @@ int QGraphicsItem::grabGesture(const QString &gesture) } /*! + \since 4.6 + Unsubscribes the graphics item from a gesture, which is specified by the \a gestureId. @@ -5834,6 +5840,8 @@ void QGraphicsItem::releaseGesture(int gestureId) } /*! + \since 4.6 + If \a enable is true, the gesture with the given \a gestureId is enabled; otherwise the gesture is disabled. diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 9795fda..b599632 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1711,8 +1711,9 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) /*! \class QGraphicsSceneGestureEvent \brief The QGraphicsSceneGestureEvent class provides gesture events for - the graphics view framework. + the graphics view framework. \since 4.6 + \ingroup multimedia \ingroup graphicsview-api QGraphicsSceneGestureEvent extends information provided by @@ -1725,6 +1726,8 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) The scene sends the event to the first QGraphicsItem under the mouse cursor that accepts gestures; a graphics item is set to accept gestures with \l{QGraphicsItem::}{grabGesture()}. + + \sa QGestureEvent */ /*! diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index ba105e0..e9a113e 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -5058,6 +5058,8 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) } /*! + \since 4.6 + Adds custom gesture \a recognizer object. Qt takes ownership of the provided \a recognizer. @@ -5070,6 +5072,8 @@ void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) } /*! + \since 4.6 + Removes custom gesture \a recognizer object. \sa Qt::AA_EnableGestures, QGestureEvent @@ -5084,6 +5088,7 @@ void QApplication::removeGestureRecognizer(QGestureRecognizer *recognizer) /*! \property QApplication::eventDeliveryDelayForGestures + \since 4.6 Specifies the \a delay before input events are delivered to the gesture enabled widgets. diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index fa85bf2..4c31d33 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3513,6 +3513,7 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) /*! \class QGestureEvent + \since 4.6 \ingroup events \brief The QGestureEvent class provides the parameters used for diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index 7437ed3..ac55521 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -48,6 +48,7 @@ QString qt_getStandardGestureTypeName(Qt::GestureType type); /*! \class QGesture + \since 4.6 \brief The QGesture class represents a gesture, containing all properties that describe a gesture. @@ -242,6 +243,7 @@ QPoint QGesture::pos() const /*! \class QPanningGesture + \since 4.6 \brief The QPanningGesture class represents a Pan gesture, providing additional information related to panning. diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp index 6600bb0..41160cd 100644 --- a/src/gui/kernel/qgesturerecognizer.cpp +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -51,6 +51,7 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); /*! \class QGestureRecognizer + \since 4.6 \brief The QGestureRecognizer class is the base class for implementing custom gestures. @@ -68,6 +69,7 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); /*! \enum QGestureRecognizer::Result + \since 4.6 This enum type defines the state of the gesture recognizer. diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 22c2ea7..1be4573 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -11024,6 +11024,8 @@ QWindowSurface *QWidget::windowSurface() const } /*! + \since 4.6 + Subscribes the widget to the specified \a gesture type. Returns the id of the gesture. @@ -11057,6 +11059,8 @@ bool QWidgetPrivate::releaseGesture(int gestureId) } /*! + \since 4.6 + Subscribes the widget to the specified \a gesture type. Returns the id of the gesture. @@ -11069,6 +11073,8 @@ int QWidget::grabGesture(Qt::GestureType gesture) } /*! + \since 4.6 + Unsubscribes the widget from a gesture, which is specified by the \a gestureId. @@ -11082,6 +11088,8 @@ void QWidget::releaseGesture(int gestureId) } /*! + \since 4.6 + If \a enable is true, the gesture with the given \a gestureId is enabled; otherwise the gesture is disabled. -- cgit v0.12 From 21e699d7abff8b75157ee1305ac1dee35c748e98 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 11 May 2009 17:24:02 +0200 Subject: Fixed gesture event id after merging branches --- src/corelib/kernel/qcoreevent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index cb24723..76cdf7c 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -273,8 +273,8 @@ public: GraphicsSceneTouchUpdate = 197, GraphicsSceneTouchEnd = 198, - Gesture = 191, - GraphicsSceneGesture = 192, + Gesture = 199, + GraphicsSceneGesture = 200, // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event -- cgit v0.12 From f14620878beed97c85c4b6a03a38f485800328d1 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 12 May 2009 10:34:54 +0200 Subject: Added gesture enabled browser demo to project file. Added missing files for building the demo. --- examples/gestures/browser/data/addtab.png | Bin 0 -> 469 bytes examples/gestures/browser/data/browser.svg | 411 +++++++++++++++++++++ examples/gestures/browser/data/closetab.png | Bin 0 -> 516 bytes examples/gestures/browser/data/data.qrc | 11 + .../gestures/browser/data/defaultbookmarks.xbel | 40 ++ examples/gestures/browser/data/defaulticon.png | Bin 0 -> 1473 bytes examples/gestures/browser/data/history.png | Bin 0 -> 1527 bytes examples/gestures/browser/data/loading.gif | Bin 0 -> 847 bytes examples/gestures/browser/htmls/htmls.qrc | 5 + examples/gestures/browser/htmls/notfound.html | 63 ++++ examples/gestures/gestures.pro | 1 + 11 files changed, 531 insertions(+) create mode 100644 examples/gestures/browser/data/addtab.png create mode 100644 examples/gestures/browser/data/browser.svg create mode 100644 examples/gestures/browser/data/closetab.png create mode 100644 examples/gestures/browser/data/data.qrc create mode 100644 examples/gestures/browser/data/defaultbookmarks.xbel create mode 100644 examples/gestures/browser/data/defaulticon.png create mode 100644 examples/gestures/browser/data/history.png create mode 100644 examples/gestures/browser/data/loading.gif create mode 100644 examples/gestures/browser/htmls/htmls.qrc create mode 100644 examples/gestures/browser/htmls/notfound.html diff --git a/examples/gestures/browser/data/addtab.png b/examples/gestures/browser/data/addtab.png new file mode 100644 index 0000000..20928fb Binary files /dev/null and b/examples/gestures/browser/data/addtab.png differ diff --git a/examples/gestures/browser/data/browser.svg b/examples/gestures/browser/data/browser.svg new file mode 100644 index 0000000..4b0fa72 --- /dev/null +++ b/examples/gestures/browser/data/browser.svg @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Qt Browser + + + Jens Bache-Wiig + + + + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/gestures/browser/data/closetab.png b/examples/gestures/browser/data/closetab.png new file mode 100644 index 0000000..ab9d669 Binary files /dev/null and b/examples/gestures/browser/data/closetab.png differ diff --git a/examples/gestures/browser/data/data.qrc b/examples/gestures/browser/data/data.qrc new file mode 100644 index 0000000..c7d0294 --- /dev/null +++ b/examples/gestures/browser/data/data.qrc @@ -0,0 +1,11 @@ + + + addtab.png + closetab.png + history.png + browser.svg + defaultbookmarks.xbel + loading.gif + defaulticon.png + + diff --git a/examples/gestures/browser/data/defaultbookmarks.xbel b/examples/gestures/browser/data/defaultbookmarks.xbel new file mode 100644 index 0000000..a168244 --- /dev/null +++ b/examples/gestures/browser/data/defaultbookmarks.xbel @@ -0,0 +1,40 @@ + + + + + Bookmarks Bar + + Qt Software + + + WebKit.org + + + Qt Documentation + + + Qt Quarterly + + + Qt Labs + + + Qt Centre + + + Qt-Apps.org + + + qtnode + + + xkcd + + + + Bookmarks Menu + + reddit.com: what's new online! + + + diff --git a/examples/gestures/browser/data/defaulticon.png b/examples/gestures/browser/data/defaulticon.png new file mode 100644 index 0000000..01a0920 Binary files /dev/null and b/examples/gestures/browser/data/defaulticon.png differ diff --git a/examples/gestures/browser/data/history.png b/examples/gestures/browser/data/history.png new file mode 100644 index 0000000..552a1cb Binary files /dev/null and b/examples/gestures/browser/data/history.png differ diff --git a/examples/gestures/browser/data/loading.gif b/examples/gestures/browser/data/loading.gif new file mode 100644 index 0000000..c1545eb Binary files /dev/null and b/examples/gestures/browser/data/loading.gif differ diff --git a/examples/gestures/browser/htmls/htmls.qrc b/examples/gestures/browser/htmls/htmls.qrc new file mode 100644 index 0000000..03b256c --- /dev/null +++ b/examples/gestures/browser/htmls/htmls.qrc @@ -0,0 +1,5 @@ + + + notfound.html + + diff --git a/examples/gestures/browser/htmls/notfound.html b/examples/gestures/browser/htmls/notfound.html new file mode 100644 index 0000000..b04a9f8 --- /dev/null +++ b/examples/gestures/browser/htmls/notfound.html @@ -0,0 +1,63 @@ + + +%1 + + + +
+ +

%2

+

When connecting to: %3.

+
    +
  • Check the address for errors such as ww.trolltech.com + instead of www.trolltech.com
  • +
  • If the address is correct, try checking the network + connection.
  • +
  • If your computer or network is protected by a firewall or + proxy, make sure that the browser demo is permitted to access + the network.
  • +
+

+
+ + diff --git a/examples/gestures/gestures.pro b/examples/gestures/gestures.pro index 8bd0acc..050be19 100644 --- a/examples/gestures/gestures.pro +++ b/examples/gestures/gestures.pro @@ -1,6 +1,7 @@ TEMPLATE = \ subdirs SUBDIRS = \ + browser \ imageviewer \ graphicsview \ collidingmice \ -- cgit v0.12 From 06d32cd1f14152ee0d49bc4a4a8adee953be11f4 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 12 May 2009 12:39:11 +0200 Subject: Keep the new QEvent::Type enumerators for touch and gestures sequential --- src/corelib/kernel/qcoreevent.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 76cdf7c..84ab390 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -266,15 +266,15 @@ public: CocoaRequestModal = 190, // Internal for requesting an application modal Cocoa Window MacGLClearDrawable = 191, // Internal Cocoa, the window has changed, so we must clear - TouchBegin = 193, - TouchUpdate = 194, - TouchEnd = 195, - GraphicsSceneTouchBegin = 196, - GraphicsSceneTouchUpdate = 197, - GraphicsSceneTouchEnd = 198, - - Gesture = 199, - GraphicsSceneGesture = 200, + TouchBegin = 192, + TouchUpdate = 193, + TouchEnd = 194, + GraphicsSceneTouchBegin = 195, + GraphicsSceneTouchUpdate = 196, + GraphicsSceneTouchEnd = 197, + + Gesture = 198, + GraphicsSceneGesture = 199, // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event -- cgit v0.12 From d9c2a93e1dc1ea0248d72fdc7f1bb863f8847658 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 12 May 2009 14:51:44 +0200 Subject: Don't store the current list of touch points for a widget in QWidgetPrivate This is information that's maintained by QApplication, so it belongs there instead. --- src/gui/kernel/qapplication.cpp | 4 ++-- src/gui/kernel/qapplication_p.h | 7 ++++-- src/gui/kernel/qapplication_win.cpp | 45 +++++++++++++++++++++---------------- src/gui/kernel/qwidget_p.h | 2 -- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index e9a113e..2fb6e91 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4048,8 +4048,8 @@ bool QApplication::notify(QObject *receiver, QEvent *e) touchPoint->d->widget = widget; } if (origin != widget) - origin->d_func()->currentTouchPoints.clear(); - widget->d_func()->currentTouchPoints = touchEvent->_touchPoints; + d->widgetCurrentTouchPoints.remove(origin); + d->widgetCurrentTouchPoints[widget] = touchEvent->_touchPoints; break; } else if (widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) { break; diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index a375933..e44e7aa 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -433,6 +433,7 @@ public: // map number of widget subscribed to it> QMap grabbedGestures; + QMap > widgetCurrentTouchPoints; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); #if defined(Q_WS_WIN) @@ -447,8 +448,10 @@ public: void initializeMultitouch(); static QTouchEvent::TouchPoint *findClosestTouchPoint(const QList &activeTouchPoints, const QPointF &pos); - QEvent::Type appendTouchPoint(QTouchEvent::TouchPoint *touchPoint); - QEvent::Type removeTouchPoint(QTouchEvent::TouchPoint *touchPoint); + QEvent::Type appendTouchPoint(QTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints); + QEvent::Type removeTouchPoint(QTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints); bool translateTouchEvent(const MSG &msg); #endif diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 723a3b4..ef49ac6 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4008,8 +4008,10 @@ void QApplicationPrivate::initializeMultitouch() GetTouchInputInfo = static_cast(library.resolve("GetTouchInputInfo")); CloseTouchInputHandle = static_cast(library.resolve("CloseTouchInputHandle")); + widgetCurrentTouchPoints.clear(); touchInputIDToTouchPointID.clear(); appAllTouchPoints.clear(); + appCurrentTouchPoints.clear(); } QTouchEvent::TouchPoint *QApplicationPrivate::findClosestTouchPoint(const QList &appActiveTouchPoints, @@ -4028,10 +4030,10 @@ QTouchEvent::TouchPoint *QApplicationPrivate::findClosestTouchPoint(const QList< return closestTouchPoint; } -QEvent::Type QApplicationPrivate::appendTouchPoint(QTouchEvent::TouchPoint *touchPoint) +QEvent::Type QApplicationPrivate::appendTouchPoint(QTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints) { - QWidget *widget = touchPoint->d->widget; - QEvent::Type eventType = widget->d_func()->currentTouchPoints.isEmpty() + QEvent::Type eventType = currentTouchPoints->isEmpty() ? QEvent::TouchBegin : QEvent::TouchUpdate; @@ -4043,11 +4045,11 @@ QEvent::Type QApplicationPrivate::appendTouchPoint(QTouchEvent::TouchPoint *touc } appCurrentTouchPoints.insert(at, touchPoint); // again, for the widget's currentTouchPoints - for (at = 0; at < widget->d_func()->currentTouchPoints.count(); ++at) { - if (widget->d_func()->currentTouchPoints.at(at)->id() > touchPoint->id()) + for (at = 0; at < currentTouchPoints->count(); ++at) { + if (currentTouchPoints->at(at)->id() > touchPoint->id()) break; } - widget->d_func()->currentTouchPoints.insert(at, touchPoint); + currentTouchPoints->insert(at, touchPoint); if (appCurrentTouchPoints.count() > appAllTouchPoints.count()) { qFatal("Qt: INTERNAL ERROR: appCurrentTouchPoints.count() (%d) > appAllTouchPoints.count() (%d)", @@ -4058,10 +4060,9 @@ QEvent::Type QApplicationPrivate::appendTouchPoint(QTouchEvent::TouchPoint *touc return eventType; } -QEvent::Type QApplicationPrivate::removeTouchPoint(QTouchEvent::TouchPoint *touchPoint) +QEvent::Type QApplicationPrivate::removeTouchPoint(QTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints) { - QWidget *widget = touchPoint->d->widget; - // remove touch point from all known touch points for (int i = qMin(appCurrentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { if (appCurrentTouchPoints.at(i) == touchPoint) { @@ -4070,14 +4071,14 @@ QEvent::Type QApplicationPrivate::removeTouchPoint(QTouchEvent::TouchPoint *touc } } // again, for the widget's currentTouchPoints - for (int i = qMin(widget->d_func()->currentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { - if (widget->d_func()->currentTouchPoints.at(i) == touchPoint) { - widget->d_func()->currentTouchPoints.removeAt(i); + for (int i = qMin(currentTouchPoints->count() - 1, touchPoint->id()); i >= 0; --i) { + if (currentTouchPoints->at(i) == touchPoint) { + currentTouchPoints->removeAt(i); break; } } - return widget->d_func()->currentTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate; + return currentTouchPoints->isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate; } bool QApplicationPrivate::translateTouchEvent(const MSG &msg) @@ -4114,6 +4115,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) bool down = touchPoint->d->state != Qt::TouchPointReleased; QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); QEvent::Type eventType = QEvent::None; + QList activeTouchPoints; if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { // determine which widget this event will go to @@ -4129,10 +4131,11 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) } touchPoint->d->widget = w; - eventType = appendTouchPoint(touchPoint); + QList ¤tTouchPoints = widgetCurrentTouchPoints[w]; + eventType = appendTouchPoint(touchPoint, ¤tTouchPoints); // make sure new points are added to activeTouchPoints as well appActiveTouchPoints = appCurrentTouchPoints; - activeTouchPoints = w->d_func()->currentTouchPoints; + activeTouchPoints = currentTouchPoints; touchPoint->d->state = Qt::TouchPointPressed; touchPoint->d->globalPos @@ -4142,9 +4145,10 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) touchPoint->d->pressure = qreal(1.); } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { QWidget *w = touchPoint->d->widget; + QList ¤tTouchPoints = widgetCurrentTouchPoints[w]; appActiveTouchPoints = appCurrentTouchPoints; - activeTouchPoints = w->d_func()->currentTouchPoints; - eventType = removeTouchPoint(touchPoint); + activeTouchPoints = currentTouchPoints; + eventType = removeTouchPoint(touchPoint, ¤tTouchPoints); touchPoint->d->state = Qt::TouchPointReleased; touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; @@ -4153,7 +4157,7 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) } else if (down) { QWidget *w = touchPoint->d->widget; appActiveTouchPoints = appCurrentTouchPoints; - activeTouchPoints = w->d_func()->currentTouchPoints; + activeTouchPoints = widgetCurrentTouchPoints.value(w); eventType = QEvent::TouchUpdate; touchPoint->d->state = globalPos == touchPoint->d->globalPos ? Qt::TouchPointStationary @@ -4199,10 +4203,13 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) break; } case QEvent::TouchEnd: - if (!widget->d_func()->currentTouchPoints.isEmpty()) { + { + QList currentTouchPoints = widgetCurrentTouchPoints.take(widget); + if (!currentTouchPoints.isEmpty()) { qFatal("Qt: INTERNAL ERROR, the widget's currentTouchPoints should be empty!"); } // fall-through intended + } default: if (widget->testAttribute(Qt::WA_AcceptedTouchBeginEvent)) { (void) QApplication::sendSpontaneousEvent(widget, &touchEvent); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 1ab31e4..d1f3acb 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -670,8 +670,6 @@ public: } QSize adjustedSize() const; - - QList currentTouchPoints; }; inline QWExtra *QWidgetPrivate::extraData() const -- cgit v0.12 From 7be95673a1266b306fd76cd43fe894f144b0e72d Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 13 May 2009 15:25:53 +0200 Subject: Support multiple touch targets in QGraphicsView This required a larger change to the kernel and graphicsview directories to make this as efficient as possible: 1. QTouchEvent::TouchPoint becomes the base for QGraphicsSceneTouchEvent::TouchPoint - this means there is one private for every touch point, and we can store both the screen and scene coordinates in one place. Converting a QTouchEvent to QGraphicsSceneTouchEvent becomes nothing more than casting the QTouchEvent::TouchPoints to QGraphicsSceneTouchEvent::TouchPoints. 2. The logic that we use in QApplication to convert WM_TOUCH* messages to QTouchEvents is essentially duplicated (with some minor changes) to QGraphicsScene so that it can support mulitple touch item targets. I will have to investigate how I can perhaps merge some of the duplicated code. QEvent::GraphicsSceneTouchBegin propagation is not implemented yet, and will come in a later commit --- src/gui/graphicsview/qgraphicsitem_p.h | 1 + src/gui/graphicsview/qgraphicsscene.cpp | 276 +++++++++++++++------------ src/gui/graphicsview/qgraphicsscene_p.h | 17 +- src/gui/graphicsview/qgraphicssceneevent.cpp | 178 +---------------- src/gui/graphicsview/qgraphicssceneevent.h | 45 +---- src/gui/graphicsview/qgraphicsview.cpp | 130 ++++--------- src/gui/graphicsview/qgraphicsview_p.h | 3 - src/gui/kernel/qapplication.cpp | 10 +- src/gui/kernel/qapplication_p.h | 5 +- src/gui/kernel/qapplication_win.cpp | 62 +++--- src/gui/kernel/qevent.cpp | 90 +++++++-- src/gui/kernel/qevent.h | 35 ++-- src/gui/kernel/qevent_p.h | 4 +- 13 files changed, 356 insertions(+), 500 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index f6daa49..1d3a316 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -331,6 +331,7 @@ public: quint32 inSetPosHelper : 1; quint32 allChildrenCombineOpacity : 1; quint32 acceptTouchEvents : 1; + quint32 acceptedTouchBeginEvent : 1; // Optional stacking order int globalStackingOrder; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 7bac21c..8140fea 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3798,9 +3798,6 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: - case QEvent::GraphicsSceneTouchBegin: - case QEvent::GraphicsSceneTouchUpdate: - case QEvent::GraphicsSceneTouchEnd: // Reset the under-mouse list to ensure that this event gets fresh // item-under-mouse data. Be careful about this list; if people delete // items from inside event handlers, this list can quickly end up @@ -3990,13 +3987,9 @@ bool QGraphicsScene::event(QEvent *event) } break; case QEvent::GraphicsSceneTouchBegin: - d->touchBeginEvent(static_cast(event)); - break; case QEvent::GraphicsSceneTouchUpdate: - d->touchUpdateEvent(static_cast(event)); - break; case QEvent::GraphicsSceneTouchEnd: - d->touchEndEvent(static_cast(event)); + d->touchEventHandler(static_cast(event)); break; case QEvent::Timer: if (d->indexTimerId && static_cast(event)->timerId() == d->indexTimerId) { @@ -5654,18 +5647,9 @@ void QGraphicsScenePrivate::releaseGesture(QGraphicsItem *item, int gestureId) //### } -// ### FIXME: the code for touch event support is mosly copied from -// ### QGraphicsScenePrivate::mousePressEventHandler() and friends, need to -// ### refactor to reduce code duplication - -void QGraphicsScenePrivate::sendTouchEvent(QGraphicsSceneTouchEvent *touchEvent) +void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, + QGraphicsSceneTouchEvent *touchEvent) { - if (touchEvent->type() == QEvent::GraphicsSceneTouchEnd && lastMouseGrabberItemHasImplicitMouseGrab) { - clearMouseGrabber(); - return; - } - - QGraphicsItem *item = mouseGrabberItems.last(); QList touchPoints = touchEvent->touchPoints(); for (int i = 0; i < touchPoints.count(); ++i) { QGraphicsSceneTouchEvent::TouchPoint *touchPoint = touchPoints.at(i); @@ -5673,139 +5657,181 @@ void QGraphicsScenePrivate::sendTouchEvent(QGraphicsSceneTouchEvent *touchEvent) touchPoint->setStartPos(item->d_ptr->genericMapFromScene(touchPoint->startScenePos(), touchEvent->widget())); touchPoint->setLastPos(item->d_ptr->genericMapFromScene(touchPoint->lastScenePos(), touchEvent->widget())); } - sendEvent(item, touchEvent); } -void QGraphicsScenePrivate::touchBeginEvent(QGraphicsSceneTouchEvent *touchEvent) +QGraphicsSceneTouchEvent::TouchPoint *QGraphicsScenePrivate::findClosestTouchPoint(const QList &sceneActiveTouchPoints, + const QPointF &scenePos) { - Q_Q(QGraphicsScene); + QGraphicsSceneTouchEvent::TouchPoint *closestTouchPoint = 0; + qreal closestDistance; + for (int i = 0; i < sceneActiveTouchPoints.count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = sceneActiveTouchPoints.at(i); + qreal distance = QLineF(scenePos, touchPoint->scenePos()).length(); + if (!closestTouchPoint || distance < closestDistance) { + closestTouchPoint = touchPoint; + closestDistance = distance; + } + } + return closestTouchPoint; +} - // Ignore by default, unless we find a mouse grabber that accepts it. - touchEvent->ignore(); +QEvent::Type QGraphicsScenePrivate::appendTouchPoint(QGraphicsSceneTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints) +{ + QEvent::Type eventType = currentTouchPoints->isEmpty() + ? QEvent::GraphicsSceneTouchBegin + : QEvent::GraphicsSceneTouchUpdate; - // Deliver to any existing mouse grabber. - if (!mouseGrabberItems.isEmpty()) { - // The event is ignored by default, but we disregard the event's - // accepted state after delivery; the mouse is grabbed, after all. - sendTouchEvent(touchEvent); - return; + // insort touch point (for the app) + int at = 0; + for (; at < sceneCurrentTouchPoints.count(); ++at) { + if (sceneCurrentTouchPoints.at(at)->id() > touchPoint->id()) + break; } - - // Start by determining the number of items at the current position. - // Reuse value from earlier calculations if possible. - if (cachedItemsUnderMouse.isEmpty()) { - QGraphicsSceneTouchEvent::TouchPoint *touchPoint = touchEvent->touchPoints().first(); - // ### FIXME: should the itemsAtPosition() function support sub-pixel screenPos? - cachedItemsUnderMouse = itemsAtPosition(touchPoint->screenPos().toPoint(), - touchPoint->scenePos(), - touchEvent->widget()); + sceneCurrentTouchPoints.insert(at, touchPoint); + // again, for the items's currentTouchPoints + for (at = 0; at < currentTouchPoints->count(); ++at) { + if (currentTouchPoints->at(at)->id() > touchPoint->id()) + break; } + currentTouchPoints->insert(at, touchPoint); - // Update window activation. - QGraphicsWidget *newActiveWindow = windowForItem(cachedItemsUnderMouse.value(0)); - if (newActiveWindow != activeWindow) - q->setActiveWindow(newActiveWindow); + return eventType; +} - // Set focus on the topmost enabled item that can take focus. - bool setFocus = false; - foreach (QGraphicsItem *item, cachedItemsUnderMouse) { - if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { - if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { - setFocus = true; - if (item != q->focusItem()) - q->setFocusItem(item, Qt::MouseFocusReason); - break; - } +QEvent::Type QGraphicsScenePrivate::removeTouchPoint(QGraphicsSceneTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints) +{ + // remove touch point from all known touch points + for (int i = qMin(sceneCurrentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { + if (sceneCurrentTouchPoints.at(i) == touchPoint) { + sceneCurrentTouchPoints.removeAt(i); + break; } } - - // If nobody could take focus, clear it. - if (!stickyFocus && !setFocus) - q->setFocusItem(0, Qt::MouseFocusReason); - - // Find a mouse grabber by sending touch events to all mouse grabber - // candidates one at a time, until the event is accepted. It's accepted by - // default, so the receiver has to explicitly ignore it for it to pass - // through. - foreach (QGraphicsItem *item, cachedItemsUnderMouse) { - if (!item->acceptTouchEvents()) { - // Skip items that don't accept the touch events - continue; + // again, for the items's currentTouchPoints + for (int i = qMin(currentTouchPoints->count() - 1, touchPoint->id()); i >= 0; --i) { + if (currentTouchPoints->at(i) == touchPoint) { + currentTouchPoints->removeAt(i); + break; } + } - grabMouse(item, /* implicit = */ true); - touchEvent->accept(); + return currentTouchPoints->isEmpty() ? QEvent::GraphicsSceneTouchEnd : QEvent::GraphicsSceneTouchUpdate; +} - // check if the item we are sending to are disabled (before we send the event) - bool disabled = !item->isEnabled(); - bool isWindow = item->isWindow(); - sendTouchEvent(touchEvent); +void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTouchEvent) +{ + Q_Q(QGraphicsScene); - bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.last() != item; - if (disabled) { - ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); - break; - } - if (touchEvent->isAccepted()) { - lastMouseGrabberItem = item; - return; - } - ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); + QList sceneActiveTouchPoints = sceneCurrentTouchPoints; + + QHash itemsNeedingEvents; + for (int i = 0; i < sceneTouchEvent->touchPoints().count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = sceneTouchEvent->touchPoints().at(i); + + // update state + QGraphicsItem *item = 0; + QEvent::Type eventType = QEvent::None; + QList activeTouchPoints; + if (touchPoint->state() == Qt::TouchPointPressed) { + // determine which widget this event will go to + item = q->itemAt(touchPoint->scenePos()); + QGraphicsSceneTouchEvent::TouchPoint *closestTouchPoint = findClosestTouchPoint(sceneActiveTouchPoints, touchPoint->scenePos()); + if (closestTouchPoint) { + QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPoint->id()); + if (!item + || (closestItem + && (item->isAncestorOf(closestItem) + || closestItem->isAncestorOf(item)))) { + item = closestItem; + } + } + if (!item) + continue; - // Don't propagate through windows. - if (isWindow) - break; - } + itemForTouchPointId.insert(touchPoint->id(), item); - // Is the event still ignored? Then the mouse press goes to the scene. - // Reset the mouse grabber, clear the selection, clear focus, and leave - // the event ignored so that it can propagate through the originating - // view. - if (!touchEvent->isAccepted()) { - clearMouseGrabber(); + QList ¤tTouchPoints = itemCurrentTouchPoints[item]; + eventType = appendTouchPoint(touchPoint, ¤tTouchPoints); + // make sure new points are added to activeTouchPoints as well + sceneActiveTouchPoints = sceneCurrentTouchPoints; + activeTouchPoints = currentTouchPoints; + } else if (touchPoint->state() == Qt::TouchPointReleased) { + item = itemForTouchPointId.take(touchPoint->id()); + if (!item) + continue; - QGraphicsView *view = touchEvent->widget() ? qobject_cast(touchEvent->widget()->parentWidget()) : 0; - bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag; - if (!dontClearSelection) { - // Clear the selection if the originating view isn't in scroll - // hand drag mode. The view will clear the selection if no drag - // happened. - q->clearSelection(); + QList ¤tTouchPoints = itemCurrentTouchPoints[item]; + sceneActiveTouchPoints = sceneCurrentTouchPoints; + activeTouchPoints = currentTouchPoints; + eventType = removeTouchPoint(touchPoint, ¤tTouchPoints); + } else { + item = itemForTouchPointId.value(touchPoint->id()); + if (!item) + continue; + + sceneActiveTouchPoints = sceneCurrentTouchPoints; + activeTouchPoints = itemCurrentTouchPoints.value(item); + eventType = QEvent::GraphicsSceneTouchUpdate; } - } -} + Q_ASSERT(eventType != QEvent::None); -void QGraphicsScenePrivate::touchUpdateEvent(QGraphicsSceneTouchEvent *touchEvent) -{ - if (mouseGrabberItems.isEmpty()) { - touchEvent->ignore(); - return; + if (touchPoint->state() != Qt::TouchPointStationary) { + QGraphicsSceneTouchEvent *&touchEvent = itemsNeedingEvents[item]; + if (!touchEvent) { + touchEvent = new QGraphicsSceneTouchEvent(eventType); + touchEvent->setModifiers(sceneTouchEvent->modifiers()); + } + Q_ASSERT(touchEvent->type() == eventType); + touchEvent->setTouchPoints(activeTouchPoints); + } } - // Forward the event to the mouse grabber - sendTouchEvent(touchEvent); - touchEvent->accept(); -} - -void QGraphicsScenePrivate::touchEndEvent(QGraphicsSceneTouchEvent *touchEvent) -{ - if (mouseGrabberItems.isEmpty()) { - touchEvent->ignore(); + if (itemsNeedingEvents.isEmpty()) { + sceneTouchEvent->ignore(); return; } - // Forward the event to the mouse grabber - sendTouchEvent(touchEvent); - touchEvent->accept(); + bool acceptSceneTouchEvent = false; + QHash::ConstIterator it = itemsNeedingEvents.constBegin(); + const QHash::ConstIterator end = itemsNeedingEvents.constEnd(); + for (; it != end; ++it) { + QGraphicsItem *item = it.key(); + + QGraphicsSceneTouchEvent *touchEvent = it.value(); + updateTouchPointsForItem(item, touchEvent); + + switch (touchEvent->type()) { + case QEvent::GraphicsSceneTouchBegin: + { + // if the TouchBegin handler recurses, we assume that means the event + // has been implicitly accepted and continue to send touch events + item->d_ptr->acceptedTouchBeginEvent = true; + bool res = sendEvent(item, touchEvent) + && touchEvent->isAccepted(); + acceptSceneTouchEvent = acceptSceneTouchEvent || res; + break; + } + case QEvent::GraphicsSceneTouchEnd: + { + QList currentTouchPoints = itemCurrentTouchPoints.take(item); + if (!currentTouchPoints.isEmpty()) { + qFatal("Qt: INTERNAL ERROR, the widget's currentTouchPoints should be empty!"); + } + // fall-through intended + } + default: + if (item->d_ptr->acceptedTouchBeginEvent) { + (void) sendEvent(item, touchEvent); + acceptSceneTouchEvent = true; + } + break; + } - // Reset the mouse grabber - if (!mouseGrabberItems.isEmpty()) { - lastMouseGrabberItem = mouseGrabberItems.last(); - if (lastMouseGrabberItemHasImplicitMouseGrab) - mouseGrabberItems.last()->ungrabMouse(); - } else { - lastMouseGrabberItem = 0; + delete touchEvent; } + sceneTouchEvent->setAccepted(acceptSceneTouchEvent); } QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 074e95c..589ab75 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -57,6 +57,7 @@ #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW +#include "qgraphicssceneevent.h" #include "qgraphicsscene_bsp_p.h" #include "qgraphicsitem_p.h" @@ -73,7 +74,6 @@ QT_BEGIN_NAMESPACE class QGraphicsView; class QGraphicsWidget; -class QGraphicsSceneTouchEvent; class QGraphicsScenePrivate : public QObjectPrivate { @@ -276,10 +276,17 @@ public: mutable QBitArray validTransforms; mutable QVector freeSceneTransformSlots; - void sendTouchEvent(QGraphicsSceneTouchEvent *touchEvent); - void touchBeginEvent(QGraphicsSceneTouchEvent *touchEvent); - void touchUpdateEvent(QGraphicsSceneTouchEvent *touchEvent); - void touchEndEvent(QGraphicsSceneTouchEvent *touchEvent); + QList sceneCurrentTouchPoints; + QHash > itemCurrentTouchPoints; + QHash itemForTouchPointId; + static void updateTouchPointsForItem(QGraphicsItem *item, QGraphicsSceneTouchEvent *touchEvent); + static QGraphicsSceneTouchEvent::TouchPoint *findClosestTouchPoint(const QList &activeTouchPoints, + const QPointF &scenePos); + QEvent::Type appendTouchPoint(QGraphicsSceneTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints); + QEvent::Type removeTouchPoint(QGraphicsSceneTouchEvent::TouchPoint *touchPoint, + QList *currentTouchPoints); + void touchEventHandler(QGraphicsSceneTouchEvent *touchEvent); }; QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index b599632..d5d0fb9 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -301,6 +301,7 @@ #include #include "qgraphicsview.h" #include "qgraphicsitem.h" +#include QT_BEGIN_NAMESPACE @@ -1966,10 +1967,7 @@ QGraphicsSceneTouchEvent::QGraphicsSceneTouchEvent(Type type) Destroys the QGraphicsSceneTouchEvent. */ QGraphicsSceneTouchEvent::~QGraphicsSceneTouchEvent() -{ - Q_D(QGraphicsSceneTouchEvent); - qDeleteAll(d->touchPoints); -} +{ } /*! Returns the list of touch points for this event. @@ -2006,116 +2004,6 @@ void QGraphicsSceneTouchEvent::setModifiers(Qt::KeyboardModifiers modifiers) d->modifiers = modifiers; } -class QGraphicsSceneTouchEventTouchPointPrivate -{ -public: - inline QGraphicsSceneTouchEventTouchPointPrivate() - : id(-1), state(Qt::TouchPointReleased), pressure(qreal(0.)) - { } - - int id; - Qt::TouchPointState state; - QPointF pos, startPos, lastPos; - QPointF scenePos, startScenePos, lastScenePos; - QPointF screenPos, startScreenPos, lastScreenPos; - qreal pressure; -}; - -/*! \internal - - Constructs a new touch point for use in a QGraphicsSceneTouchEvent. -*/ -QGraphicsSceneTouchEvent::TouchPoint::TouchPoint() - : d(new QGraphicsSceneTouchEventTouchPointPrivate) -{ -} - -/*! \internal - - Destroys the QGraphicsSceneTouchEvent::TouchPoint. -*/ -QGraphicsSceneTouchEvent::TouchPoint::~TouchPoint() -{ - delete d; -} - -/*! - Returns the identifier for this touch point. -*/ -int QGraphicsSceneTouchEvent::TouchPoint::id() const -{ - return d->id; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setId(int id) -{ - d->id = id; -} - -/*! - Returns the state of this touch point at the time the - QGraphicsSceneTouchEvent occurred. -*/ -Qt::TouchPointState QGraphicsSceneTouchEvent::TouchPoint::state() const -{ - return d->state; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setState(Qt::TouchPointState state) -{ - d->state = state; -} - -/*! - Returns the current position of this touch point in item coordinates. - - \sa scenePos(), screenPos() -*/ -QPointF QGraphicsSceneTouchEvent::TouchPoint::pos() const -{ - return d->pos; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setPos(const QPointF &pos) -{ - d->pos = pos; -} - -/*! - Returns the starting position of this touch point in item coordinates. - - \sa startScenePos(), startScreenPos() -*/ -QPointF QGraphicsSceneTouchEvent::TouchPoint::startPos() const -{ - return d->startPos; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setStartPos(const QPointF &startPos) -{ - d->startPos = startPos; -} - -/*! - Returns the previous position of this touch point in item coordinates. - - \sa lastScenePos(), lastScreenPos() -*/ -QPointF QGraphicsSceneTouchEvent::TouchPoint::lastPos() const -{ - return d->lastPos; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos) -{ - d->lastPos = lastPos; -} - /*! Returns the current position of this touch point in scene coordinates. @@ -2164,68 +2052,6 @@ void QGraphicsSceneTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastSc d->lastScenePos = lastScenePos; } -/*! - Returns the current position of this touch point in screen coordinates. - - \sa pos(), scenePos() -*/ -QPointF QGraphicsSceneTouchEvent::TouchPoint::screenPos() const -{ - return d->screenPos; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) -{ - d->screenPos = screenPos; -} - -/*! - Returns the starting position of this touch point in screen coordinates. - - \sa startPos(), startScenePos() -*/ -QPointF QGraphicsSceneTouchEvent::TouchPoint::startScreenPos() const -{ - return d->startScreenPos; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos) -{ - d->startScreenPos = startScreenPos; -} - -/*! - Returns the previous position of this touch point in screen coordinates. - - \sa lastPos(), lastScenePos() -*/ -QPointF QGraphicsSceneTouchEvent::TouchPoint::lastScreenPos() const -{ - return d->lastScreenPos; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos) -{ - d->lastScreenPos = lastScreenPos; -} - -/*! - Returns the pressure of this touch point. -*/ -qreal QGraphicsSceneTouchEvent::TouchPoint::pressure() const -{ - return d->pressure; -} - -/*! \internal */ -void QGraphicsSceneTouchEvent::TouchPoint::setPressure(qreal pressure) -{ - d->pressure = pressure; -} - QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index 4b7065e..01cdab9 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -42,7 +42,7 @@ #ifndef QGRAPHICSSCENEEVENT_H #define QGRAPHICSSCENEEVENT_H -#include +#include #include #include #include @@ -349,34 +349,12 @@ protected: }; class QGraphicsSceneTouchEventPrivate; -class QGraphicsSceneTouchEventTouchPointPrivate; class Q_GUI_EXPORT QGraphicsSceneTouchEvent : public QGraphicsSceneEvent { public: - QGraphicsSceneTouchEvent(Type type = None); - ~QGraphicsSceneTouchEvent(); - - class Q_GUI_EXPORT TouchPoint + class Q_GUI_EXPORT TouchPoint : public QTouchEvent::TouchPoint { public: - TouchPoint(); - ~TouchPoint(); - - int id() const; - void setId(int id); - - Qt::TouchPointState state() const; - void setState(Qt::TouchPointState state); - - QPointF pos() const; - void setPos(const QPointF &pos); - - QPointF startPos() const; - void setStartPos(const QPointF &startPos); - - QPointF lastPos() const; - void setLastPos(const QPointF &lastPos); - QPointF scenePos() const; void setScenePos(const QPointF &scenePos); @@ -386,24 +364,15 @@ public: QPointF lastScenePos() const; void setLastScenePos(const QPointF &lastScenePos); - QPointF screenPos() const; - void setScreenPos(const QPointF &screenPos); - - QPointF startScreenPos() const; - void setStartScreenPos(const QPointF &startScreenPos); - - QPointF lastScreenPos() const; - void setLastScreenPos(const QPointF &lastScreenPos); - qreal pressure() const; // 0.0 -> 1.0 void setPressure(qreal pressure); - - private: - QGraphicsSceneTouchEventTouchPointPrivate *d; }; - const QList &touchPoints() const; - void setTouchPoints(const QList &touchPoints); + QGraphicsSceneTouchEvent(Type type = None); + ~QGraphicsSceneTouchEvent(); + + const QList &touchPoints() const; + void setTouchPoints(const QList &touchPoints); Qt::KeyboardModifiers modifiers() const; void setModifiers(Qt::KeyboardModifiers modifiers); diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 6f142e3..1d801cd 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -306,6 +306,30 @@ inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for sing return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1); } +static void qt_convertTouchEventToGraphicsSceneTouchEvent(QGraphicsViewPrivate *d, + QTouchEvent *originalEvent, + QGraphicsSceneTouchEvent *touchEvent) +{ + QList originalTouchPoints = originalEvent->touchPoints(); + QList touchPoints; + for (int i = 0; i < originalTouchPoints.count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = + static_cast(originalTouchPoints.at(i)); + // the scene will set the pos before delivering to an item + touchPoint->setScenePos(d->mapToScene(touchPoint->pos())); + // the scene will set the startPos before delivering to an item + touchPoint->setStartScenePos(d->mapToScene(touchPoint->startPos())); + // the scene will set the lastPos before delivering to an item + touchPoint->setLastScenePos(d->mapToScene(touchPoint->lastScreenPos())); + // lastScreenPos is already set in the originalTouchPoint + + touchPoints.append(touchPoint); + } + + touchEvent->setTouchPoints(touchPoints); + touchEvent->setModifiers(originalEvent->modifiers()); +} + /*! \internal */ @@ -2962,20 +2986,24 @@ bool QGraphicsView::viewportEvent(QEvent *event) { if (!isEnabled()) return false; - QTouchEvent *touchEvent = static_cast(event); - switch (touchEvent->type()) { - case QEvent::TouchBegin: - d->touchBeginEvent(touchEvent); - break; - case QEvent::TouchUpdate: - d->touchUpdateEvent(touchEvent); - break; - case QEvent::TouchEnd: - d->touchEndEvent(touchEvent); - break; - default: - break; + + if (d->scene && d->sceneInteractionAllowed) { + // Convert and deliver the touch event to the scene. + QEvent::Type eventType = event->type() == QEvent::TouchBegin + ? QEvent::GraphicsSceneTouchBegin + : event->type() == QEvent::TouchEnd + ? QEvent::GraphicsSceneTouchEnd + : QEvent::GraphicsSceneTouchUpdate; + QGraphicsSceneTouchEvent touchEvent(eventType); + touchEvent.setWidget(viewport()); + qt_convertTouchEventToGraphicsSceneTouchEvent(d, + static_cast(event), + &touchEvent); + touchEvent.setAccepted(false); + QApplication::sendEvent(d->scene, &touchEvent); + event->setAccepted(touchEvent.isAccepted()); } + return true; } default: @@ -3916,34 +3944,6 @@ void QGraphicsView::resetTransform() setTransform(QTransform()); } -static void qt_convertTouchEventToGraphicsSceneTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *originalEvent, QGraphicsSceneTouchEvent *touchEvent) -{ - QList originalTouchPoints = originalEvent->touchPoints(); - QList touchPoints; - for (int i = 0; i < originalTouchPoints.count(); ++i) { - QTouchEvent::TouchPoint *originalTouchPoint = originalTouchPoints.at(i); - - QGraphicsSceneTouchEvent::TouchPoint *touchPoint = new QGraphicsSceneTouchEvent::TouchPoint(); - touchPoint->setId(originalTouchPoint->id()); - touchPoint->setState(originalTouchPoint->state()); - // the scene will set the pos before delivering to an item - touchPoint->setScenePos(d->mapToScene(originalTouchPoint->pos())); - touchPoint->setScreenPos(originalTouchPoint->globalPos()); - // the scene will set the startPos before delivering to an item - touchPoint->setStartScenePos(d->mapToScene(originalTouchPoint->startPos())); - touchPoint->setStartScreenPos(originalTouchPoint->startGlobalPos()); - // the scene will set the lastPos before delivering to an item - touchPoint->setLastScenePos(d->mapToScene(originalTouchPoint->lastPos())); - touchPoint->setLastScreenPos(originalTouchPoint->lastGlobalPos()); - touchPoint->setPressure(originalTouchPoint->pressure()); - - touchPoints.append(touchPoint); - } - - touchEvent->setTouchPoints(touchPoints); - touchEvent->setModifiers(originalEvent->modifiers()); -} - QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const { QPointF p = point; @@ -3952,54 +3952,6 @@ QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const return identityMatrix ? p : matrix.inverted().map(p); } -void QGraphicsViewPrivate::touchBeginEvent(QTouchEvent *event) -{ - Q_Q(QGraphicsView); - - if (!scene || !sceneInteractionAllowed) - return; - - // Convert and deliver the touch event to the scene. - QGraphicsSceneTouchEvent touchEvent(QEvent::GraphicsSceneTouchBegin); - touchEvent.setWidget(q->viewport()); - qt_convertTouchEventToGraphicsSceneTouchEvent(this, event, &touchEvent); - touchEvent.setAccepted(false); - QApplication::sendEvent(scene, &touchEvent); - event->setAccepted(touchEvent.isAccepted()); -} - -void QGraphicsViewPrivate::touchUpdateEvent(QTouchEvent *event) -{ - Q_Q(QGraphicsView); - - if (!scene || !sceneInteractionAllowed) - return; - - // Convert and deliver the touch event to the scene. - QGraphicsSceneTouchEvent touchEvent(QEvent::GraphicsSceneTouchUpdate); - touchEvent.setWidget(q->viewport()); - qt_convertTouchEventToGraphicsSceneTouchEvent(this, event, &touchEvent); - touchEvent.setAccepted(false); - QApplication::sendEvent(scene, &touchEvent); - event->setAccepted(touchEvent.isAccepted()); -} - -void QGraphicsViewPrivate::touchEndEvent(QTouchEvent *event) -{ - Q_Q(QGraphicsView); - - if (!scene || !sceneInteractionAllowed) - return; - - // Convert and deliver the touch event to the scene. - QGraphicsSceneTouchEvent touchEvent(QEvent::GraphicsSceneTouchEnd); - touchEvent.setWidget(q->viewport()); - qt_convertTouchEventToGraphicsSceneTouchEvent(this, event, &touchEvent); - touchEvent.setAccepted(false); - QApplication::sendEvent(scene, &touchEvent); - event->setAccepted(touchEvent.isAccepted()); -} - QT_END_NAMESPACE #include "moc_qgraphicsview.cpp" diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index daaeca2..779b638 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -183,9 +183,6 @@ public: const QRegion &exposedRegion) const; QPointF mapToScene(const QPointF &point) const; - void touchBeginEvent(QTouchEvent *event); - void touchUpdateEvent(QTouchEvent *event); - void touchEndEvent(QTouchEvent *event); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 2fb6e91..e46ccf1 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4045,7 +4045,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) // the first widget to accept the TouchBegin gets an implicit grab. for (int i = 0; i < touchEvent->_touchPoints.count(); ++i) { QTouchEvent::TouchPoint *touchPoint = touchEvent->_touchPoints.at(i); - touchPoint->d->widget = widget; + d->widgetForTouchPointId[touchPoint->d->id] = widget; } if (origin != widget) d->widgetCurrentTouchPoints.remove(origin); @@ -5245,10 +5245,10 @@ void QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEven QTouchEvent::TouchPoint *touchPoint = touchEvent->_touchPoints.at(i); // preserve the sub-pixel resolution - const QPointF delta = touchPoint->d->globalPos - touchPoint->d->globalPos.toPoint(); - touchPoint->d->pos = widget->mapFromGlobal(touchPoint->d->globalPos.toPoint()) + delta; - touchPoint->d->startPos = widget->mapFromGlobal(touchPoint->d->startGlobalPos.toPoint()) + delta; - touchPoint->d->lastPos = widget->mapFromGlobal(touchPoint->d->lastGlobalPos.toPoint()) + delta; + const QPointF delta = touchPoint->d->screenPos - touchPoint->d->screenPos.toPoint(); + touchPoint->d->pos = widget->mapFromGlobal(touchPoint->d->screenPos.toPoint()) + delta; + touchPoint->d->startPos = widget->mapFromGlobal(touchPoint->d->startScreenPos.toPoint()) + delta; + touchPoint->d->lastPos = widget->mapFromGlobal(touchPoint->d->lastScreenPos.toPoint()) + delta; } } diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index e44e7aa..096c349 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -433,6 +433,7 @@ public: // map number of widget subscribed to it> QMap grabbedGestures; + QHash widgetForTouchPointId; QMap > widgetCurrentTouchPoints; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); @@ -441,13 +442,13 @@ public: static qt_GetTouchInputInfoPtr GetTouchInputInfo; static qt_CloseTouchInputHandlePtr CloseTouchInputHandle; - QMap touchInputIDToTouchPointID; + QHash touchInputIDToTouchPointID; QVector appAllTouchPoints; QList appCurrentTouchPoints; void initializeMultitouch(); static QTouchEvent::TouchPoint *findClosestTouchPoint(const QList &activeTouchPoints, - const QPointF &pos); + const QPointF &screenPos); QEvent::Type appendTouchPoint(QTouchEvent::TouchPoint *touchPoint, QList *currentTouchPoints); QEvent::Type removeTouchPoint(QTouchEvent::TouchPoint *touchPoint, diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index ef49ac6..f537805 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -4008,6 +4008,7 @@ void QApplicationPrivate::initializeMultitouch() GetTouchInputInfo = static_cast(library.resolve("GetTouchInputInfo")); CloseTouchInputHandle = static_cast(library.resolve("CloseTouchInputHandle")); + widgetForTouchPointId.clear(); widgetCurrentTouchPoints.clear(); touchInputIDToTouchPointID.clear(); appAllTouchPoints.clear(); @@ -4015,13 +4016,13 @@ void QApplicationPrivate::initializeMultitouch() } QTouchEvent::TouchPoint *QApplicationPrivate::findClosestTouchPoint(const QList &appActiveTouchPoints, - const QPointF &pos) + const QPointF &screenPos) { QTouchEvent::TouchPoint *closestTouchPoint = 0; qreal closestDistance; for (int i = 0; i < appActiveTouchPoints.count(); ++i) { QTouchEvent::TouchPoint *touchPoint = appActiveTouchPoints.at(i); - qreal distance = QLineF(pos, touchPoint->d->globalPos).length(); + qreal distance = QLineF(screenPos, touchPoint->d->screenPos).length(); if (!closestTouchPoint || distance < closestDistance) { closestTouchPoint = touchPoint; closestDistance = distance; @@ -4112,64 +4113,67 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg) touchPoint = appAllTouchPoints[touchPointID] = new QTouchEvent::TouchPoint(touchPointID); // update state + QWidget *widget = 0; bool down = touchPoint->d->state != Qt::TouchPointReleased; - QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); + QPointF screenPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); QEvent::Type eventType = QEvent::None; QList activeTouchPoints; if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) { // determine which widget this event will go to - QWidget *w = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(globalPos.toPoint())); - if (!w) - w = widgetForHwnd; - - QTouchEvent::TouchPoint *closestTouchPoint = findClosestTouchPoint(appActiveTouchPoints, globalPos); - if (closestTouchPoint - && (w->isAncestorOf(closestTouchPoint->d->widget) - || closestTouchPoint->d->widget->isAncestorOf(w))) { - w = closestTouchPoint->d->widget; + widget = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(screenPos.toPoint())); + if (!widget) + widget = widgetForHwnd; + + QTouchEvent::TouchPoint *closestTouchPoint = findClosestTouchPoint(appActiveTouchPoints, screenPos); + if (closestTouchPoint) { + QWidget *closestWidget = widgetForTouchPointId.value(closestTouchPoint->d->id); + if (closestWidget + && (widget->isAncestorOf(closestWidget) + || closestWidget->isAncestorOf(widget))) + widget = closestWidget; } - touchPoint->d->widget = w; + widgetForTouchPointId[touchPoint->d->id] = widget; - QList ¤tTouchPoints = widgetCurrentTouchPoints[w]; + QList ¤tTouchPoints = widgetCurrentTouchPoints[widget]; eventType = appendTouchPoint(touchPoint, ¤tTouchPoints); // make sure new points are added to activeTouchPoints as well appActiveTouchPoints = appCurrentTouchPoints; activeTouchPoints = currentTouchPoints; touchPoint->d->state = Qt::TouchPointPressed; - touchPoint->d->globalPos - = touchPoint->d->startGlobalPos - = touchPoint->d->lastGlobalPos - = globalPos; + touchPoint->d->screenPos + = touchPoint->d->startScreenPos + = touchPoint->d->lastScreenPos + = screenPos; touchPoint->d->pressure = qreal(1.); } else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) { - QWidget *w = touchPoint->d->widget; - QList ¤tTouchPoints = widgetCurrentTouchPoints[w]; + widget = widgetForTouchPointId.take(touchPoint->d->id); + QList ¤tTouchPoints = widgetCurrentTouchPoints[widget]; appActiveTouchPoints = appCurrentTouchPoints; activeTouchPoints = currentTouchPoints; eventType = removeTouchPoint(touchPoint, ¤tTouchPoints); touchPoint->d->state = Qt::TouchPointReleased; - touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; - touchPoint->d->globalPos = globalPos; + touchPoint->d->lastScreenPos = touchPoint->d->screenPos; + touchPoint->d->screenPos = screenPos; touchPoint->d->pressure = qreal(0.); } else if (down) { - QWidget *w = touchPoint->d->widget; + widget = widgetForTouchPointId.value(touchPoint->d->id); appActiveTouchPoints = appCurrentTouchPoints; - activeTouchPoints = widgetCurrentTouchPoints.value(w); + activeTouchPoints = widgetCurrentTouchPoints.value(widget); eventType = QEvent::TouchUpdate; - touchPoint->d->state = globalPos == touchPoint->d->globalPos + touchPoint->d->state = screenPos == touchPoint->d->screenPos ? Qt::TouchPointStationary : Qt::TouchPointMoved; - touchPoint->d->lastGlobalPos = touchPoint->d->globalPos; - touchPoint->d->globalPos = globalPos; + touchPoint->d->lastScreenPos = touchPoint->d->screenPos; + touchPoint->d->screenPos = screenPos; // pressure should still be 1. } - Q_ASSERT(eventType != QEvent::None); + Q_ASSERT(widget != 0 && eventType != QEvent::None); if (touchPoint->d->state != Qt::TouchPointStationary) { - widgetsNeedingEvents.insert(touchPoint->d->widget, + widgetsNeedingEvents.insert(widget, QTouchEvent(eventType, q->keyboardModifiers(), activeTouchPoints)); } } diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 15f0511..35fe6a3 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3726,11 +3726,17 @@ void QGestureEvent::accept(const QString &type) */ QTouchEvent::QTouchEvent(QEvent::Type type, Qt::KeyboardModifiers modifiers, - const QList &touchPoints) + const QList &touchPoints) : QInputEvent(type, modifiers), _touchPoints(touchPoints) { } -/*! \fn const QList &QTouchEvent::touchPoints() const +/*! + Destroys the QTouchEvent. +*/ +QTouchEvent::~QTouchEvent() +{ } + +/*! \fn const QList &QTouchEvent::TouchPoints() const Returns the list of touch points contained in the touch event. */ @@ -3762,6 +3768,12 @@ int QTouchEvent::TouchPoint::id() const return d->id; } +/*! \internal */ +void QTouchEvent::TouchPoint::setId(int id) +{ + d->id = id; +} + /*! Returns the current state of this touch point. */ @@ -3770,56 +3782,98 @@ Qt::TouchPointState QTouchEvent::TouchPoint::state() const return d->state; } +/*! \internal */ +void QTouchEvent::TouchPoint::setState(Qt::TouchPointState state) +{ + d->state = state; +} + /*! Returns the position of this touch point, relative to the widget - that received the event. + or item that received the event. */ -const QPointF &QTouchEvent::TouchPoint::pos() const +QPointF QTouchEvent::TouchPoint::pos() const { return d->pos; } +/*! \internal */ +void QTouchEvent::TouchPoint::setPos(const QPointF &pos) +{ + d->pos = pos; +} + /*! Returns the starting position of this touch point, relative to the widget that received the event. */ -const QPointF &QTouchEvent::TouchPoint::startPos() const +QPointF QTouchEvent::TouchPoint::startPos() const { return d->startPos; } +/*! \internal */ +void QTouchEvent::TouchPoint::setStartPos(const QPointF &startPos) +{ + d->startPos = startPos; +} + /*! Returns the position of this touch point from the previous touch event, relative to the widget that received the event. */ -const QPointF &QTouchEvent::TouchPoint::lastPos() const +QPointF QTouchEvent::TouchPoint::lastPos() const { return d->lastPos; } +/*! \internal */ +void QTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos) +{ + d->lastPos = lastPos; +} + /*! - Returns the global position of this touch point. + Returns the screen position of this touch point. */ -const QPointF &QTouchEvent::TouchPoint::globalPos() const +QPointF QTouchEvent::TouchPoint::screenPos() const +{ + return d->screenPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) { - return d->globalPos; + d->screenPos = screenPos; } /*! - Returns the global starting position of this touch point. + Returns the starting screen position of this touch point. */ -const QPointF &QTouchEvent::TouchPoint::startGlobalPos() const +QPointF QTouchEvent::TouchPoint::startScreenPos() const +{ + return d->startScreenPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos) { - return d->startGlobalPos; + d->startScreenPos = startScreenPos; } /*! - Returns the global position of this touch point from the previous + Returns the screen position of this touch point from the previous touch event. */ -const QPointF &QTouchEvent::TouchPoint::lastGlobalPos() const +QPointF QTouchEvent::TouchPoint::lastScreenPos() const +{ + return d->lastScreenPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos) { - return d->lastGlobalPos; + d->lastScreenPos = lastScreenPos; } /*! @@ -3831,4 +3885,10 @@ qreal QTouchEvent::TouchPoint::pressure() const return d->pressure; } +/*! \internal */ +void QTouchEvent::TouchPoint::setPressure(qreal pressure) +{ + d->pressure = pressure; +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index ea4f577..ed4129c 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -768,20 +768,33 @@ public: ~TouchPoint(); int id() const; + void setId(int id); Qt::TouchPointState state() const; + void setState(Qt::TouchPointState state); - const QPointF &pos() const; - const QPointF &startPos() const; - const QPointF &lastPos() const; + QPointF pos() const; + void setPos(const QPointF &pos); - const QPointF &globalPos() const; - const QPointF &startGlobalPos() const; - const QPointF &lastGlobalPos() const; + QPointF startPos() const; + void setStartPos(const QPointF &startPos); + + QPointF lastPos() const; + void setLastPos(const QPointF &lastPos); + + QPointF screenPos() const; + void setScreenPos(const QPointF &screenPos); + + QPointF startScreenPos() const; + void setStartScreenPos(const QPointF &startScreenPos); + + QPointF lastScreenPos() const; + void setLastScreenPos(const QPointF &lastScreenPos); qreal pressure() const; // 0.0 -> 1.0 + void setPressure(qreal pressure); - private: + protected: QTouchEventTouchPointPrivate *d; friend class QApplication; @@ -790,13 +803,13 @@ public: QTouchEvent(QEvent::Type type, Qt::KeyboardModifiers modifiers, - const QList &touchPoints); + const QList &touchPoints); + ~QTouchEvent(); - inline const QList &touchPoints() const { return _touchPoints; - } + inline const QList &touchPoints() const { return _touchPoints; } protected: - QList _touchPoints; + QList _touchPoints; friend class QApplication; friend class QApplicationPrivate; diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 8623c66..dd78c3e 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -98,11 +98,11 @@ public: pressure(qreal(-1.)) { } - QPointer widget; int id; Qt::TouchPointState state; QPointF pos, startPos, lastPos; - QPointF globalPos, startGlobalPos, lastGlobalPos; + QPointF scenePos, startScenePos, lastScenePos; + QPointF screenPos, startScreenPos, lastScreenPos; qreal pressure; }; -- cgit v0.12 From 3134a71a6b2a1b31ca02c445adc7d9c66e082b25 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 13 May 2009 16:07:05 +0200 Subject: make the red and green children items larger --- examples/multitouch/knobs/knob.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/multitouch/knobs/knob.cpp b/examples/multitouch/knobs/knob.cpp index 4622f69..b7cc6f3 100644 --- a/examples/multitouch/knobs/knob.cpp +++ b/examples/multitouch/knobs/knob.cpp @@ -50,12 +50,12 @@ Knob::Knob() setAcceptTouchEvents(true); setBrush(Qt::lightGray); - QGraphicsEllipseItem *leftItem = new QGraphicsEllipseItem(0, 0, 10, 10, this); - leftItem->setPos(-45, -5); + QGraphicsEllipseItem *leftItem = new QGraphicsEllipseItem(0, 0, 20, 20, this); + leftItem->setPos(-40, -10); leftItem->setBrush(Qt::darkGreen); - QGraphicsEllipseItem *rightItem = new QGraphicsEllipseItem(0, 0, 10, 10, this); - rightItem->setPos(35, -5); + QGraphicsEllipseItem *rightItem = new QGraphicsEllipseItem(0, 0, 20, 20, this); + rightItem->setPos(20, -10); rightItem->setBrush(Qt::darkRed); } -- cgit v0.12 From fdd2ad0813a856a00cac83ccff9bdd210b398c9c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 13 May 2009 16:16:11 +0200 Subject: Implement QEvent::GraphicsSceneTouchBegin propagation This event will be propagated to all items under the first touch point, otherwise it's the same as QEvent::TouchBegin propagation: the first item to accept the event gets the remaining touch events. If none of the GraphicsSceneTouchEvents sent to the items are sent, then the GraphicsSceneTouchEvent that was sent to the seen will be ignored, causing the QTouchEvent sent to the QGraphicsView to also be ignored, meaning widget TouchBegin event propagation resumes. --- src/gui/graphicsview/qgraphicsscene.cpp | 74 ++++++++++++++++++++++++++++++--- src/gui/graphicsview/qgraphicsscene_p.h | 1 + 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 8140fea..3d4bb5c 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3798,6 +3798,9 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: + case QEvent::GraphicsSceneTouchBegin: + case QEvent::GraphicsSceneTouchUpdate: + case QEvent::GraphicsSceneTouchEnd: // Reset the under-mouse list to ensure that this event gets fresh // item-under-mouse data. Be careful about this list; if people delete // items from inside event handlers, this list can quickly end up @@ -5722,8 +5725,6 @@ QEvent::Type QGraphicsScenePrivate::removeTouchPoint(QGraphicsSceneTouchEvent::T void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTouchEvent) { - Q_Q(QGraphicsScene); - QList sceneActiveTouchPoints = sceneCurrentTouchPoints; QHash itemsNeedingEvents; @@ -5735,8 +5736,11 @@ void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTou QEvent::Type eventType = QEvent::None; QList activeTouchPoints; if (touchPoint->state() == Qt::TouchPointPressed) { - // determine which widget this event will go to - item = q->itemAt(touchPoint->scenePos()); + // determine which item this event will go to + cachedItemsUnderMouse = itemsAtPosition(touchPoint->screenPos().toPoint(), + touchPoint->scenePos(), + sceneTouchEvent->widget()); + item = cachedItemsUnderMouse.isEmpty() ? 0 : cachedItemsUnderMouse.first(); QGraphicsSceneTouchEvent::TouchPoint *closestTouchPoint = findClosestTouchPoint(sceneActiveTouchPoints, touchPoint->scenePos()); if (closestTouchPoint) { QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPoint->id()); @@ -5781,6 +5785,7 @@ void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTou QGraphicsSceneTouchEvent *&touchEvent = itemsNeedingEvents[item]; if (!touchEvent) { touchEvent = new QGraphicsSceneTouchEvent(eventType); + touchEvent->setWidget(sceneTouchEvent->widget()); touchEvent->setModifiers(sceneTouchEvent->modifiers()); } Q_ASSERT(touchEvent->type() == eventType); @@ -5800,7 +5805,6 @@ void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTou QGraphicsItem *item = it.key(); QGraphicsSceneTouchEvent *touchEvent = it.value(); - updateTouchPointsForItem(item, touchEvent); switch (touchEvent->type()) { case QEvent::GraphicsSceneTouchBegin: @@ -5808,7 +5812,7 @@ void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTou // if the TouchBegin handler recurses, we assume that means the event // has been implicitly accepted and continue to send touch events item->d_ptr->acceptedTouchBeginEvent = true; - bool res = sendEvent(item, touchEvent) + bool res = sendTouchBeginEvent(item, touchEvent) && touchEvent->isAccepted(); acceptSceneTouchEvent = acceptSceneTouchEvent || res; break; @@ -5823,6 +5827,7 @@ void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTou } default: if (item->d_ptr->acceptedTouchBeginEvent) { + updateTouchPointsForItem(item, touchEvent); (void) sendEvent(item, touchEvent); acceptSceneTouchEvent = true; } @@ -5834,6 +5839,63 @@ void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTou sceneTouchEvent->setAccepted(acceptSceneTouchEvent); } +bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QGraphicsSceneTouchEvent *touchEvent) +{ + Q_Q(QGraphicsScene); + + if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.first() != origin) { + QGraphicsSceneTouchEvent::TouchPoint *firstTouchPoint = touchEvent->touchPoints().first(); + cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint->screenPos().toPoint(), + firstTouchPoint->scenePos(), + touchEvent->widget()); + } + Q_ASSERT(cachedItemsUnderMouse.first() == origin); + + // Set focus on the topmost enabled item that can take focus. + bool setFocus = false; + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { + setFocus = true; + if (item != q->focusItem()) + q->setFocusItem(item, Qt::MouseFocusReason); + break; + } + } + } + + // If nobody could take focus, clear it. + if (!stickyFocus && !setFocus) + q->setFocusItem(0, Qt::MouseFocusReason); + + bool res = false; + bool eventAccepted = touchEvent->isAccepted(); + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + // first, try to deliver the touch event + updateTouchPointsForItem(item, touchEvent); + touchEvent->ignore(); + res = item->acceptTouchEvents() + && sendEvent(item, touchEvent); + eventAccepted = touchEvent->isAccepted(); + item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted); + touchEvent->spont = false; + if (res && eventAccepted) { + // the first item to accept the TouchBegin gets an implicit grab. + for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = touchEvent->touchPoints().at(i); + itemForTouchPointId[touchPoint->id()] = item; + } + if (origin != item) + itemCurrentTouchPoints.remove(origin); + itemCurrentTouchPoints[item] = touchEvent->touchPoints(); + break; + } + } + + touchEvent->setAccepted(eventAccepted); + return res; +} + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 589ab75..7928a45 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -287,6 +287,7 @@ public: QEvent::Type removeTouchPoint(QGraphicsSceneTouchEvent::TouchPoint *touchPoint, QList *currentTouchPoints); void touchEventHandler(QGraphicsSceneTouchEvent *touchEvent); + bool sendTouchBeginEvent(QGraphicsItem *item, QGraphicsSceneTouchEvent *touchEvent); }; QT_END_NAMESPACE -- cgit v0.12 From ce851964f45266cafd51f2c1ac4e9a7d89ce0ef0 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 12 May 2009 11:39:16 +0200 Subject: Replaced evil foreach with a for loop. --- src/gui/kernel/qgesturemanager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index c7341f2..1dda34c 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -248,11 +248,15 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) activeGestures -= newMaybeGestures; activeGestures -= cancelledGestures; activeGestures += startedGestures; - foreach(QGestureRecognizer *r, startedGestures+finishedGestures+notGestures) { - QMap::iterator it = maybeGestures.find(r); - if (it != maybeGestures.end()) { + for(QMap::iterator it = maybeGestures.begin(); + it != maybeGestures.end();) { + QGestureRecognizer *r = it.key(); + if (startedGestures.contains(r) || finishedGestures.contains(r) || + notGestures.contains(r)) { killTimer(it.value()); - maybeGestures.erase(it); + it = maybeGestures.erase(it); + } else { + ++it; } } foreach(QGestureRecognizer *r, newMaybeGestures) { -- cgit v0.12 From 20b05e38c4f364e4d21dab79a4f66103e6fb2dc3 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 12 May 2009 14:09:59 +0200 Subject: Mentioned in the doc that the QGesture pointer that is returned by the QGestureRecognizer *must* be the same object for a gesture sequence (i.e. GestureStarted, GestureUpdated and GestureFinished should use the same object) --- src/gui/kernel/qgesturerecognizer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp index 41160cd..879f557 100644 --- a/src/gui/kernel/qgesturerecognizer.cpp +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -108,6 +108,9 @@ QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); to QGestureRecognizer::GestureStarted or QGestureRecognizer::GestureFinished. + The returned QGesture object must point to the same object in a + single gesture sequence. + The gesture object is owned by the recognizer itself. */ -- cgit v0.12 From e599814feb5bc62c4a419d332bcca39bbea15190 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 12 May 2009 16:38:08 +0200 Subject: Small improvement in gesturemanager and a fix for potential crash --- src/gui/kernel/qgesturemanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 1dda34c..2750df4 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -519,7 +519,7 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet it != e; ++it) { QGesture *g = *it; QGesturePrivate *gd = g->d_func(); - if (!gd->widget && (g->state() == Qt::GestureStarted || g->state() == Qt::GestureFinished)) { + if (!gd->widget && (g->state() == Qt::GestureStarted || gd->singleshot)) { startedGestures.insert(g); // find the target widget QWidget *w = receiver; @@ -575,7 +575,7 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet QWidget *w = gd->widget; gd->widget = 0; - if (!w->isWindow()) { + if (w && !w->isWindow()) { g->translate(w->pos()); w = w->parentWidget(); QPoint offset; -- cgit v0.12 From 51bf6930372f3ff88c0b9a84a1bb55c458172385 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 13 May 2009 10:49:59 +0200 Subject: Fixed mouse event propagation to widgets inside graphicsview - make sure spontaneous flag is preserved. --- src/gui/kernel/qapplication.cpp | 9 +++++++-- src/gui/kernel/qapplication_p.h | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index e46ccf1..f469c80 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -2876,7 +2876,8 @@ QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPoint */ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, QWidget *nativeWidget, - QWidget **buttonDown, QPointer &lastMouseReceiver) + QWidget **buttonDown, QPointer &lastMouseReceiver, + bool spontaneous) { Q_ASSERT(receiver); Q_ASSERT(event); @@ -2929,7 +2930,11 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, // We need this quard in case someone opens a modal dialog / popup. If that's the case // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver. const bool wasLeaveAfterRelease = leaveAfterRelease != 0; - bool result = QApplication::sendSpontaneousEvent(receiver, event); + bool result; + if (spontaneous) + result = QApplication::sendSpontaneousEvent(receiver, event); + else + result = QApplication::sendEvent(receiver, event); if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) { diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 096c349..08020fb 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -424,7 +424,8 @@ public: QEvent::Type type, Qt::MouseButtons buttons, QWidget *buttonDown, QWidget *alienWidget); static bool sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, - QWidget *native, QWidget **buttonDown, QPointer &lastMouseReceiver); + QWidget *native, QWidget **buttonDown, QPointer &lastMouseReceiver, + bool spontaneous = true); #if defined(Q_WS_WIN) || defined(Q_WS_X11) void sendSyntheticEnterLeave(QWidget *widget); #endif -- cgit v0.12 From 8f1724f5b3b09f57e260518f644ebc002b065443 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 14 May 2009 11:06:27 +0200 Subject: Don't export private class QGestureManager --- src/gui/kernel/qgesturemanager_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 963edf9..18183e2 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class QWidget; -class Q_GUI_EXPORT QGestureManager : public QObject +class Q_AUTOTEST_EXPORT QGestureManager : public QObject { Q_OBJECT public: -- cgit v0.12 From ac8e33994455200c369d8aa9d1853b618c225058 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Thu, 14 May 2009 10:55:04 +0200 Subject: implemented gestures support in graphicsview for QGraphicsItems and QGraphicsProxyWidgets. This is still work-in-progress. --- src/gui/graphicsview/qgraphicsitem.cpp | 44 ++++++++-- src/gui/graphicsview/qgraphicsitem_p.h | 4 + src/gui/graphicsview/qgraphicsproxywidget.cpp | 16 +++- src/gui/graphicsview/qgraphicsscene.cpp | 120 +++++++++++++++++++++----- src/gui/graphicsview/qgraphicsscene_p.h | 5 +- src/gui/graphicsview/qgraphicssceneevent.cpp | 9 ++ src/gui/graphicsview/qgraphicssceneevent.h | 1 + src/gui/graphicsview/qgraphicsview.cpp | 1 + src/gui/kernel/qgesture.h | 2 + src/gui/kernel/qgesture_p.h | 4 +- src/gui/kernel/qgesturemanager.cpp | 38 ++++---- src/gui/kernel/qgesturemanager_p.h | 7 +- src/gui/kernel/qwidget.cpp | 22 ++++- src/gui/kernel/qwidget_p.h | 1 + 14 files changed, 216 insertions(+), 58 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index e2b8115..d9f450a 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -5802,6 +5802,7 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const */ int QGraphicsItem::grabGesture(Qt::GestureType gesture) { + /// TODO: if we are QGraphicsProxyWidget we should subscribe the widget to gesture as well. return grabGesture(qt_getStandardGestureTypeName(gesture)); } @@ -5817,12 +5818,30 @@ int QGraphicsItem::grabGesture(Qt::GestureType gesture) int QGraphicsItem::grabGesture(const QString &gesture) { int id = QGestureManager::instance()->makeGestureId(gesture); - d_ptr->gestures << id; - if (d_ptr->scene) - d_ptr->scene->d_func()->grabGesture(this, id); + d_ptr->grabGesture(id); return id; } +void QGraphicsItemPrivate::grabGesture(int id) +{ + Q_Q(QGraphicsItem); + gestures << id; + if (scene) + scene->d_func()->grabGesture(q, id); +} + +bool QGraphicsItemPrivate::releaseGesture(int id) +{ + Q_Q(QGraphicsItem); + if (gestures.contains(id)) { + if (scene) + scene->d_func()->releaseGesture(q, id); + gestures.remove(id); + return true; + } + return false; +} + /*! \since 4.6 @@ -5833,10 +5852,9 @@ int QGraphicsItem::grabGesture(const QString &gesture) */ void QGraphicsItem::releaseGesture(int gestureId) { - if (d_ptr->scene) - d_ptr->scene->d_func()->releaseGesture(this, gestureId); - QGestureManager::instance()->releaseGestureId(gestureId); - d_ptr->gestures.remove(gestureId); + /// TODO: if we are QGraphicsProxyWidget we should unsubscribe the widget from gesture as well. + if (d_ptr->releaseGesture(gestureId)) + QGestureManager::instance()->releaseGestureId(gestureId); } /*! @@ -5854,6 +5872,18 @@ void QGraphicsItem::setGestureEnabled(int gestureId, bool enable) //### } +bool QGraphicsItemPrivate::hasGesture(const QString &name) const +{ + QGestureManager *gm = QGestureManager::instance(); + QSet::const_iterator it = gestures.begin(), + e = gestures.end(); + for (; it != e; ++it) { + if (gm->gestureNameFromId(*it) == name) + return true; + } + return false; +} + /*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 1d3a316..e9e36fa 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -300,6 +300,10 @@ public: int depth; QSet gestures; + bool hasGesture(const QString &gesture) const; + void grabGesture(int id); + bool releaseGesture(int id); + // Packed 32 bytes quint32 acceptedMouseButtons : 5; quint32 visible : 1; diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 01b7593..2d7490c 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -48,6 +48,7 @@ #include "private/qgraphicsproxywidget_p.h" #include "private/qwidget_p.h" #include "private/qapplication_p.h" +#include "private/qgesturemanager_p.h" #include #include @@ -275,7 +276,7 @@ void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent QWidget *embeddedMouseGrabberPtr = (QWidget *)embeddedMouseGrabber; QApplicationPrivate::sendMouseEvent(receiver, mouseEvent, alienWidget, widget, - &embeddedMouseGrabberPtr, lastWidgetUnderMouse); + &embeddedMouseGrabberPtr, lastWidgetUnderMouse, event->spontaneous()); embeddedMouseGrabber = embeddedMouseGrabberPtr; // Handle enter/leave events when last button is released from mouse @@ -648,6 +649,9 @@ void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool auto q->setAttribute(Qt::WA_OpaquePaintEvent); widget = newWidget; + foreach(int gestureId, widget->d_func()->gestures) { + grabGesture(gestureId); + } // Changes only go from the widget to the proxy. enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; @@ -871,6 +875,16 @@ bool QGraphicsProxyWidget::event(QEvent *event) } break; } + case QEvent::GraphicsSceneGesture: { + qDebug() << "QGraphicsProxyWidget: graphicsscenegesture"; + if (d->widget && d->widget->isVisible()) { + QGraphicsSceneGestureEvent *ge = static_cast(event); + //### TODO: widget->childAt(): decompose gesture event and find widget under hotspots. + QGestureManager::instance()->sendGestureEvent(d->widget, ge->gestures().toSet(), ge->cancelledGestures()); + return true; + } + break; + } default: break; } diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 3d4bb5c..9fdb46f 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -248,6 +248,7 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; #include #endif #include +#include QT_BEGIN_NAMESPACE @@ -3953,40 +3954,44 @@ bool QGraphicsScene::event(QEvent *event) update(); break; case QEvent::GraphicsSceneGesture: { + qDebug() << "GraphicsSceneGesture"; QGraphicsSceneGestureEvent *ev = static_cast(event); - QList gestureTypes = ev->gestureTypes(); QGraphicsView *view = qobject_cast(ev->widget()); if (!view) { qWarning("QGraphicsScene::event: gesture event was received without a view"); break; } - // find graphics items that intersects with gestures hot spots. - QPolygonF poly; - QMap sceneHotSpots; - foreach(const QString &type, gestureTypes) { - QPointF pt = ev->mapToScene(ev->gesture(type)->hotSpot()); - sceneHotSpots.insert(type, pt); - poly << pt; + // get a list of gestures that just started. + QSet startedGestures; + QList gestures = ev->gestures(); + for(QList::const_iterator it = gestures.begin(), e = gestures.end(); + it != e; ++it) { + QGesture *g = *it; + QGesturePrivate *gd = g->d_func(); + if (g->state() == Qt::GestureStarted || gd->singleshot) { + startedGestures.insert(g); + } } - QList itemsInGestureArea = items(poly, Qt::IntersectsItemBoundingRect); - - foreach(QGraphicsItem *item, itemsInGestureArea) { - QMap::const_iterator it = sceneHotSpots.begin(), - e = sceneHotSpots.end(); - for(; it != e; ++it) { - if (item->contains(item->mapFromScene(it.value()))) { - const QString gestureName = it.key(); - foreach(int gestureId, item->d_ptr->gestures) { - if (QGestureManager::instance()->gestureNameFromId(gestureId) == gestureName) { - d->sendEvent(item, ev); - if (ev->isAccepted()) - break; - } + if (!startedGestures.isEmpty()) { + // find a target for each started gesture. + for(QSet::const_iterator it = startedGestures.begin(), e = startedGestures.end(); + it != e; ++it) { + QGesture *g = *it; + QGesturePrivate *gd = g->d_func(); + QList itemsInGestureArea = items(g->hotSpot()); + const QString gestureName = g->type(); + foreach(QGraphicsItem *item, itemsInGestureArea) { + if (item->d_func()->hasGesture(gestureName)) { + Q_ASSERT(gd->graphicsItem == 0); + gd->graphicsItem = item; + d->itemsWithGestures[item].insert(g); + break; } } } } + d->sendGestureEvent(ev->gestures().toSet(), ev->cancelledGestures()); } break; case QEvent::GraphicsSceneTouchBegin: @@ -4010,6 +4015,74 @@ bool QGraphicsScene::event(QEvent *event) return true; } +void QGraphicsScenePrivate::sendGestureEvent(const QSet &gestures, const QSet &cancelled) +{ + qDebug() << "QGraphicsScenePrivate::sendGestureEvent:" << gestures; + Q_Q(QGraphicsScene); + typedef QMap > ItemGesturesMap; + ItemGesturesMap itemGestures; + QSet startedGestures; + for(QSet::const_iterator it = gestures.begin(), e = gestures.end(); + it != e; ++it) { + QGesture *g = *it; + Q_ASSERT(g != 0); + QGesturePrivate *gd = g->d_func(); + if (gd->graphicsItem != 0) { + itemGestures[gd->graphicsItem].insert(g); + if (g->state() == Qt::GestureStarted || gd->singleshot) + startedGestures.insert(g); + } + } + + qDebug() << "QGraphicsScenePrivate::sendGestureEvent: started: " << startedGestures; + QSet ignoredGestures; + for(ItemGesturesMap::const_iterator it = itemGestures.begin(), e = itemGestures.end(); + it != e; ++it) { + QGraphicsItem *receiver = it.key(); + Q_ASSERT(receiver != 0); + QGraphicsSceneGestureEvent event; + event.setGestures(it.value()); + event.setCancelledGestures(cancelled); + qDebug() << "QGraphicsScenePrivate::sendGestureEvent: sending to " << receiver << it.value(); + bool processed = sendEvent(receiver, &event); + QSet started = startedGestures.intersect(it.value()); + if (event.isAccepted()) + foreach(QGesture *g, started) + g->accept(); + if (!started.isEmpty() && !(processed && event.isAccepted())) { + // there are started gestures event that weren't + // accepted, so propagating each gesture independently. + QSet::const_iterator it = started.begin(), + e = started.end(); + for(; it != e; ++it) { + QGesture *g = *it; + qDebug() << "result: " << g << ":" << processed << g->isAccepted(); + if (processed && g->isAccepted()) { + continue; + } + QGesturePrivate *gd = g->d_func(); + QGraphicsItem *item = gd->graphicsItem; + gd->graphicsItem = 0; + + //### THIS IS BS, DONT FORGET TO REWRITE THIS CODE + // need to make sure we try to deliver event just once to each widget + const QString gestureType = g->type(); + QList itemsUnderGesture = q->items(g->hotSpot()); + for (int i = 0; i < itemsUnderGesture.size(); ++i) { + QGraphicsItem *item = itemsUnderGesture.at(i); + if (item != receiver && item->d_func()->hasGesture(gestureType)) { + ignoredGestures.insert(g); + gd->graphicsItem = item; + break; + } + } + } + } + } + if (!ignoredGestures.isEmpty()) + sendGestureEvent(ignoredGestures, cancelled); +} + /*! \reimp @@ -5641,12 +5714,13 @@ void QGraphicsScenePrivate::grabGesture(QGraphicsItem *item, int gestureId) foreach(QGraphicsView *view, views) view->d_func()->grabGesture(gestureId); } - itemsWithGestures << item; + (void)itemsWithGestures[item]; grabbedGestures << gestureId; } void QGraphicsScenePrivate::releaseGesture(QGraphicsItem *item, int gestureId) { + itemsWithGestures.remove(item); //### } diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 7928a45..160ba49 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -74,6 +74,7 @@ QT_BEGIN_NAMESPACE class QGraphicsView; class QGraphicsWidget; +class QGesture; class QGraphicsScenePrivate : public QObjectPrivate { @@ -267,10 +268,12 @@ public: void resolvePalette(); void updatePalette(const QPalette &palette); - QSet itemsWithGestures; + // items with gestures -> list of started gestures. + QMap > itemsWithGestures; QSet grabbedGestures; void grabGesture(QGraphicsItem *item, int gestureId); void releaseGesture(QGraphicsItem *item, int gestureId); + void sendGestureEvent(const QSet &gestures, const QSet &cancelled); mutable QVector sceneTransformCache; mutable QBitArray validTransforms; diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index d5d0fb9..2c5c946 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1895,6 +1895,15 @@ void QGraphicsSceneGestureEvent::setGestures(const QList &gestures) } /*! + Set a list of gesture objects containing extended information about \a gestures. +*/ +void QGraphicsSceneGestureEvent::setGestures(const QSet &gestures) +{ + foreach(QGesture *g, gestures) + m_gestures.insert(g->type(), g); +} + +/*! Sets the accept flag of the all gestures inside the event object, the equivalent of calling \l{QEvent::accept()}{accept()} or \l{QEvent::setAccepted()}{setAccepted(true)}. diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index 01cdab9..8b24d33 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -325,6 +325,7 @@ public: const QGesture* gesture(const QString &type) const; QList gestures() const; void setGestures(const QList &gestures); + void setGestures(const QSet &gestures); QSet cancelledGestures() const; void setCancelledGestures(const QSet &cancelledGestures); diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 2d2113c..6a20139 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -2977,6 +2977,7 @@ bool QGraphicsView::viewportEvent(QEvent *event) gestureEvent.setGestures(ev->gestures()); gestureEvent.setCancelledGestures(ev->cancelledGestures()); QApplication::sendEvent(d->scene, &gestureEvent); + event->setAccepted(gestureEvent.isAccepted()); if (gestureEvent.isAccepted()) return true; } diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index d220232..bf501f2 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -110,6 +110,8 @@ private: friend class QGestureManager; friend class QApplication; + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; friend class QGestureRecognizerPan; friend class QDoubleTapGestureRecognizer; friend class QTapAndHoldGestureRecognizer; diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index d2d77cc..0f2e791 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -60,13 +60,14 @@ QT_BEGIN_NAMESPACE +class QGraphicsItem; class QGesturePrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QGesture) public: QGesturePrivate() - : state(Qt::NoGesture), singleshot(0), duration(0) { } + : state(Qt::NoGesture), graphicsItem(0), singleshot(0), duration(0) { } void init(const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, @@ -86,6 +87,7 @@ public: Qt::GestureState state; QPointer widget; + QGraphicsItem *graphicsItem; uint singleshot:1; QRect rect; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 2750df4..23127cb 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -53,7 +53,7 @@ #include "qdebug.h" -// #define GESTURE_DEBUG +#define GESTURE_DEBUG #ifndef GESTURE_DEBUG # define DEBUG if (0) qDebug #else @@ -170,6 +170,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) foreach(QGestureRecognizer *r, finishedGestures) { if (QGesture *gesture = r->getGesture()) { gestures << gesture; + qDebug() << "SINGLESHOT" << gesture; gesture->d_func()->singleshot = true; } } @@ -277,7 +278,9 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) foreach(QGestureRecognizer *r, finishedGestures) { if (QGesture *gesture = r->getGesture()) { gestures << gesture; - gesture->d_func()->singleshot = activeGestures.contains(r); + gesture->d_func()->singleshot = !activeGestures.contains(r); + if (gesture->d_func()->singleshot) + qDebug() << "SINGLESHOT:" << gesture; } } activeGestures -= finishedGestures; @@ -492,18 +495,6 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) } } -bool QGestureManager::widgetHasGesture(QWidget *widget, QGesture *gesture) const -{ - const QString gestureName = gesture->type(); - QSet::iterator it = widget->d_func()->gestures.begin(), - e = widget->d_func()->gestures.end(); - for (; it != e; ++it) { - if (gestureNameFromId(*it) == gestureName) - return true; - } - return false; -} - bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet &gestures, const QSet &cancelled) { @@ -519,13 +510,16 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet it != e; ++it) { QGesture *g = *it; QGesturePrivate *gd = g->d_func(); - if (!gd->widget && (g->state() == Qt::GestureStarted || gd->singleshot)) { + qDebug() << "sendGestureEvent:" << g << g->state() << "singleshot = " << gd->singleshot; + if (receiver && (g->state() == Qt::GestureStarted || gd->singleshot)) { startedGestures.insert(g); // find the target widget QWidget *w = receiver; QPoint offset; + const QString gestureType = g->type(); while (w) { - if (widgetHasGesture(w, g)) + qDebug() << w << w->d_func()->gestures; + if (w->d_func()->hasGesture(gestureType)) break; if (w->isWindow()) { w = 0; @@ -536,6 +530,7 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet } if (!w) // no widget in the tree that accepts this gesture. continue; + qDebug() << "QGestureManager::sendGestureEvent:" << g << "setting widget to " << w; gd->widget = w; g->translate(offset); } @@ -556,10 +551,14 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet Q_ASSERT(receiver != 0 /*should be taken care above*/); // TODO: send cancelled gesture event to the widget that received the original gesture! QGestureEvent event(it.value(), cancelled); + qDebug() << "QGestureManager::sendGestureEvent: sending now: " << receiver << it.value(); bool processed = qt_sendSpontaneousEvent(receiver, &event); QSet started = startedGestures.intersect(it.value()); + if (event.isAccepted()) + foreach(QGesture *g, started) + g->accept(); if (!started.isEmpty() && !(processed && event.isAccepted())) { - // there are started gestures event that weren't + // there are started gestures events that weren't // accepted, so propagating each gesture independently. QSet::const_iterator it = started.begin(), e = started.end(); @@ -579,9 +578,10 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet g->translate(w->pos()); w = w->parentWidget(); QPoint offset; + const QString gestureType = g->type(); while (w) { - if (widgetHasGesture(w, g)) { - DEBUG() << "QGestureManager: sendGestureEvent:" << w << "didn't accept gesture" << g; + if (w->d_func()->hasGesture(gestureType)) { + DEBUG() << "QGestureManager::sendGestureEvent:" << w << "didn't accept gesture" << g; ignoredGestures.insert(g); break; } diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 18183e2..138def5 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -86,6 +86,9 @@ public: // declared in qapplication.cpp static QGestureManager* instance(); + bool sendGestureEvent(QWidget *receiver, const QSet &gestures, + const QSet &cancelled = QSet()); + protected: void timerEvent(QTimerEvent *event); @@ -93,10 +96,6 @@ private slots: void recognizerStateChanged(QGestureRecognizer::Result); private: - bool widgetHasGesture(QWidget *widget, QGesture *gesture) const; - bool sendGestureEvent(QWidget *receiver, const QSet &gestures, - const QSet &cancelled = QSet()); - QSet activeGestures; QMap maybeGestures; QSet recognizers; diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 3a146e5..7920139 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -10970,7 +10970,10 @@ QWindowSurface *QWidget::windowSurface() const int QWidget::grabGesture(const QString &gesture) { Q_D(QWidget); - return d->grabGesture(QGestureManager::instance()->makeGestureId(gesture)); + int id = d->grabGesture(QGestureManager::instance()->makeGestureId(gesture)); + if (d->extra && d->extra->proxyWidget) + d->extra->proxyWidget->QGraphicsItem::d_ptr->grabGesture(id); + return id; } int QWidgetPrivate::grabGesture(int gestureId) @@ -10993,6 +10996,18 @@ bool QWidgetPrivate::releaseGesture(int gestureId) return false; } +bool QWidgetPrivate::hasGesture(const QString &name) const +{ + QGestureManager *gm = QGestureManager::instance(); + QSet::const_iterator it = gestures.begin(), + e = gestures.end(); + for (; it != e; ++it) { + if (gm->gestureNameFromId(*it) == name) + return true; + } + return false; +} + /*! \since 4.6 @@ -11018,8 +11033,11 @@ int QWidget::grabGesture(Qt::GestureType gesture) void QWidget::releaseGesture(int gestureId) { Q_D(QWidget); - if (d->releaseGesture(gestureId)) + if (d->releaseGesture(gestureId)) { + if (d->extra && d->extra->proxyWidget) + d->extra->proxyWidget->QGraphicsItem::d_ptr->releaseGesture(gestureId); QGestureManager::instance()->releaseGestureId(gestureId); + } } /*! diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index a603b44..b2ae75e 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -521,6 +521,7 @@ public: QSet gestures; int grabGesture(int gestureId); bool releaseGesture(int gestureId); + bool hasGesture(const QString &type) const; // Bit fields. uint high_attributes[3]; // the low ones are in QWidget::widget_attributes -- cgit v0.12 From d62320c58a36e3a87b3018e9bc7be49b887fa2cb Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 12:24:14 +0200 Subject: More improvement to gesture propagation (still no graphicsview). --- src/gui/graphicsview/qgraphicsproxywidget.cpp | 2 +- src/gui/kernel/qgesturemanager.cpp | 109 ++++++++++++++------------ src/gui/kernel/qgesturemanager_p.h | 4 +- 3 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 2d7490c..5641068 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -880,7 +880,7 @@ bool QGraphicsProxyWidget::event(QEvent *event) if (d->widget && d->widget->isVisible()) { QGraphicsSceneGestureEvent *ge = static_cast(event); //### TODO: widget->childAt(): decompose gesture event and find widget under hotspots. - QGestureManager::instance()->sendGestureEvent(d->widget, ge->gestures().toSet(), ge->cancelledGestures()); + //QGestureManager::instance()->sendGestureEvent(d->widget, ge->gestures().toSet(), ge->cancelledGestures()); return true; } break; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 23127cb..88704ef 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -53,7 +53,7 @@ #include "qdebug.h" -#define GESTURE_DEBUG +// #define GESTURE_DEBUG #ifndef GESTURE_DEBUG # define DEBUG if (0) qDebug #else @@ -139,7 +139,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) notGestures << r; } } - activeGestures -= newMaybeGestures; + Q_ASSERT(activeGestures.isEmpty()); activeGestures += startedGestures; for(QMap::iterator it = maybeGestures.begin(); it != maybeGestures.end();) { @@ -160,28 +160,24 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) maybeGestures.insert(r, timerId); } } - if (!finishedGestures.isEmpty() || !activeGestures.isEmpty()) { + if (!finishedGestures.isEmpty() || !startedGestures.isEmpty()) { // gesture found! ret = true; - DEBUG() << "QGestureManager: sending gesture event for: " - << activeGestures << " and " << finishedGestures; - - QSet gestures; + QSet started; foreach(QGestureRecognizer *r, finishedGestures) { if (QGesture *gesture = r->getGesture()) { - gestures << gesture; - qDebug() << "SINGLESHOT" << gesture; + started << gesture; gesture->d_func()->singleshot = true; } } - foreach(QGestureRecognizer *r, activeGestures) { + foreach(QGestureRecognizer *r, startedGestures) { if (QGesture *gesture = r->getGesture()) { - gestures << gesture; + started << gesture; gesture->d_func()->singleshot = false; } } - Q_ASSERT(!gestures.isEmpty()); - ret = sendGestureEvent(receiver, gestures); + Q_ASSERT(!started.isEmpty()); + ret = sendGestureEvent(receiver, started, QSet()); if (!activeGestures.isEmpty()) { DEBUG() << "QGestureManager: new state = Gesture"; @@ -244,11 +240,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) } } } - // TODO: make sure that if gesture recognizer ignored the event we dont swallow it. - activeGestures -= newMaybeGestures; - activeGestures -= cancelledGestures; - activeGestures += startedGestures; for(QMap::iterator it = maybeGestures.begin(); it != maybeGestures.end();) { QGestureRecognizer *r = it.key(); @@ -268,35 +260,37 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) maybeGestures.insert(r, timerId); } } - QSet gestures; - if (!finishedGestures.isEmpty() || !activeGestures.isEmpty()) { + QSet started, updated; + if (!finishedGestures.isEmpty() || !startedGestures.isEmpty()) { // another gesture found! ret = true; - DEBUG() << "QGestureManager: sending gesture event for: " - << activeGestures << " and " << finishedGestures; - foreach(QGestureRecognizer *r, finishedGestures) { if (QGesture *gesture = r->getGesture()) { - gestures << gesture; gesture->d_func()->singleshot = !activeGestures.contains(r); if (gesture->d_func()->singleshot) - qDebug() << "SINGLESHOT:" << gesture; + started << gesture; + else + updated << gesture; } } - activeGestures -= finishedGestures; - foreach(QGestureRecognizer *r, activeGestures) { + foreach(QGestureRecognizer *r, startedGestures) { if (QGesture *gesture = r->getGesture()) { - gestures << gesture; - gesture->d_func()->singleshot = false; + gesture->d_func()->singleshot = !activeGestures.contains(r); + if (gesture->d_func()->singleshot) + started << gesture; + else + updated << gesture; } } } + activeGestures -= newMaybeGestures; + activeGestures -= cancelledGestures; + activeGestures += startedGestures; + activeGestures -= finishedGestures; QSet cancelledGestureNames; foreach(QGestureRecognizer *r, cancelledGestures) cancelledGestureNames << r->gestureType(); - if(!gestures.isEmpty()) { - ret = sendGestureEvent(receiver, gestures, cancelledGestureNames); - } + ret = sendGestureEvent(receiver, started, updated, cancelledGestureNames); foreach(QGestureRecognizer *r, finishedGestures) r->reset(); @@ -495,30 +489,28 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) } } -bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet &gestures, +bool QGestureManager::sendGestureEvent(QWidget *receiver, + const QSet &startedGestures, + const QSet &updatedGestures, const QSet &cancelled) { - if (gestures.isEmpty()) - return false; DEBUG() << "QGestureManager::sendGestureEvent: sending to" << receiver - << "gestures:" << gestures << "; cancelled:" << cancelled; - QSet startedGestures; + << "gestures:" << startedGestures << "," << updatedGestures + << "cancelled:" << cancelled; // grouping gesture objects by receiver widgets. typedef QMap > WidgetGesturesMap; WidgetGesturesMap widgetGestures; - for(QSet::const_iterator it = gestures.begin(), e = gestures.end(); + for(QSet::const_iterator it = startedGestures.begin(), e = startedGestures.end(); it != e; ++it) { QGesture *g = *it; QGesturePrivate *gd = g->d_func(); - qDebug() << "sendGestureEvent:" << g << g->state() << "singleshot = " << gd->singleshot; - if (receiver && (g->state() == Qt::GestureStarted || gd->singleshot)) { - startedGestures.insert(g); + if (receiver) { // find the target widget + gd->widget = 0; QWidget *w = receiver; QPoint offset; const QString gestureType = g->type(); while (w) { - qDebug() << w << w->d_func()->gestures; if (w->d_func()->hasGesture(gestureType)) break; if (w->isWindow()) { @@ -528,11 +520,11 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet offset += w->pos(); w = w->parentWidget(); } - if (!w) // no widget in the tree that accepts this gesture. - continue; - qDebug() << "QGestureManager::sendGestureEvent:" << g << "setting widget to " << w; + if (w && w != gd->widget) { + DEBUG() << "QGestureManager::sendGestureEvent:" << g << "propagating to widget" << w << "offset" << offset; + g->translate(offset); + } gd->widget = w; - g->translate(offset); } if (!gd->widget) { DEBUG() << "QGestureManager: didn't find a widget to send gesture event (" @@ -549,14 +541,21 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet it != e; ++it) { QWidget *receiver = it.key(); Q_ASSERT(receiver != 0 /*should be taken care above*/); + QSet gestures = it.value(); + // mark all gestures as ignored by default + for(QSet::iterator it = gestures.begin(), e = gestures.end(); it != e; ++it) + (*it)->ignore(); // TODO: send cancelled gesture event to the widget that received the original gesture! QGestureEvent event(it.value(), cancelled); - qDebug() << "QGestureManager::sendGestureEvent: sending now: " << receiver << it.value(); + DEBUG() << "QGestureManager::sendGestureEvent: sending now to" << receiver + << "gestures" << it.value(); bool processed = qt_sendSpontaneousEvent(receiver, &event); - QSet started = startedGestures.intersect(it.value()); - if (event.isAccepted()) + QSet started = startedGestures & it.value(); + if (event.isAccepted()) { + DEBUG() << "QGestureManager::sendGestureEvent: all gestures were accepted"; foreach(QGesture *g, started) g->accept(); + } if (!started.isEmpty() && !(processed && event.isAccepted())) { // there are started gestures events that weren't // accepted, so propagating each gesture independently. @@ -581,8 +580,10 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet const QString gestureType = g->type(); while (w) { if (w->d_func()->hasGesture(gestureType)) { - DEBUG() << "QGestureManager::sendGestureEvent:" << w << "didn't accept gesture" << g; + DEBUG() << "QGestureManager::sendGestureEvent:" << receiver + << "didn't accept gesture" << g << "propagating to" << w; ignoredGestures.insert(g); + gd->widget = w; break; } if (w->isWindow()) { @@ -592,14 +593,20 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet offset += w->pos(); w = w->parentWidget(); } - if (w) + if (w) { g->translate(offset); + } else { + DEBUG() << "QGestureManager::sendGestureEvent:" << receiver + << "didn't accept gesture" << g << "and nobody wants it"; + } } } } } + if (ignoredGestures.isEmpty()) + return ret; // try to send all gestures that were ignored to the next parent - return sendGestureEvent(0, ignoredGestures, cancelled) || ret; + return sendGestureEvent(0, ignoredGestures, QSet(), cancelled) || ret; } int QGestureManager::eventDeliveryDelay() const diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 138def5..745feb3 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -86,7 +86,9 @@ public: // declared in qapplication.cpp static QGestureManager* instance(); - bool sendGestureEvent(QWidget *receiver, const QSet &gestures, + bool sendGestureEvent(QWidget *receiver, + const QSet &startedGestures, + const QSet &updatedGestures, const QSet &cancelled = QSet()); protected: -- cgit v0.12 From 7196f70851ba9ad223efd8fd71b135ed13f22f1c Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 15:03:01 +0200 Subject: Added autotest for gestures. --- tests/auto/gestures/customgesturerecognizer.cpp | 105 +++++ tests/auto/gestures/customgesturerecognizer.h | 147 +++++++ tests/auto/gestures/gestures.pro | 3 + tests/auto/gestures/tst_gestures.cpp | 487 ++++++++++++++++++++++++ 4 files changed, 742 insertions(+) create mode 100644 tests/auto/gestures/customgesturerecognizer.cpp create mode 100644 tests/auto/gestures/customgesturerecognizer.h create mode 100644 tests/auto/gestures/gestures.pro create mode 100644 tests/auto/gestures/tst_gestures.cpp diff --git a/tests/auto/gestures/customgesturerecognizer.cpp b/tests/auto/gestures/customgesturerecognizer.cpp new file mode 100644 index 0000000..906fd32 --- /dev/null +++ b/tests/auto/gestures/customgesturerecognizer.cpp @@ -0,0 +1,105 @@ +#include "customgesturerecognizer.h" +#include "qgesture.h" + +const char* SingleshotGestureRecognizer::Name = "SingleshotGesture"; +const char* PinchGestureRecognizer::Name = "PinchGesture"; +const char* SecondFingerGestureRecognizer::Name = "SecondFingerGesture"; + +SingleshotGestureRecognizer::SingleshotGestureRecognizer(QObject *parent) + : QGestureRecognizer(QString(SingleshotGestureRecognizer::Name), parent) +{ + gesture = new SingleshotGesture(this, SingleshotGestureRecognizer::Name); +} + +QGestureRecognizer::Result SingleshotGestureRecognizer::filterEvent(const QEvent *event) +{ + if (event->type() == SingleshotEvent::Type) { + return QGestureRecognizer::GestureFinished; + } + return QGestureRecognizer::NotGesture; +} + +void SingleshotGestureRecognizer::reset() +{ + gesture->offset = QPoint(); +} + +PinchGestureRecognizer::PinchGestureRecognizer(QObject *parent) + : QGestureRecognizer(PinchGestureRecognizer::Name, parent) +{ + gesture = new PinchGesture(this, PinchGestureRecognizer::Name); +} + +QGestureRecognizer::Result PinchGestureRecognizer::filterEvent(const QEvent *event) +{ + if (event->type() != TouchEvent::Type) + return QGestureRecognizer::Ignore; + + const TouchEvent *e = static_cast(event); + if (e->points[0].state == TouchPoint::Begin) + gesture->startPoints[0] = e->points[0]; + if (e->points[1].state == TouchPoint::Begin) + gesture->startPoints[1] = e->points[1]; + gesture->lastPoints[0] = gesture->points[0]; + gesture->lastPoints[1] = gesture->points[1]; + gesture->points[0] = e->points[0]; + gesture->points[1] = e->points[1]; + if (((e->points[0].state == TouchPoint::Begin || e->points[0].state == TouchPoint::Update))) { + if (e->points[1].state == TouchPoint::End || e->points[1].state == TouchPoint::None) + return MaybeGesture; + return GestureStarted; + } else if (((e->points[1].state == TouchPoint::Begin || e->points[1].state == TouchPoint::Update))) { + if (e->points[0].state == TouchPoint::End || e->points[0].state == TouchPoint::None) + return MaybeGesture; + return GestureStarted; + } else if ((e->points[0].state == TouchPoint::End && e->points[1].state == TouchPoint::End) || + (e->points[0].state == TouchPoint::End && e->points[1].state == TouchPoint::None) || + (e->points[0].state == TouchPoint::None && e->points[1].state == TouchPoint::End)) { + return QGestureRecognizer::NotGesture; + } + return QGestureRecognizer::NotGesture; +} + +void PinchGestureRecognizer::reset() +{ + gesture->startPoints[0] = TouchPoint(); + gesture->startPoints[1] = TouchPoint(); + gesture->lastPoints[0] = TouchPoint(); + gesture->lastPoints[1] = TouchPoint(); + gesture->points[0] = TouchPoint(); + gesture->points[1] = TouchPoint(); + gesture->offset = QPoint(); +} + +SecondFingerGestureRecognizer::SecondFingerGestureRecognizer(QObject *parent) + : QGestureRecognizer(SecondFingerGestureRecognizer::Name, parent) +{ + gesture = new SecondFingerGesture(this, SecondFingerGestureRecognizer::Name); +} + +QGestureRecognizer::Result SecondFingerGestureRecognizer::filterEvent(const QEvent *event) +{ + if (event->type() != TouchEvent::Type) + return QGestureRecognizer::Ignore; + + const TouchEvent *e = static_cast(event); + if (e->points[1].state != TouchPoint::None) { + if (e->points[1].state == TouchPoint::Begin) + gesture->startPoint = e->points[1]; + gesture->lastPoint = gesture->point; + gesture->point = e->points[1]; + if (e->points[1].state == TouchPoint::End) + return QGestureRecognizer::GestureFinished; + else if (e->points[1].state != TouchPoint::None) + return QGestureRecognizer::GestureStarted; + } + return QGestureRecognizer::NotGesture; +} + +void SecondFingerGestureRecognizer::reset() +{ + gesture->startPoint = TouchPoint(); + gesture->lastPoint = TouchPoint(); + gesture->point = TouchPoint(); + gesture->offset = QPoint(); +} diff --git a/tests/auto/gestures/customgesturerecognizer.h b/tests/auto/gestures/customgesturerecognizer.h new file mode 100644 index 0000000..b9ab421 --- /dev/null +++ b/tests/auto/gestures/customgesturerecognizer.h @@ -0,0 +1,147 @@ +#ifndef CUSTOMGESTURERECOGNIZER_H +#define CUSTOMGESTURERECOGNIZER_H + +#include "qgesturerecognizer.h" +#include "qgesture.h" +#include "qevent.h" + +class SingleshotEvent : public QEvent +{ +public: + static const int Type = QEvent::User + 1; + SingleshotEvent() : QEvent(QEvent::Type(Type)) { } +}; + +class SingleshotGesture : public QGesture +{ + Q_OBJECT +public: + SingleshotGesture(QObject *parent, const QString &type) + : QGesture(parent, type) { } + + QPoint offset; + +protected: + void translate(const QPoint &pt) + { + offset += pt; + } +}; + +class SingleshotGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + static const char *Name; + + SingleshotGestureRecognizer(QObject *parent = 0); + + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture() { return gesture; } + void reset(); + +private: + SingleshotGesture *gesture; +}; + +struct TouchPoint { + enum State + { + None, + Begin, + Update, + End + }; + int id; + QPoint pt; + State state; + TouchPoint() : id(0), state(None) { } + TouchPoint(int id_, int x_, int y_, State state_) : id(id_), pt(x_, y_), state(state_) { } +}; + +class TouchEvent : public QEvent +{ +public: + static const int Type = QEvent::User + 2; + + TouchEvent() + : QEvent(QEvent::Type(Type)) + { + } + + TouchPoint points[2]; +}; + +class PinchGesture : public QGesture +{ + Q_OBJECT +public: + PinchGesture(QObject *parent, const QString &type) + : QGesture(parent, type) { } + + TouchPoint startPoints[2]; + TouchPoint lastPoints[2]; + TouchPoint points[2]; + + QPoint offset; + +protected: + void translate(const QPoint &pt) + { + offset += pt; + } +}; + +class PinchGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + static const char *Name; + + PinchGestureRecognizer(QObject *parent = 0); + + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture() { return gesture; } + void reset(); + +private: + PinchGesture *gesture; +}; + +class SecondFingerGesture : public QGesture +{ + Q_OBJECT +public: + SecondFingerGesture(QObject *parent, const QString &type) + : QGesture(parent, type) { } + + TouchPoint startPoint; + TouchPoint lastPoint; + TouchPoint point; + + QPoint offset; + +protected: + void translate(const QPoint &pt) + { + offset += pt; + } +}; + +class SecondFingerGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + static const char *Name; + + SecondFingerGestureRecognizer(QObject *parent = 0); + + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture() { return gesture; } + void reset(); + +private: + SecondFingerGesture *gesture; +}; + +#endif diff --git a/tests/auto/gestures/gestures.pro b/tests/auto/gestures/gestures.pro new file mode 100644 index 0000000..ac99b19 --- /dev/null +++ b/tests/auto/gestures/gestures.pro @@ -0,0 +1,3 @@ +load(qttest_p4) +SOURCES += tst_gestures.cpp customgesturerecognizer.cpp +HEADERS += customgesturerecognizer.h diff --git a/tests/auto/gestures/tst_gestures.cpp b/tests/auto/gestures/tst_gestures.cpp new file mode 100644 index 0000000..eb692e9 --- /dev/null +++ b/tests/auto/gestures/tst_gestures.cpp @@ -0,0 +1,487 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: 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. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include + +#include "customgesturerecognizer.h" + +//TESTED_CLASS= +//TESTED_FILES= + +// color generator for syntax highlighting from QQ-26 by Helder Correia +QVector highlight(const QColor &bg, const + QColor &fg, int noColors) +{ + QVector colors; + const int HUE_BASE = (bg.hue() == -1) ? 90 : bg.hue(); + int h, s, v; + for (int i = 0; i < noColors; i++) { + h = int(HUE_BASE + (360.0 / noColors * i)) % 360; + s = 240; + v = int(qMax(bg.value(), fg.value()) * 0.85); + + const int M = 35; + if ((h < bg.hue() + M && h > bg.hue() - M) + || (h < fg.hue() + M && h > fg.hue() - M)) + { + h = ((bg.hue() + fg.hue()) / (i+1)) % 360; + s = ((bg.saturation() + fg.saturation() + 2*i) + / 2) % 256; + v = ((bg.value() + fg.value() + 2*i) / 2) + % 256; + } + colors.append(QColor::fromHsv(h, s, v)); + } + return colors; +} + + +// a hack to mark an event as spontaneous. +class QETWidget +{ +public: + static void setSpont(QEvent *event, bool spont) { event->spont = spont; } +}; + +class GestureWidget : public QWidget +{ + Q_OBJECT + static QVector colors; + static int numberOfWidgets; + +public: + enum Type { DoNotGrabGestures, GrabAllGestures, GrabSingleshot, GrabPinch, GrabSecondFinger }; + + static const int LeftMargin = 10; + static const int TopMargin = 20; + + GestureWidget(Type type = GrabAllGestures) + { + if (colors.isEmpty()) { + colors = highlight(palette().color(QPalette::Window), palette().color(QPalette::Text), 5); + } + QPalette p = palette(); + p.setColor(QPalette::Window, colors[numberOfWidgets % colors.size()]); + setPalette(p); + setAutoFillBackground(true); + ++numberOfWidgets; + + QVBoxLayout *l = new QVBoxLayout(this); + l->setSpacing(0); + l->setContentsMargins(LeftMargin, TopMargin, LeftMargin, TopMargin); + + singleshotGestureId = -1; + pinchGestureId = -1; + secondFingerGestureId = -1; + if (type == GrabAllGestures || type == GrabSingleshot) { + singleshotGestureId = grabGesture(SingleshotGestureRecognizer::Name); + } + if (type == GrabAllGestures || type == GrabPinch) { + pinchGestureId = grabGesture(PinchGestureRecognizer::Name); + } + if (type == GrabAllGestures || type == GrabSecondFinger) { + secondFingerGestureId = grabGesture(SecondFingerGestureRecognizer::Name); + } + + reset(); + } + ~GestureWidget() + { + --numberOfWidgets; + ungrabGestures(); + } + + void grabSingleshotGesture() + { + singleshotGestureId = grabGesture(SingleshotGestureRecognizer::Name); + } + void grabPinchGesture() + { + pinchGestureId = grabGesture(PinchGestureRecognizer::Name); + } + void grabSecondFingerGesture() + { + secondFingerGestureId = grabGesture(SecondFingerGestureRecognizer::Name); + } + void ungrabGestures() + { + releaseGesture(singleshotGestureId); + singleshotGestureId = -1; + releaseGesture(pinchGestureId); + pinchGestureId = -1; + releaseGesture(secondFingerGestureId); + secondFingerGestureId = -1; + } + + int singleshotGestureId; + int pinchGestureId; + int secondFingerGestureId; + + bool shouldAcceptSingleshotGesture; + bool shouldAcceptPinchGesture; + bool shouldAcceptSecondFingerGesture; + + int seenGestureEvent; + struct LastGestureEvent + { + struct SingleshotGesture + { + bool delivered; + QPoint offset; + } singleshot; + struct PinchGesture + { + bool delivered; + TouchPoint startPoints[2]; + TouchPoint lastPoints[2]; + TouchPoint points[2]; + QPoint offset; + } pinch; + struct SecondFingerGesture + { + bool delivered; + TouchPoint startPoint; + TouchPoint lastPoint; + TouchPoint point; + QPoint offset; + } secondfinger; + QSet cancelled; + } last; + void reset() + { + seenGestureEvent = 0; + last.singleshot.delivered = false; + last.singleshot.offset = QPoint(); + last.pinch.delivered = false; + last.pinch.startPoints[0] = TouchPoint(); + last.pinch.startPoints[1] = TouchPoint(); + last.pinch.lastPoints[0] = TouchPoint(); + last.pinch.lastPoints[1] = TouchPoint(); + last.pinch.points[0] = TouchPoint(); + last.pinch.points[1] = TouchPoint(); + last.pinch.offset = QPoint(); + last.secondfinger.delivered = false; + last.secondfinger.startPoint = TouchPoint(); + last.secondfinger.lastPoint = TouchPoint(); + last.secondfinger.point = TouchPoint(); + last.secondfinger.offset = QPoint(); + last.cancelled.clear(); + + shouldAcceptSingleshotGesture = true; + shouldAcceptPinchGesture = true; + shouldAcceptSecondFingerGesture = true; + } + +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *e = static_cast(event); + ++seenGestureEvent; + if (SingleshotGesture *g = (SingleshotGesture*)e->gesture(SingleshotGestureRecognizer::Name)) { + last.singleshot.delivered = true; + last.singleshot.offset = g->offset; + if (shouldAcceptSingleshotGesture) + g->accept(); + } + if (PinchGesture *g = (PinchGesture*)e->gesture(PinchGestureRecognizer::Name)) { + last.pinch.delivered = true; + last.pinch.startPoints[0] = g->startPoints[0]; + last.pinch.startPoints[1] = g->startPoints[1]; + last.pinch.lastPoints[0] = g->lastPoints[0]; + last.pinch.lastPoints[1] = g->lastPoints[1]; + last.pinch.points[0] = g->points[0]; + last.pinch.points[1] = g->points[1]; + last.pinch.offset = g->offset; + if (shouldAcceptPinchGesture) + g->accept(); + } + if (SecondFingerGesture *g = (SecondFingerGesture*)e->gesture(SecondFingerGestureRecognizer::Name)) { + last.secondfinger.delivered = true; + last.secondfinger.startPoint = g->startPoint; + last.secondfinger.lastPoint = g->lastPoint; + last.secondfinger.point = g->point; + last.secondfinger.offset = g->offset; + if (shouldAcceptSecondFingerGesture) + g->accept(); + } + last.cancelled = e->cancelledGestures(); + return true; + } + return QWidget::event(event); + } +}; +QVector GestureWidget::colors; +int GestureWidget::numberOfWidgets = 0; + +class tst_Gestures : public QObject +{ + Q_OBJECT + +public: + tst_Gestures(); + virtual ~tst_Gestures(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void singleshotGesture(); + void pinchGesture(); + + void simplePropagation(); + void simplePropagation2(); + void acceptedGesturePropagation(); + +private: + SingleshotGestureRecognizer *singleshotRecognizer; + PinchGestureRecognizer *pinchRecognizer; + SecondFingerGestureRecognizer *secondFingerRecognizer; + GestureWidget *mainWidget; + + bool sendSpontaneousEvent(QWidget *receiver, QEvent *event); + void sendPinchEvents(QWidget *receiver, const QPoint &fromFinger1, const QPoint &fromFinger2); +}; + +tst_Gestures::tst_Gestures() +{ + singleshotRecognizer = new SingleshotGestureRecognizer; + pinchRecognizer = new PinchGestureRecognizer; + secondFingerRecognizer = new SecondFingerGestureRecognizer; + qApp->addGestureRecognizer(singleshotRecognizer); + qApp->addGestureRecognizer(pinchRecognizer); + qApp->addGestureRecognizer(secondFingerRecognizer); +} + +tst_Gestures::~tst_Gestures() +{ +} + + +void tst_Gestures::initTestCase() +{ + mainWidget = new GestureWidget(GestureWidget::DoNotGrabGestures); + mainWidget->setObjectName("MainGestureWidget"); + mainWidget->resize(500, 600); + mainWidget->show(); +} + +void tst_Gestures::cleanupTestCase() +{ + delete mainWidget; mainWidget = 0; +} + +void tst_Gestures::init() +{ + // TODO: Add initialization code here. + // This will be executed immediately before each test is run. + mainWidget->reset(); +} + +void tst_Gestures::cleanup() +{ +} + +bool tst_Gestures::sendSpontaneousEvent(QWidget *receiver, QEvent *event) +{ + QETWidget::setSpont(event, true); + return qApp->notify(receiver, event); +} + +void tst_Gestures::singleshotGesture() +{ + mainWidget->grabSingleshotGesture(); + SingleshotEvent event; + sendSpontaneousEvent(mainWidget, &event); + QVERIFY(mainWidget->seenGestureEvent); + QVERIFY(mainWidget->last.singleshot.delivered); + QVERIFY(mainWidget->last.cancelled.isEmpty()); +} + +void tst_Gestures::sendPinchEvents(QWidget *receiver, const QPoint &fromFinger1, const QPoint &fromFinger2) +{ + int x1 = fromFinger1.x(); + int y1 = fromFinger1.x(); + int x2 = fromFinger2.x(); + int y2 = fromFinger2.x(); + + TouchEvent event; + event.points[0] = TouchPoint(0,x1,y1, TouchPoint::Begin); + event.points[1] = TouchPoint(); + sendSpontaneousEvent(receiver, &event); + event.points[0] = TouchPoint(0, x1+=2,y1+=2, TouchPoint::Update); + event.points[1] = TouchPoint(); + sendSpontaneousEvent(receiver, &event); + event.points[0] = TouchPoint(0, x1,y1, TouchPoint::Update); + event.points[1] = TouchPoint(1, x2,y2, TouchPoint::Begin); + sendSpontaneousEvent(receiver, &event); + event.points[0] = TouchPoint(0, x1+=5,y1+=10, TouchPoint::End); + event.points[1] = TouchPoint(1, x2+=3,y2+=6, TouchPoint::Update); + sendSpontaneousEvent(receiver, &event); + event.points[0] = TouchPoint(); + event.points[1] = TouchPoint(1, x2+=10,y2+=15, TouchPoint::Update); + sendSpontaneousEvent(receiver, &event); + event.points[0] = TouchPoint(); + event.points[1] = TouchPoint(1, x2,y2, TouchPoint::End); + sendSpontaneousEvent(receiver, &event); +} + +void tst_Gestures::pinchGesture() +{ + mainWidget->grabPinchGesture(); + sendPinchEvents(mainWidget, QPoint(10,10), QPoint(20,20)); + + QVERIFY(mainWidget->seenGestureEvent); + QVERIFY(!mainWidget->last.singleshot.delivered); + QVERIFY(mainWidget->last.cancelled.isEmpty()); + QVERIFY(mainWidget->last.pinch.delivered); + QCOMPARE(mainWidget->last.pinch.startPoints[0].pt, QPoint(10,10)); + QCOMPARE(mainWidget->last.pinch.startPoints[1].pt, QPoint(20,20)); + QCOMPARE(mainWidget->last.pinch.offset, QPoint(0,0)); +} + +void tst_Gestures::simplePropagation() +{ + mainWidget->grabSingleshotGesture(); + GestureWidget offsetWidget(GestureWidget::DoNotGrabGestures); + offsetWidget.setFixedSize(30, 30); + mainWidget->layout()->addWidget(&offsetWidget); + GestureWidget nonGestureWidget(GestureWidget::DoNotGrabGestures); + mainWidget->layout()->addWidget(&nonGestureWidget); + QApplication::processEvents(); + + SingleshotEvent event; + sendSpontaneousEvent(&nonGestureWidget, &event); + QVERIFY(!offsetWidget.seenGestureEvent); + QVERIFY(!nonGestureWidget.seenGestureEvent); + QVERIFY(mainWidget->seenGestureEvent); + QVERIFY(mainWidget->last.cancelled.isEmpty()); + QVERIFY(mainWidget->last.singleshot.delivered); + QCOMPARE(mainWidget->last.singleshot.offset, QPoint(GestureWidget::LeftMargin, 30 + GestureWidget::TopMargin)); +} + +void tst_Gestures::simplePropagation2() +{ + mainWidget->grabSingleshotGesture(); + mainWidget->grabPinchGesture(); + GestureWidget nonGestureMiddleWidget(GestureWidget::DoNotGrabGestures); + GestureWidget secondGestureWidget(GestureWidget::GrabPinch); + nonGestureMiddleWidget.layout()->addWidget(&secondGestureWidget); + mainWidget->layout()->addWidget(&nonGestureMiddleWidget); + QApplication::processEvents(); + + SingleshotEvent event; + sendSpontaneousEvent(&secondGestureWidget, &event); + QVERIFY(!secondGestureWidget.seenGestureEvent); + QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); + QVERIFY(mainWidget->seenGestureEvent); + QVERIFY(mainWidget->last.singleshot.delivered); + QVERIFY(mainWidget->last.cancelled.isEmpty()); + QCOMPARE(mainWidget->last.singleshot.offset, QPoint(GestureWidget::LeftMargin*2,GestureWidget::TopMargin*2)); + + mainWidget->reset(); + nonGestureMiddleWidget.reset(); + secondGestureWidget.reset(); + + sendPinchEvents(&secondGestureWidget, QPoint(10,10), QPoint(20,20)); + QVERIFY(secondGestureWidget.seenGestureEvent); + QVERIFY(!secondGestureWidget.last.singleshot.delivered); + QVERIFY(secondGestureWidget.last.pinch.delivered); + QCOMPARE(secondGestureWidget.last.pinch.startPoints[0].pt, QPoint(10,10)); + QCOMPARE(secondGestureWidget.last.pinch.startPoints[1].pt, QPoint(20,20)); + QCOMPARE(secondGestureWidget.last.pinch.offset, QPoint(0,0)); + QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); + QVERIFY(!mainWidget->seenGestureEvent); +} + +void tst_Gestures::acceptedGesturePropagation() +{ + mainWidget->grabSingleshotGesture(); + mainWidget->grabPinchGesture(); + mainWidget->grabSecondFingerGesture(); + GestureWidget nonGestureMiddleWidget(GestureWidget::DoNotGrabGestures); + nonGestureMiddleWidget.setObjectName("nonGestureMiddleWidget"); + GestureWidget secondGestureWidget(GestureWidget::GrabSecondFinger); + secondGestureWidget.setObjectName("secondGestureWidget"); + nonGestureMiddleWidget.layout()->addWidget(&secondGestureWidget); + mainWidget->layout()->addWidget(&nonGestureMiddleWidget); + QApplication::processEvents(); + + sendPinchEvents(&secondGestureWidget, QPoint(10, 10), QPoint(40, 40)); + QVERIFY(secondGestureWidget.seenGestureEvent); + QVERIFY(!secondGestureWidget.last.singleshot.delivered); + QVERIFY(!secondGestureWidget.last.pinch.delivered); + QVERIFY(secondGestureWidget.last.secondfinger.delivered); + QCOMPARE(secondGestureWidget.last.secondfinger.startPoint.pt, QPoint(40,40)); + QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); + QVERIFY(mainWidget->seenGestureEvent); + QVERIFY(!mainWidget->last.singleshot.delivered); + QVERIFY(!mainWidget->last.secondfinger.delivered); + QVERIFY(mainWidget->last.pinch.delivered); + QCOMPARE(mainWidget->last.pinch.startPoints[0].pt, QPoint(10,10)); + QCOMPARE(mainWidget->last.pinch.startPoints[1].pt, QPoint(40,40)); + + mainWidget->reset(); + nonGestureMiddleWidget.reset(); + secondGestureWidget.reset(); + + // don't accept it and make sure it propagates to parent + secondGestureWidget.shouldAcceptSecondFingerGesture = false; + sendPinchEvents(&secondGestureWidget, QPoint(10, 10), QPoint(40, 40)); + QVERIFY(secondGestureWidget.seenGestureEvent); + QVERIFY(secondGestureWidget.last.secondfinger.delivered); + QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); + QVERIFY(mainWidget->seenGestureEvent); + QVERIFY(!mainWidget->last.singleshot.delivered); + QVERIFY(mainWidget->last.secondfinger.delivered); + QVERIFY(mainWidget->last.pinch.delivered); +} + + +QTEST_MAIN(tst_Gestures) +#include "tst_gestures.moc" -- cgit v0.12 From f1e61b11ae9561a2886365f58a82dd5d33298582 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 12:25:23 +0200 Subject: Added a TODO entry (bad name in the enum). --- src/gui/kernel/qgesturerecognizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h index 8bc8b97..67d7949 100644 --- a/src/gui/kernel/qgesturerecognizer.h +++ b/src/gui/kernel/qgesturerecognizer.h @@ -60,7 +60,7 @@ public: { Ignore, NotGesture, - GestureStarted, + GestureStarted, //TODO: rename to just Gesture? GestureFinished, MaybeGesture }; -- cgit v0.12 From 2964d27fdad2b9722b639e9b5c7c4d7a98fe5a3b Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 13:59:48 +0200 Subject: No need to mark QGesture ctor explicit --- src/gui/kernel/qgesture.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index bf501f2..64a1424 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -73,8 +73,8 @@ class Q_GUI_EXPORT QGesture : public QObject Q_PROPERTY(QPoint pos READ pos) public: - explicit QGesture(QObject *parent, const QString &type, - Qt::GestureState state = Qt::GestureStarted); + QGesture(QObject *parent, const QString &type, + Qt::GestureState state = Qt::GestureStarted); QGesture(QObject *parent, const QString &type, const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, -- cgit v0.12 From 537243d70de1474cf234880b215c75e83e80abcd Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 14:08:07 +0200 Subject: Fixed warning. --- src/gui/kernel/qgesturemanager.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 88704ef..9e8a5bb 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -546,19 +546,20 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, for(QSet::iterator it = gestures.begin(), e = gestures.end(); it != e; ++it) (*it)->ignore(); // TODO: send cancelled gesture event to the widget that received the original gesture! - QGestureEvent event(it.value(), cancelled); + QGestureEvent event(gestures, cancelled); DEBUG() << "QGestureManager::sendGestureEvent: sending now to" << receiver - << "gestures" << it.value(); + << "gestures" << gestures; bool processed = qt_sendSpontaneousEvent(receiver, &event); - QSet started = startedGestures & it.value(); - if (event.isAccepted()) { - DEBUG() << "QGestureManager::sendGestureEvent: all gestures were accepted"; - foreach(QGesture *g, started) - g->accept(); - } + QSet started = startedGestures & gestures; + DEBUG() << "QGestureManager::sendGestureEvent:" << + (event.isAccepted() ? "" : "not") << "all gestures were accepted"; if (!started.isEmpty() && !(processed && event.isAccepted())) { // there are started gestures events that weren't // accepted, so propagating each gesture independently. + if (event.isAccepted()) { + foreach(QGesture *g, started) + g->accept(); + } QSet::const_iterator it = started.begin(), e = started.end(); for(; it != e; ++it) { -- cgit v0.12 From 2836a2be1be91cd17fd1e525d47e0ec60d153657 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 17:10:43 +0200 Subject: Moved gestures info to an extra in QGraphicsItemPrivate not to increase the size of the latter when gestures are not used --- src/gui/graphicsview/qgraphicsitem.cpp | 33 ++++++++++++++++++++------------- src/gui/graphicsview/qgraphicsitem_p.h | 30 ++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index ae33674..fb95875 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1055,6 +1055,7 @@ QGraphicsItem::~QGraphicsItem() clearFocus(); d_ptr->removeExtraItemCache(); + d_ptr->removeExtraGestures(); QList oldChildren = d_ptr->children; qDeleteAll(oldChildren); Q_ASSERT(d_ptr->children.isEmpty()); @@ -5880,7 +5881,7 @@ int QGraphicsItem::grabGesture(const QString &gesture) void QGraphicsItemPrivate::grabGesture(int id) { Q_Q(QGraphicsItem); - gestures << id; + extraGestures()->gestures << id; if (scene) scene->d_func()->grabGesture(q, id); } @@ -5888,10 +5889,11 @@ void QGraphicsItemPrivate::grabGesture(int id) bool QGraphicsItemPrivate::releaseGesture(int id) { Q_Q(QGraphicsItem); - if (gestures.contains(id)) { + QGestureExtraData *extra = maybeExtraGestures(); + if (extra && extra->gestures.contains(id)) { if (scene) scene->d_func()->releaseGesture(q, id); - gestures.remove(id); + extra->gestures.remove(id); return true; } return false; @@ -5929,12 +5931,14 @@ void QGraphicsItem::setGestureEnabled(int gestureId, bool enable) bool QGraphicsItemPrivate::hasGesture(const QString &name) const { - QGestureManager *gm = QGestureManager::instance(); - QSet::const_iterator it = gestures.begin(), - e = gestures.end(); - for (; it != e; ++it) { - if (gm->gestureNameFromId(*it) == name) - return true; + if (QGestureExtraData *extra = maybeExtraGestures()) { + QGestureManager *gm = QGestureManager::instance(); + QSet::const_iterator it = extra->gestures.begin(), + e = extra->gestures.end(); + for (; it != e; ++it) { + if (gm->gestureNameFromId(*it) == name) + return true; + } } return false; } @@ -5964,16 +5968,19 @@ QVariant QGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &va { Q_UNUSED(change); if (change == QGraphicsItem::ItemSceneChange) { - if (!qVariantValue(value)) { + QGestureExtraData *extra = d_ptr->maybeExtraGestures(); + if (!qVariantValue(value) && extra) { // the item has been removed from a scene, unsubscribe gestures. Q_ASSERT(d_ptr->scene); - foreach(int id, d_ptr->gestures) + foreach(int id, extra->gestures) d_ptr->scene->d_func()->releaseGesture(this, id); } } else if (change == QGraphicsItem::ItemSceneHasChanged) { - if (QGraphicsScene *scene = qVariantValue(value)) { + QGraphicsScene *scene = qVariantValue(value); + QGestureExtraData *extra = d_ptr->maybeExtraGestures(); + if (scene && extra) { // item has been added to the scene - foreach(int id, d_ptr->gestures) + foreach(int id, extra->gestures) scene->d_func()->grabGesture(this, id); } } diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 091a461..7642b98 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -90,6 +90,12 @@ public: void purge(); }; +class QGestureExtraData +{ +public: + QSet gestures; +}; + class Q_AUTOTEST_EXPORT QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsItem) @@ -102,7 +108,8 @@ public: ExtraMaxDeviceCoordCacheSize, ExtraBoundingRegionGranularity, ExtraOpacity, - ExtraEffectiveOpacity + ExtraEffectiveOpacity, + ExtraGestures }; enum AncestorFlag { @@ -303,8 +310,27 @@ public: int siblingIndex; int index; int depth; - QSet gestures; + inline QGestureExtraData* extraGestures() const + { + QGestureExtraData *c = (QGestureExtraData *)qVariantValue(extra(ExtraGestures)); + if (!c) { + QGraphicsItemPrivate *that = const_cast(this); + c = new QGestureExtraData; + that->setExtra(ExtraGestures, qVariantFromValue(c)); + } + return c; + } + QGestureExtraData* maybeExtraGestures() const + { + return (QGestureExtraData *)qVariantValue(extra(ExtraGestures)); + } + inline void removeExtraGestures() + { + QGestureExtraData *c = (QGestureExtraData *)qVariantValue(extra(ExtraGestures)); + delete c; + unsetExtra(ExtraGestures); + } bool hasGesture(const QString &gesture) const; void grabGesture(int id); bool releaseGesture(int id); -- cgit v0.12 From c7320034f2a0f9433188a45ab507fd361a270f19 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 17:56:39 +0200 Subject: Dont accept graphicsscenegesture event by default --- src/gui/graphicsview/qgraphicssceneevent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 059d89d..bb13bd8 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -1737,6 +1737,7 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) QGraphicsSceneGestureEvent::QGraphicsSceneGestureEvent() : QGraphicsSceneEvent(QEvent::GraphicsSceneGesture) { + setAccepted(false); } /*! -- cgit v0.12 From 6cd7a9039eafd0a4a00b0a1cf80bdf7517a25c50 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 18 May 2009 18:49:50 +0200 Subject: Started implementing tests for gestures on graphicsview --- src/gui/graphicsview/qgraphicsview.cpp | 5 +- tests/auto/gestures/tst_gestures.cpp | 311 +++++++++++++++++++++------------ 2 files changed, 199 insertions(+), 117 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 2babce9..f3feb15 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -2787,7 +2787,7 @@ bool QGraphicsView::event(QEvent *event) break; case QEvent::Gesture: viewportEvent(event); - break; + return true; default: break; } @@ -2876,8 +2876,7 @@ bool QGraphicsView::viewportEvent(QEvent *event) gestureEvent.setCancelledGestures(ev->cancelledGestures()); QApplication::sendEvent(d->scene, &gestureEvent); event->setAccepted(gestureEvent.isAccepted()); - if (gestureEvent.isAccepted()) - return true; + return true; } break; case QEvent::TouchBegin: diff --git a/tests/auto/gestures/tst_gestures.cpp b/tests/auto/gestures/tst_gestures.cpp index eb692e9..614474f9 100644 --- a/tests/auto/gestures/tst_gestures.cpp +++ b/tests/auto/gestures/tst_gestures.cpp @@ -40,8 +40,7 @@ ****************************************************************************/ -#include -#include +#include #include @@ -85,6 +84,58 @@ public: static void setSpont(QEvent *event, bool spont) { event->spont = spont; } }; +struct GestureState +{ + int seenGestureEvent; + struct LastGestureEvent + { + struct SingleshotGesture + { + bool delivered; + QPoint offset; + } singleshot; + struct PinchGesture + { + bool delivered; + TouchPoint startPoints[2]; + TouchPoint lastPoints[2]; + TouchPoint points[2]; + QPoint offset; + } pinch; + struct SecondFingerGesture + { + bool delivered; + TouchPoint startPoint; + TouchPoint lastPoint; + TouchPoint point; + QPoint offset; + } secondfinger; + QSet cancelled; + } last; + + GestureState() { reset(); } + void reset() + { + seenGestureEvent = 0; + last.singleshot.delivered = false; + last.singleshot.offset = QPoint(); + last.pinch.delivered = false; + last.pinch.startPoints[0] = TouchPoint(); + last.pinch.startPoints[1] = TouchPoint(); + last.pinch.lastPoints[0] = TouchPoint(); + last.pinch.lastPoints[1] = TouchPoint(); + last.pinch.points[0] = TouchPoint(); + last.pinch.points[1] = TouchPoint(); + last.pinch.offset = QPoint(); + last.secondfinger.delivered = false; + last.secondfinger.startPoint = TouchPoint(); + last.secondfinger.lastPoint = TouchPoint(); + last.secondfinger.point = TouchPoint(); + last.secondfinger.offset = QPoint(); + last.cancelled.clear(); + } +}; + class GestureWidget : public QWidget { Q_OBJECT @@ -124,7 +175,6 @@ public: if (type == GrabAllGestures || type == GrabSecondFinger) { secondFingerGestureId = grabGesture(SecondFingerGestureRecognizer::Name); } - reset(); } ~GestureWidget() @@ -163,91 +213,49 @@ public: bool shouldAcceptPinchGesture; bool shouldAcceptSecondFingerGesture; - int seenGestureEvent; - struct LastGestureEvent - { - struct SingleshotGesture - { - bool delivered; - QPoint offset; - } singleshot; - struct PinchGesture - { - bool delivered; - TouchPoint startPoints[2]; - TouchPoint lastPoints[2]; - TouchPoint points[2]; - QPoint offset; - } pinch; - struct SecondFingerGesture - { - bool delivered; - TouchPoint startPoint; - TouchPoint lastPoint; - TouchPoint point; - QPoint offset; - } secondfinger; - QSet cancelled; - } last; + GestureState gesture; + void reset() { - seenGestureEvent = 0; - last.singleshot.delivered = false; - last.singleshot.offset = QPoint(); - last.pinch.delivered = false; - last.pinch.startPoints[0] = TouchPoint(); - last.pinch.startPoints[1] = TouchPoint(); - last.pinch.lastPoints[0] = TouchPoint(); - last.pinch.lastPoints[1] = TouchPoint(); - last.pinch.points[0] = TouchPoint(); - last.pinch.points[1] = TouchPoint(); - last.pinch.offset = QPoint(); - last.secondfinger.delivered = false; - last.secondfinger.startPoint = TouchPoint(); - last.secondfinger.lastPoint = TouchPoint(); - last.secondfinger.point = TouchPoint(); - last.secondfinger.offset = QPoint(); - last.cancelled.clear(); - shouldAcceptSingleshotGesture = true; shouldAcceptPinchGesture = true; shouldAcceptSecondFingerGesture = true; + gesture.reset(); } - protected: bool event(QEvent *event) { if (event->type() == QEvent::Gesture) { QGestureEvent *e = static_cast(event); - ++seenGestureEvent; + ++gesture.seenGestureEvent; if (SingleshotGesture *g = (SingleshotGesture*)e->gesture(SingleshotGestureRecognizer::Name)) { - last.singleshot.delivered = true; - last.singleshot.offset = g->offset; + gesture.last.singleshot.delivered = true; + gesture.last.singleshot.offset = g->offset; if (shouldAcceptSingleshotGesture) g->accept(); } if (PinchGesture *g = (PinchGesture*)e->gesture(PinchGestureRecognizer::Name)) { - last.pinch.delivered = true; - last.pinch.startPoints[0] = g->startPoints[0]; - last.pinch.startPoints[1] = g->startPoints[1]; - last.pinch.lastPoints[0] = g->lastPoints[0]; - last.pinch.lastPoints[1] = g->lastPoints[1]; - last.pinch.points[0] = g->points[0]; - last.pinch.points[1] = g->points[1]; - last.pinch.offset = g->offset; + gesture.last.pinch.delivered = true; + gesture.last.pinch.startPoints[0] = g->startPoints[0]; + gesture.last.pinch.startPoints[1] = g->startPoints[1]; + gesture.last.pinch.lastPoints[0] = g->lastPoints[0]; + gesture.last.pinch.lastPoints[1] = g->lastPoints[1]; + gesture.last.pinch.points[0] = g->points[0]; + gesture.last.pinch.points[1] = g->points[1]; + gesture.last.pinch.offset = g->offset; if (shouldAcceptPinchGesture) g->accept(); } if (SecondFingerGesture *g = (SecondFingerGesture*)e->gesture(SecondFingerGestureRecognizer::Name)) { - last.secondfinger.delivered = true; - last.secondfinger.startPoint = g->startPoint; - last.secondfinger.lastPoint = g->lastPoint; - last.secondfinger.point = g->point; - last.secondfinger.offset = g->offset; + gesture.last.secondfinger.delivered = true; + gesture.last.secondfinger.startPoint = g->startPoint; + gesture.last.secondfinger.lastPoint = g->lastPoint; + gesture.last.secondfinger.point = g->point; + gesture.last.secondfinger.offset = g->offset; if (shouldAcceptSecondFingerGesture) g->accept(); } - last.cancelled = e->cancelledGestures(); + gesture.last.cancelled = e->cancelledGestures(); return true; } return QWidget::event(event); @@ -256,6 +264,61 @@ protected: QVector GestureWidget::colors; int GestureWidget::numberOfWidgets = 0; +class GraphicsScene : public QGraphicsScene +{ +public: + GraphicsScene() + { + shouldAcceptSingleshotGesture = false; + shouldAcceptPinchGesture = false; + shouldAcceptSecondFingerGesture = false; + } + bool shouldAcceptSingleshotGesture; + bool shouldAcceptPinchGesture; + bool shouldAcceptSecondFingerGesture; + GestureState gesture; + +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::GraphicsSceneGesture) { + QGraphicsSceneGestureEvent *e = static_cast(event); + ++gesture.seenGestureEvent; + QGraphicsScene::event(event); + if (SingleshotGesture *g = (SingleshotGesture*)e->gesture(SingleshotGestureRecognizer::Name)) { + gesture.last.singleshot.delivered = true; + gesture.last.singleshot.offset = g->offset; + if (shouldAcceptSingleshotGesture) + g->accept(); + } + if (PinchGesture *g = (PinchGesture*)e->gesture(PinchGestureRecognizer::Name)) { + gesture.last.pinch.delivered = true; + gesture.last.pinch.startPoints[0] = g->startPoints[0]; + gesture.last.pinch.startPoints[1] = g->startPoints[1]; + gesture.last.pinch.lastPoints[0] = g->lastPoints[0]; + gesture.last.pinch.lastPoints[1] = g->lastPoints[1]; + gesture.last.pinch.points[0] = g->points[0]; + gesture.last.pinch.points[1] = g->points[1]; + gesture.last.pinch.offset = g->offset; + if (shouldAcceptPinchGesture) + g->accept(); + } + if (SecondFingerGesture *g = (SecondFingerGesture*)e->gesture(SecondFingerGestureRecognizer::Name)) { + gesture.last.secondfinger.delivered = true; + gesture.last.secondfinger.startPoint = g->startPoint; + gesture.last.secondfinger.lastPoint = g->lastPoint; + gesture.last.secondfinger.point = g->point; + gesture.last.secondfinger.offset = g->offset; + if (shouldAcceptSecondFingerGesture) + g->accept(); + } + gesture.last.cancelled = e->cancelledGestures(); + return true; + } + return QGraphicsScene::event(event); + } +}; + class tst_Gestures : public QObject { Q_OBJECT @@ -278,6 +341,8 @@ private slots: void simplePropagation2(); void acceptedGesturePropagation(); + void simpleGraphicsView(); + private: SingleshotGestureRecognizer *singleshotRecognizer; PinchGestureRecognizer *pinchRecognizer; @@ -338,9 +403,9 @@ void tst_Gestures::singleshotGesture() mainWidget->grabSingleshotGesture(); SingleshotEvent event; sendSpontaneousEvent(mainWidget, &event); - QVERIFY(mainWidget->seenGestureEvent); - QVERIFY(mainWidget->last.singleshot.delivered); - QVERIFY(mainWidget->last.cancelled.isEmpty()); + QVERIFY(mainWidget->gesture.seenGestureEvent); + QVERIFY(mainWidget->gesture.last.singleshot.delivered); + QVERIFY(mainWidget->gesture.last.cancelled.isEmpty()); } void tst_Gestures::sendPinchEvents(QWidget *receiver, const QPoint &fromFinger1, const QPoint &fromFinger2) @@ -376,13 +441,13 @@ void tst_Gestures::pinchGesture() mainWidget->grabPinchGesture(); sendPinchEvents(mainWidget, QPoint(10,10), QPoint(20,20)); - QVERIFY(mainWidget->seenGestureEvent); - QVERIFY(!mainWidget->last.singleshot.delivered); - QVERIFY(mainWidget->last.cancelled.isEmpty()); - QVERIFY(mainWidget->last.pinch.delivered); - QCOMPARE(mainWidget->last.pinch.startPoints[0].pt, QPoint(10,10)); - QCOMPARE(mainWidget->last.pinch.startPoints[1].pt, QPoint(20,20)); - QCOMPARE(mainWidget->last.pinch.offset, QPoint(0,0)); + QVERIFY(mainWidget->gesture.seenGestureEvent); + QVERIFY(!mainWidget->gesture.last.singleshot.delivered); + QVERIFY(mainWidget->gesture.last.cancelled.isEmpty()); + QVERIFY(mainWidget->gesture.last.pinch.delivered); + QCOMPARE(mainWidget->gesture.last.pinch.startPoints[0].pt, QPoint(10,10)); + QCOMPARE(mainWidget->gesture.last.pinch.startPoints[1].pt, QPoint(20,20)); + QCOMPARE(mainWidget->gesture.last.pinch.offset, QPoint(0,0)); } void tst_Gestures::simplePropagation() @@ -397,12 +462,12 @@ void tst_Gestures::simplePropagation() SingleshotEvent event; sendSpontaneousEvent(&nonGestureWidget, &event); - QVERIFY(!offsetWidget.seenGestureEvent); - QVERIFY(!nonGestureWidget.seenGestureEvent); - QVERIFY(mainWidget->seenGestureEvent); - QVERIFY(mainWidget->last.cancelled.isEmpty()); - QVERIFY(mainWidget->last.singleshot.delivered); - QCOMPARE(mainWidget->last.singleshot.offset, QPoint(GestureWidget::LeftMargin, 30 + GestureWidget::TopMargin)); + QVERIFY(!offsetWidget.gesture.seenGestureEvent); + QVERIFY(!nonGestureWidget.gesture.seenGestureEvent); + QVERIFY(mainWidget->gesture.seenGestureEvent); + QVERIFY(mainWidget->gesture.last.cancelled.isEmpty()); + QVERIFY(mainWidget->gesture.last.singleshot.delivered); + QCOMPARE(mainWidget->gesture.last.singleshot.offset, QPoint(GestureWidget::LeftMargin, 30 + GestureWidget::TopMargin)); } void tst_Gestures::simplePropagation2() @@ -417,26 +482,26 @@ void tst_Gestures::simplePropagation2() SingleshotEvent event; sendSpontaneousEvent(&secondGestureWidget, &event); - QVERIFY(!secondGestureWidget.seenGestureEvent); - QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); - QVERIFY(mainWidget->seenGestureEvent); - QVERIFY(mainWidget->last.singleshot.delivered); - QVERIFY(mainWidget->last.cancelled.isEmpty()); - QCOMPARE(mainWidget->last.singleshot.offset, QPoint(GestureWidget::LeftMargin*2,GestureWidget::TopMargin*2)); + QVERIFY(!secondGestureWidget.gesture.seenGestureEvent); + QVERIFY(!nonGestureMiddleWidget.gesture.seenGestureEvent); + QVERIFY(mainWidget->gesture.seenGestureEvent); + QVERIFY(mainWidget->gesture.last.singleshot.delivered); + QVERIFY(mainWidget->gesture.last.cancelled.isEmpty()); + QCOMPARE(mainWidget->gesture.last.singleshot.offset, QPoint(GestureWidget::LeftMargin*2,GestureWidget::TopMargin*2)); mainWidget->reset(); nonGestureMiddleWidget.reset(); secondGestureWidget.reset(); sendPinchEvents(&secondGestureWidget, QPoint(10,10), QPoint(20,20)); - QVERIFY(secondGestureWidget.seenGestureEvent); - QVERIFY(!secondGestureWidget.last.singleshot.delivered); - QVERIFY(secondGestureWidget.last.pinch.delivered); - QCOMPARE(secondGestureWidget.last.pinch.startPoints[0].pt, QPoint(10,10)); - QCOMPARE(secondGestureWidget.last.pinch.startPoints[1].pt, QPoint(20,20)); - QCOMPARE(secondGestureWidget.last.pinch.offset, QPoint(0,0)); - QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); - QVERIFY(!mainWidget->seenGestureEvent); + QVERIFY(secondGestureWidget.gesture.seenGestureEvent); + QVERIFY(!secondGestureWidget.gesture.last.singleshot.delivered); + QVERIFY(secondGestureWidget.gesture.last.pinch.delivered); + QCOMPARE(secondGestureWidget.gesture.last.pinch.startPoints[0].pt, QPoint(10,10)); + QCOMPARE(secondGestureWidget.gesture.last.pinch.startPoints[1].pt, QPoint(20,20)); + QCOMPARE(secondGestureWidget.gesture.last.pinch.offset, QPoint(0,0)); + QVERIFY(!nonGestureMiddleWidget.gesture.seenGestureEvent); + QVERIFY(!mainWidget->gesture.seenGestureEvent); } void tst_Gestures::acceptedGesturePropagation() @@ -453,18 +518,18 @@ void tst_Gestures::acceptedGesturePropagation() QApplication::processEvents(); sendPinchEvents(&secondGestureWidget, QPoint(10, 10), QPoint(40, 40)); - QVERIFY(secondGestureWidget.seenGestureEvent); - QVERIFY(!secondGestureWidget.last.singleshot.delivered); - QVERIFY(!secondGestureWidget.last.pinch.delivered); - QVERIFY(secondGestureWidget.last.secondfinger.delivered); - QCOMPARE(secondGestureWidget.last.secondfinger.startPoint.pt, QPoint(40,40)); - QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); - QVERIFY(mainWidget->seenGestureEvent); - QVERIFY(!mainWidget->last.singleshot.delivered); - QVERIFY(!mainWidget->last.secondfinger.delivered); - QVERIFY(mainWidget->last.pinch.delivered); - QCOMPARE(mainWidget->last.pinch.startPoints[0].pt, QPoint(10,10)); - QCOMPARE(mainWidget->last.pinch.startPoints[1].pt, QPoint(40,40)); + QVERIFY(secondGestureWidget.gesture.seenGestureEvent); + QVERIFY(!secondGestureWidget.gesture.last.singleshot.delivered); + QVERIFY(!secondGestureWidget.gesture.last.pinch.delivered); + QVERIFY(secondGestureWidget.gesture.last.secondfinger.delivered); + QCOMPARE(secondGestureWidget.gesture.last.secondfinger.startPoint.pt, QPoint(40,40)); + QVERIFY(!nonGestureMiddleWidget.gesture.seenGestureEvent); + QVERIFY(mainWidget->gesture.seenGestureEvent); + QVERIFY(!mainWidget->gesture.last.singleshot.delivered); + QVERIFY(!mainWidget->gesture.last.secondfinger.delivered); + QVERIFY(mainWidget->gesture.last.pinch.delivered); + QCOMPARE(mainWidget->gesture.last.pinch.startPoints[0].pt, QPoint(10,10)); + QCOMPARE(mainWidget->gesture.last.pinch.startPoints[1].pt, QPoint(40,40)); mainWidget->reset(); nonGestureMiddleWidget.reset(); @@ -473,15 +538,33 @@ void tst_Gestures::acceptedGesturePropagation() // don't accept it and make sure it propagates to parent secondGestureWidget.shouldAcceptSecondFingerGesture = false; sendPinchEvents(&secondGestureWidget, QPoint(10, 10), QPoint(40, 40)); - QVERIFY(secondGestureWidget.seenGestureEvent); - QVERIFY(secondGestureWidget.last.secondfinger.delivered); - QVERIFY(!nonGestureMiddleWidget.seenGestureEvent); - QVERIFY(mainWidget->seenGestureEvent); - QVERIFY(!mainWidget->last.singleshot.delivered); - QVERIFY(mainWidget->last.secondfinger.delivered); - QVERIFY(mainWidget->last.pinch.delivered); + QVERIFY(secondGestureWidget.gesture.seenGestureEvent); + QVERIFY(secondGestureWidget.gesture.last.secondfinger.delivered); + QVERIFY(!nonGestureMiddleWidget.gesture.seenGestureEvent); + QVERIFY(mainWidget->gesture.seenGestureEvent); + QVERIFY(!mainWidget->gesture.last.singleshot.delivered); + QVERIFY(mainWidget->gesture.last.secondfinger.delivered); + QVERIFY(mainWidget->gesture.last.pinch.delivered); } +void tst_Gestures::simpleGraphicsView() +{ + mainWidget->grabSingleshotGesture(); + GraphicsScene scene; + QGraphicsView view(&scene); + view.grabGesture(SingleshotGestureRecognizer::Name); + mainWidget->layout()->addWidget(&view); + QApplication::processEvents(); + + scene.shouldAcceptSingleshotGesture = true; + + SingleshotEvent event; + sendSpontaneousEvent(&view, &event); + QVERIFY(!mainWidget->gesture.seenGestureEvent); + QVERIFY(scene.gesture.seenGestureEvent); + QVERIFY(scene.gesture.last.singleshot.delivered); + QVERIFY(scene.gesture.last.cancelled.isEmpty()); +} QTEST_MAIN(tst_Gestures) #include "tst_gestures.moc" -- cgit v0.12 From cc1486132fcb47f74c132f3f17f7f2759b0ba165 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 19 May 2009 18:10:30 +0200 Subject: Added setters to QGesture object. --- src/gui/kernel/qgesture.cpp | 25 +++++++++++++++++++++++++ src/gui/kernel/qgesture.h | 21 ++++++++++++++------- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index ac55521..2bb7de7 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -181,6 +181,11 @@ QRect QGesture::rect() const return d_func()->rect; } +void QGesture::setRect(const QRect &rect) +{ + d_func()->rect = rect; +} + /*! \property QGesture::hotSpot @@ -191,6 +196,11 @@ QPoint QGesture::hotSpot() const return d_func()->hotSpot; } +void QGesture::setHotSpot(const QPoint &point) +{ + d_func()->hotSpot = point; +} + /*! \property QGesture::startTime @@ -221,6 +231,11 @@ QPoint QGesture::startPos() const return d_func()->startPos; } +void QGesture::setStartPos(const QPoint &point) +{ + d_func()->startPos = point; +} + /*! \property QGesture::lastPos @@ -231,6 +246,11 @@ QPoint QGesture::lastPos() const return d_func()->lastPos; } +void QGesture::setLastPos(const QPoint &point) +{ + d_func()->lastPos = point; +} + /*! \property QGesture::pos @@ -241,6 +261,11 @@ QPoint QGesture::pos() const return d_func()->pos; } +void QGesture::setPos(const QPoint &point) +{ + d_func()->pos = point; +} + /*! \class QPanningGesture \since 4.6 diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 64a1424..95486bf 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -64,13 +64,14 @@ class Q_GUI_EXPORT QGesture : public QObject Q_PROPERTY(QString type READ type) Q_PROPERTY(Qt::GestureState state READ state) - Q_PROPERTY(QRect rect READ rect) - Q_PROPERTY(QPoint hotSpot READ hotSpot) Q_PROPERTY(QDateTime startTime READ startTime) Q_PROPERTY(uint duration READ duration) - Q_PROPERTY(QPoint startPos READ startPos) - Q_PROPERTY(QPoint lastPos READ lastPos) - Q_PROPERTY(QPoint pos READ pos) + + Q_PROPERTY(QRect rect READ rect WRITE setRect) + Q_PROPERTY(QPoint hotSpot READ hotSpot WRITE setHotSpot) + Q_PROPERTY(QPoint startPos READ startPos WRITE setStartPos) + Q_PROPERTY(QPoint lastPos READ lastPos WRITE setLastPos) + Q_PROPERTY(QPoint pos READ pos WRITE setPos) public: QGesture(QObject *parent, const QString &type, @@ -91,14 +92,20 @@ public: QString type() const; Qt::GestureState state() const; - QRect rect() const; - QPoint hotSpot() const; QDateTime startTime() const; uint duration() const; + QRect rect() const; + void setRect(const QRect &rect); + QPoint hotSpot() const; + void setHotSpot(const QPoint &point); + QPoint startPos() const; + void setStartPos(const QPoint &point); QPoint lastPos() const; + void setLastPos(const QPoint &point); QPoint pos() const; + void setPos(const QPoint &point); protected: QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, -- cgit v0.12 From 7a667dd45130142589c6a72f663ee647b1e563fd Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 19 May 2009 18:52:11 +0200 Subject: Added a simple test for gesture event delivery to graphicsitems --- src/gui/graphicsview/qgraphicsscene.cpp | 1 + src/gui/kernel/qgesturemanager.cpp | 1 + tests/auto/gestures/customgesturerecognizer.cpp | 2 + tests/auto/gestures/customgesturerecognizer.h | 6 +- tests/auto/gestures/tst_gestures.cpp | 139 +++++++++++++++++++++++- 5 files changed, 145 insertions(+), 4 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 8e5ebdb..53feb9e 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3945,6 +3945,7 @@ bool QGraphicsScene::event(QEvent *event) it != e; ++it) { QGesture *g = *it; QGesturePrivate *gd = g->d_func(); + gd->graphicsItem = 0; QList itemsInGestureArea = items(g->hotSpot()); const QString gestureName = g->type(); foreach(QGraphicsItem *item, itemsInGestureArea) { diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 9e8a5bb..799bcfd 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -507,6 +507,7 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, if (receiver) { // find the target widget gd->widget = 0; + gd->graphicsItem = 0; QWidget *w = receiver; QPoint offset; const QString gestureType = g->type(); diff --git a/tests/auto/gestures/customgesturerecognizer.cpp b/tests/auto/gestures/customgesturerecognizer.cpp index 906fd32..898071a 100644 --- a/tests/auto/gestures/customgesturerecognizer.cpp +++ b/tests/auto/gestures/customgesturerecognizer.cpp @@ -14,6 +14,7 @@ SingleshotGestureRecognizer::SingleshotGestureRecognizer(QObject *parent) QGestureRecognizer::Result SingleshotGestureRecognizer::filterEvent(const QEvent *event) { if (event->type() == SingleshotEvent::Type) { + gesture->setHotSpot(static_cast(event)->point); return QGestureRecognizer::GestureFinished; } return QGestureRecognizer::NotGesture; @@ -21,6 +22,7 @@ QGestureRecognizer::Result SingleshotGestureRecognizer::filterEvent(const QEvent void SingleshotGestureRecognizer::reset() { + gesture->setHotSpot(QPoint()); gesture->offset = QPoint(); } diff --git a/tests/auto/gestures/customgesturerecognizer.h b/tests/auto/gestures/customgesturerecognizer.h index b9ab421..9b56433 100644 --- a/tests/auto/gestures/customgesturerecognizer.h +++ b/tests/auto/gestures/customgesturerecognizer.h @@ -9,7 +9,11 @@ class SingleshotEvent : public QEvent { public: static const int Type = QEvent::User + 1; - SingleshotEvent() : QEvent(QEvent::Type(Type)) { } + + QPoint point; + + explicit SingleshotEvent(int x = 0, int y = 0) + : QEvent(QEvent::Type(Type)), point(x, y) { } }; class SingleshotGesture : public QGesture diff --git a/tests/auto/gestures/tst_gestures.cpp b/tests/auto/gestures/tst_gestures.cpp index 614474f9..22d16fd 100644 --- a/tests/auto/gestures/tst_gestures.cpp +++ b/tests/auto/gestures/tst_gestures.cpp @@ -269,15 +269,20 @@ class GraphicsScene : public QGraphicsScene public: GraphicsScene() { - shouldAcceptSingleshotGesture = false; - shouldAcceptPinchGesture = false; - shouldAcceptSecondFingerGesture = false; + reset(); } bool shouldAcceptSingleshotGesture; bool shouldAcceptPinchGesture; bool shouldAcceptSecondFingerGesture; GestureState gesture; + void reset() + { + shouldAcceptSingleshotGesture = false; + shouldAcceptPinchGesture = false; + shouldAcceptSecondFingerGesture = false; + gesture.reset(); + } protected: bool event(QEvent *event) { @@ -319,6 +324,103 @@ protected: } }; +class GraphicsItem : public QGraphicsItem +{ +public: + GraphicsItem() + { + reset(); + } + + QRectF boundingRect() const + { + return QRectF(0, 0, 100, 100); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*) + { + painter->setBrush(Qt::green); + painter->drawRect(0, 0, 100, 100); + } + + void grabSingleshotGesture() + { + singleshotGestureId = grabGesture(SingleshotGestureRecognizer::Name); + } + void grabPinchGesture() + { + pinchGestureId = grabGesture(PinchGestureRecognizer::Name); + } + void grabSecondFingerGesture() + { + secondFingerGestureId = grabGesture(SecondFingerGestureRecognizer::Name); + } + void ungrabGestures() + { + releaseGesture(singleshotGestureId); + singleshotGestureId = -1; + releaseGesture(pinchGestureId); + pinchGestureId = -1; + releaseGesture(secondFingerGestureId); + secondFingerGestureId = -1; + } + + int singleshotGestureId; + int pinchGestureId; + int secondFingerGestureId; + + bool shouldAcceptSingleshotGesture; + bool shouldAcceptPinchGesture; + bool shouldAcceptSecondFingerGesture; + GestureState gesture; + + void reset() + { + shouldAcceptSingleshotGesture = true; + shouldAcceptPinchGesture = true; + shouldAcceptSecondFingerGesture = true; + gesture.reset(); + } +protected: + bool sceneEvent(QEvent *event) + { + if (event->type() == QEvent::GraphicsSceneGesture) { + QGraphicsSceneGestureEvent *e = static_cast(event); + ++gesture.seenGestureEvent; + if (SingleshotGesture *g = (SingleshotGesture*)e->gesture(SingleshotGestureRecognizer::Name)) { + gesture.last.singleshot.delivered = true; + gesture.last.singleshot.offset = g->offset; + if (shouldAcceptSingleshotGesture) + g->accept(); + } + if (PinchGesture *g = (PinchGesture*)e->gesture(PinchGestureRecognizer::Name)) { + gesture.last.pinch.delivered = true; + gesture.last.pinch.startPoints[0] = g->startPoints[0]; + gesture.last.pinch.startPoints[1] = g->startPoints[1]; + gesture.last.pinch.lastPoints[0] = g->lastPoints[0]; + gesture.last.pinch.lastPoints[1] = g->lastPoints[1]; + gesture.last.pinch.points[0] = g->points[0]; + gesture.last.pinch.points[1] = g->points[1]; + gesture.last.pinch.offset = g->offset; + if (shouldAcceptPinchGesture) + g->accept(); + } + if (SecondFingerGesture *g = (SecondFingerGesture*)e->gesture(SecondFingerGestureRecognizer::Name)) { + gesture.last.secondfinger.delivered = true; + gesture.last.secondfinger.startPoint = g->startPoint; + gesture.last.secondfinger.lastPoint = g->lastPoint; + gesture.last.secondfinger.point = g->point; + gesture.last.secondfinger.offset = g->offset; + if (shouldAcceptSecondFingerGesture) + g->accept(); + } + gesture.last.cancelled = e->cancelledGestures(); + return true; + } + return QGraphicsItem::sceneEvent(event); + } +}; + class tst_Gestures : public QObject { Q_OBJECT @@ -342,6 +444,7 @@ private slots: void acceptedGesturePropagation(); void simpleGraphicsView(); + void simpleGraphicsItem(); private: SingleshotGestureRecognizer *singleshotRecognizer; @@ -566,5 +669