aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/extras/doc/images/qtquickextras2-tumbler-wrap.gifbin28883 -> 38931 bytes
-rw-r--r--tests/manual/gifs/capturedevent.cpp113
-rw-r--r--tests/manual/gifs/capturedevent.h67
-rw-r--r--tests/manual/gifs/data/qtquickextras2-tumbler-wrap.qml119
-rw-r--r--tests/manual/gifs/eventcapturer.cpp234
-rw-r--r--tests/manual/gifs/eventcapturer.h97
-rw-r--r--tests/manual/gifs/gifrecorder.cpp170
-rw-r--r--tests/manual/gifs/gifrecorder.h83
-rw-r--r--tests/manual/gifs/gifs.pro20
-rw-r--r--tests/manual/gifs/tst_gifs.cpp148
-rw-r--r--tests/manual/manual.pro3
11 files changed, 1054 insertions, 0 deletions
diff --git a/src/extras/doc/images/qtquickextras2-tumbler-wrap.gif b/src/extras/doc/images/qtquickextras2-tumbler-wrap.gif
index c7624599..2a7e435d 100644
--- a/src/extras/doc/images/qtquickextras2-tumbler-wrap.gif
+++ b/src/extras/doc/images/qtquickextras2-tumbler-wrap.gif
Binary files differ
diff --git a/tests/manual/gifs/capturedevent.cpp b/tests/manual/gifs/capturedevent.cpp
new file mode 100644
index 00000000..a36086c8
--- /dev/null
+++ b/tests/manual/gifs/capturedevent.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "capturedevent.h"
+
+#include <QMetaEnum>
+#include <QMouseEvent>
+
+namespace {
+ static inline bool isMouseEvent(const QEvent &event)
+ {
+ return event.type() >= QEvent::MouseButtonPress && event.type() <= QEvent::MouseMove;
+ }
+}
+
+CapturedEvent::CapturedEvent()
+{
+}
+
+CapturedEvent::CapturedEvent(const QEvent &event, int delay)
+{
+ setEvent(event);
+ setDelay(delay);
+}
+
+void CapturedEvent::setEvent(const QEvent &event)
+{
+ mType = event.type();
+
+ if (isMouseEvent(event)) {
+ const QMouseEvent *mouseEvent = static_cast<const QMouseEvent*>(&event);
+ mPos = mouseEvent->pos();
+ mMouseButton = mouseEvent->button();
+ }
+}
+
+QEvent::Type CapturedEvent::type() const
+{
+ return mType;
+}
+
+int CapturedEvent::delay() const
+{
+ return mDelay;
+}
+
+void CapturedEvent::setDelay(int delay)
+{
+ mDelay = delay;
+
+ mCppCommand.clear();
+
+ // We generate the C++ command here instead of when the event is captured,
+ // because events() might trim some events, causing the delay of some events to change.
+ // If we did it earlier, the events wouldn't have correct delays.
+ if (mType == QEvent::MouseMove) {
+ mCppCommand = QString::fromLatin1("QTest::mouseMove(&view, QPoint(%1, %2), %3);")
+ .arg(mPos.x())
+ .arg(mPos.y())
+ .arg(mDelay);
+
+ } else if (mType >= QEvent::MouseButtonPress && mType <= QEvent::MouseButtonDblClick) {
+ QString eventTestFunctionName = (mType == QEvent::MouseButtonPress
+ ? "mousePress" : (mType == QEvent::MouseButtonRelease
+ ? "mouseRelease" : "mouseDClick"));
+ QString buttonStr = QMetaEnum::fromType<Qt::MouseButtons>().valueToKey(mMouseButton);
+ mCppCommand = QString::fromLatin1("QTest::%1(&view, Qt::%2, Qt::NoModifier, QPoint(%3, %4), %5);")
+ .arg(eventTestFunctionName)
+ .arg(buttonStr)
+ .arg(mPos.x())
+ .arg(mPos.y())
+ .arg(mDelay);
+ }
+}
+
+QString CapturedEvent::cppCommand() const
+{
+ return mCppCommand;
+}
+
diff --git a/tests/manual/gifs/capturedevent.h b/tests/manual/gifs/capturedevent.h
new file mode 100644
index 00000000..dd495c2d
--- /dev/null
+++ b/tests/manual/gifs/capturedevent.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CAPTUREDEVENT_H
+#define CAPTUREDEVENT_H
+
+#include <QEvent>
+#include <QPoint>
+#include <QString>
+
+class CapturedEvent
+{
+public:
+ CapturedEvent();
+ CapturedEvent(const QEvent &event, int delay);
+
+ void setEvent(const QEvent &event);
+
+ int delay() const;
+ void setDelay(int delay);
+
+ QEvent::Type type() const;
+
+ QString cppCommand() const;
+
+private:
+ QEvent::Type mType;
+ QPoint mPos;
+ Qt::MouseButton mMouseButton;
+ int mDelay;
+ QString mCppCommand;
+};
+
+#endif // CAPTUREDEVENT_H
diff --git a/tests/manual/gifs/data/qtquickextras2-tumbler-wrap.qml b/tests/manual/gifs/data/qtquickextras2-tumbler-wrap.qml
new file mode 100644
index 00000000..e2517d53
--- /dev/null
+++ b/tests/manual/gifs/data/qtquickextras2-tumbler-wrap.qml
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.0
+import QtQuick.Extras 2.0
+
+Item {
+ width: 200
+ height: 200
+
+ Frame {
+ padding: 0
+ anchors.centerIn: parent
+ width: row.implicitWidth
+ height: row.implicitHeight
+
+ FontMetrics {
+ id: fontMetrics
+ }
+
+ Component {
+ id: delegateComponent
+
+ Text {
+ text: modelData.toString().length < 2 ? "0" + modelData : modelData
+ color: "#666666"
+ opacity: 0.4 + Math.max(0, 1 - Math.abs(AbstractTumbler.displacement)) * 0.6
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: fontMetrics.font.pixelSize * 1.25
+ }
+ }
+
+ Row {
+ id: row
+
+ Tumbler {
+ id: hoursTumbler
+ model: 12
+ delegate: Text {
+ text: (modelData.toString().length < 2 ? "0" : "") + (modelData + 1)
+ color: "#666666"
+ opacity: 0.4 + Math.max(0, 1 - Math.abs(AbstractTumbler.displacement)) * 0.6
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: fontMetrics.font.pixelSize * 1.25
+ }
+ width: 50
+ height: 150
+ }
+
+ Tumbler {
+ id: minutesTumbler
+ currentIndex: 6
+ model: 60
+ delegate: delegateComponent
+ width: 50
+ height: 150
+ }
+
+ Tumbler {
+ id: amPmTumbler
+ model: ["AM", "PM"]
+ delegate: delegateComponent
+ width: 50
+ height: 150
+ contentItem: ListView {
+ anchors.fill: parent
+ model: amPmTumbler.model
+ delegate: amPmTumbler.delegate
+
+ snapMode: ListView.SnapToItem
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ preferredHighlightBegin: height / 2 - (height / 3 / 2)
+ preferredHighlightEnd: height / 2 + (height / 3 / 2)
+ clip: true
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/gifs/eventcapturer.cpp b/tests/manual/gifs/eventcapturer.cpp
new file mode 100644
index 00000000..f84d7840
--- /dev/null
+++ b/tests/manual/gifs/eventcapturer.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "eventcapturer.h"
+
+#include <QDebug>
+#include <QMetaEnum>
+#include <QMouseEvent>
+#include <QTimer>
+
+/*!
+ Installs an event filter on a particular object to record specific events
+ that can be retrieved as C++ source code.
+
+ For example:
+
+ \code
+ EventCapturer eventCapturer;
+
+ view.show();
+
+ eventCapturer.startCapturing(&view, 5000);
+
+ // interact with the view here, in order for the events to be captured
+
+ qDebug() << "\n";
+ foreach (CapturedEvent event, eventCapturer.capturedEvents())
+ qDebug().noquote() << event.cppCommand();
+ \endcode
+
+ It is recommended to set the \c Qt::FramelessWindowHint flag on the view
+ (this code has not been tested under other usage):
+
+ view.setFlags(view.flags() | Qt::FramelessWindowHint);
+*/
+
+EventCapturer::EventCapturer(QObject *parent) :
+ QObject(parent),
+ mEventSource(Q_NULLPTR),
+ mStopCaptureKey(Qt::Key_Escape),
+ mMoveEventTrimFlags(TrimNone),
+ mDuration(0),
+ mLastCaptureTime(0)
+{
+ mCapturedEventTypes << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick << QEvent::MouseMove;
+}
+
+void EventCapturer::startCapturing(QObject *eventSource, int duration)
+{
+ mEventSource = eventSource;
+
+ if (!mEventSource)
+ return;
+
+ mEventSource->installEventFilter(this);
+ mDelayTimer.start();
+ mDuration = duration;
+ mLastCaptureTime = 0;
+
+ QTimer::singleShot(mDuration, this, SLOT(stopCapturing()));
+}
+
+void EventCapturer::setStopCaptureKey(Qt::Key stopCaptureKey)
+{
+ mStopCaptureKey = stopCaptureKey;
+}
+
+/*!
+ Move events generate a lot of clutter, and for most cases they're not
+ necessary. Here's a list of scenarios where various trim flags make sense:
+
+ Scenario Flags
+
+ Record the mouse cursor TrimNone
+ Record mouseover/hover effects TrimNone
+ Dragging/flicking TrimAll
+*/
+void EventCapturer::setMoveEventTrimFlags(MoveEventTrimFlags trimFlags)
+{
+ mMoveEventTrimFlags = trimFlags;
+}
+
+QSet<QEvent::Type> EventCapturer::capturedEventTypes()
+{
+ return mCapturedEventTypes;
+}
+
+void EventCapturer::setCapturedEventTypes(QSet<QEvent::Type> types)
+{
+ mCapturedEventTypes = types;
+}
+
+QVector<CapturedEvent> EventCapturer::capturedEvents() const
+{
+ if (mMoveEventTrimFlags == TrimNone || mEvents.isEmpty())
+ return mEvents;
+
+ // We can't easily trim "trailing" move events as they come in without
+ // storing them in some form, so we just do it all here.
+
+ int firstEventIndex = 0;
+ int lastEventIndex = mEvents.size() - 1;
+ // The accumulated delay of all of the move events that we remove.
+ // We keep this in order to maintain the correct timing between events.
+ int accumulatedDelay = 0;
+
+ bool encounteredNonMoveEvent = false;
+ if (mMoveEventTrimFlags.testFlag(TrimLeading)) {
+ for (int eventIndex = 0; !encounteredNonMoveEvent && eventIndex < mEvents.size(); ++eventIndex) {
+ const CapturedEvent event = mEvents.at(eventIndex);
+ if (event.type() != QEvent::MouseMove) {
+ encounteredNonMoveEvent = true;
+ firstEventIndex = eventIndex;
+ } else {
+ accumulatedDelay += event.delay();
+ }
+ }
+ }
+
+ if (mMoveEventTrimFlags.testFlag(TrimTrailing)) {
+ encounteredNonMoveEvent = false;
+ for (int eventIndex = mEvents.size() - 1; !encounteredNonMoveEvent && eventIndex >= 0; --eventIndex) {
+ const CapturedEvent event = mEvents.at(eventIndex);
+ if (event.type() != QEvent::MouseMove) {
+ encounteredNonMoveEvent = true;
+ lastEventIndex = eventIndex;
+ // Don't need to bother with delays for trailing mouse moves, as there is nothing after them.
+ }
+ }
+ }
+
+ // Before we go any further, we need to copy the subset of commands while
+ // the indices are still valid - we could be removing from the middle of
+ // the commands next. Also, the function is const, so we can't remove from
+ // mEvents anyway. :)
+ QVector<CapturedEvent> events = mEvents.mid(firstEventIndex, (lastEventIndex - firstEventIndex) + 1);
+
+ if (mMoveEventTrimFlags.testFlag(TrimAfterReleases)) {
+ bool lastNonMoveEventWasRelease = false;
+ for (int eventIndex = 0; eventIndex < events.size(); ) {
+ CapturedEvent &event = events[eventIndex];
+ if (event.type() == QEvent::MouseMove && lastNonMoveEventWasRelease) {
+ accumulatedDelay += event.delay();
+ events.remove(eventIndex);
+ } else {
+ lastNonMoveEventWasRelease = event.type() == QEvent::MouseButtonRelease;
+ if (event.type() == QEvent::MouseButtonPress) {
+ event.setDelay(event.delay() + accumulatedDelay);
+ accumulatedDelay = 0;
+ }
+ ++eventIndex;
+ }
+ }
+ }
+
+ return events;
+}
+
+bool EventCapturer::eventFilter(QObject *object, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress && static_cast<QKeyEvent*>(event)->key() == mStopCaptureKey) {
+ stopCapturing();
+ return true;
+ }
+
+ if (object != mEventSource)
+ return false;
+
+ if (!mCapturedEventTypes.contains(event->type()))
+ return false;
+
+ if (event->type() == QEvent::MouseButtonPress) {
+ captureEvent(event);
+ } else if (event->type() == QEvent::MouseButtonRelease) {
+ captureEvent(event);
+ } else if (event->type() == QEvent::MouseButtonDblClick) {
+ captureEvent(event);
+ } else if (event->type() == QEvent::MouseMove) {
+ captureEvent(event);
+ } else {
+ qWarning() << "No support for event type" << QMetaEnum::fromType<QEvent::Type>().valueToKey(event->type());
+ }
+ return false;
+}
+
+void EventCapturer::stopCapturing()
+{
+ if (mEventSource) {
+ mEventSource->removeEventFilter(this);
+ mEventSource = 0;
+ mDuration = 0;
+ mLastCaptureTime = 0;
+ }
+}
+
+void EventCapturer::captureEvent(const QEvent *event)
+{
+ CapturedEvent capturedEvent(*event, mDelayTimer.elapsed() - mLastCaptureTime);
+ mEvents.append(capturedEvent);
+ mLastCaptureTime = mDelayTimer.elapsed();
+}
diff --git a/tests/manual/gifs/eventcapturer.h b/tests/manual/gifs/eventcapturer.h
new file mode 100644
index 00000000..a1f48d32
--- /dev/null
+++ b/tests/manual/gifs/eventcapturer.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EVENTCAPTURER_H
+#define EVENTCAPTURER_H
+
+#include <QObject>
+#include <QElapsedTimer>
+#include <QEvent>
+#include <QPoint>
+#include <QSet>
+#include <QVector>
+
+#include "capturedevent.h"
+
+class EventCapturer : public QObject
+{
+ Q_OBJECT
+
+public:
+ EventCapturer(QObject *parent = 0);
+
+ enum MoveEventTrimFlag
+ {
+ TrimNone = 0x0,
+ TrimLeading = 0x1,
+ TrimTrailing = 0x2,
+ TrimAfterReleases = 0x4,
+ TrimAll = TrimLeading | TrimTrailing | TrimAfterReleases
+ };
+
+ Q_DECLARE_FLAGS(MoveEventTrimFlags, MoveEventTrimFlag)
+
+ void setStopCaptureKey(Qt::Key stopCaptureKey);
+ void setMoveEventTrimFlags(MoveEventTrimFlags trimFlags);
+
+ void startCapturing(QObject *eventSource, int duration);
+
+ QSet<QEvent::Type> capturedEventTypes();
+ void setCapturedEventTypes(QSet<QEvent::Type> types);
+
+ QVector<CapturedEvent> capturedEvents() const;
+protected:
+ bool eventFilter(QObject *object, QEvent *event) Q_DECL_OVERRIDE;
+
+private slots:
+ void stopCapturing();
+
+private:
+ void captureEvent(const QEvent *event);
+
+ QObject *mEventSource;
+ QSet<QEvent::Type> mCapturedEventTypes;
+ Qt::Key mStopCaptureKey;
+ MoveEventTrimFlags mMoveEventTrimFlags;
+ QElapsedTimer mDelayTimer;
+ QVector<CapturedEvent> mEvents;
+ int mDuration;
+ int mLastCaptureTime;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(EventCapturer::MoveEventTrimFlags)
+
+#endif // EVENTCAPTURER_H
diff --git a/tests/manual/gifs/gifrecorder.cpp b/tests/manual/gifs/gifrecorder.cpp
new file mode 100644
index 00000000..e684181c
--- /dev/null
+++ b/tests/manual/gifs/gifrecorder.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gifrecorder.h"
+
+#include <QtTest>
+
+/*!
+ QProcess wrapper around byzanz-record (sudo apt-get install byzanz).
+
+ It is recommended to set the \c Qt::FramelessWindowHint flag on the view
+ (this code has not been tested under other usage):
+
+ view.setFlags(view.flags() | Qt::FramelessWindowHint);
+*/
+
+GifRecorder::GifRecorder() :
+ QObject(Q_NULLPTR),
+ mProcessName(QStringLiteral("byzanz-record")),
+ mRecordingDuration(0),
+ mRecordCursor(false),
+ mView(Q_NULLPTR),
+ mStarted(false)
+{
+ // Ensures output from the process goes directly into the console.
+ mProcess.setProcessChannelMode(QProcess::ForwardedChannels);
+
+ connect(&mProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError()));
+ connect(&mProcess, SIGNAL(finished(int)), this, SLOT(onFinished()));
+}
+
+void GifRecorder::setRecordingDuration(int duration)
+{
+ QVERIFY2(duration >= 1, qPrintable(QString::fromLatin1("Recording duration %1 must be larger than 1 second").arg(duration)));
+ QVERIFY2(duration < 10, qPrintable(QString::fromLatin1("Recording duration %1 must be less than 10 seconds").arg(duration)));
+
+ mRecordingDuration = duration;
+}
+
+void GifRecorder::setRecordCursor(bool recordCursor)
+{
+ mRecordCursor = recordCursor;
+}
+
+void GifRecorder::setDataDirPath(const QString &path)
+{
+ QVERIFY2(!path.isEmpty(), "Data directory path cannot be empty");
+ mDataDirPath = path;
+}
+
+void GifRecorder::setOutputDir(const QDir &dir)
+{
+ QVERIFY2(dir.exists(), "Output directory must exist");
+ mOutputDir = dir;
+}
+
+void GifRecorder::setOutputFileName(const QString &fileName)
+{
+ QVERIFY2(!fileName.isEmpty(), "Output file name cannot be empty");
+ mOutputFileName = fileName;
+}
+
+void GifRecorder::setQmlFileName(const QString &fileName)
+{
+ QVERIFY2(!fileName.isEmpty(), "QML file name cannot be empty");
+ mQmlInputFileName = fileName;
+}
+
+void GifRecorder::setView(QQuickView *view)
+{
+ this->mView = view;
+}
+
+void GifRecorder::start()
+{
+ QVERIFY2(mView, "Must have a view to record");
+
+ QDir gifQmlDir(mDataDirPath);
+ QVERIFY(gifQmlDir.entryList().contains(mQmlInputFileName));
+
+ const QString qmlPath = gifQmlDir.absoluteFilePath(mQmlInputFileName);
+ mView->setSource(QUrl::fromLocalFile(qmlPath));
+ QVERIFY(mView->rootObject());
+
+ mView->show();
+ mView->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(mView, 500));
+
+ mOutputFileName = mOutputDir.absoluteFilePath(mQmlInputFileName);
+ mOutputFileName.replace(".qml", ".gif");
+
+ QStringList args;
+ args << "-d" << QString::number(mRecordingDuration) << "-v";
+ if (mRecordCursor)
+ args << "-c";
+ args << "-x" << QString::number(mView->x()) << "-y" << QString::number(mView->y());
+ args << "-w" << QString::number(mView->width()) << "-h" << QString::number(mView->height());
+ args << mOutputFileName;
+ qInfo() << "Starting" << mProcessName << "with the following arguments:" << args;
+ mProcess.start(mProcessName, args);
+
+ if (!mProcess.waitForStarted(1000)) {
+ QString message = QString::fromLatin1("Could not launch %1 with the following arguments: %2\nError:\n%3");
+ message = message.arg(mProcessName).arg(args.join(QLatin1Char(' '))).arg(mProcess.errorString());
+ QFAIL(qPrintable(message));
+ }
+
+ mStarted = true;
+}
+
+bool GifRecorder::hasStarted() const
+{
+ return mStarted;
+}
+
+void GifRecorder::waitForFinish()
+{
+ // Give it an extra couple of seconds on top of its recording duration.
+ QTRY_VERIFY_WITH_TIMEOUT(mFinished, (mRecordingDuration + 2) * 1000);
+
+ if (!QFileInfo::exists(mOutputFileName)) {
+ const QString message = QString::fromLatin1(
+ "The process said it finished successfully, but no GIF was generated.");
+ QFAIL(qPrintable(message));
+ }
+}
+
+void GifRecorder::onError()
+{
+ const QString message = QString::fromLatin1("%1 failed to finish: %2");
+ QFAIL(qPrintable(message.arg(mProcessName).arg(mProcess.errorString())));
+}
+
+void GifRecorder::onFinished()
+{
+ mFinished = true;
+}
diff --git a/tests/manual/gifs/gifrecorder.h b/tests/manual/gifs/gifrecorder.h
new file mode 100644
index 00000000..546c8669
--- /dev/null
+++ b/tests/manual/gifs/gifrecorder.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GIFRECORDER_H
+#define GIFRECORDER_H
+
+#include <QObject>
+#include <QProcess>
+#include <QQuickView>
+#include <QDir>
+#include <QString>
+
+class GifRecorder : public QObject
+{
+ Q_OBJECT
+
+public:
+ GifRecorder();
+
+ void setRecordingDuration(int duration);
+ void setRecordCursor(bool recordCursor);
+ void setDataDirPath(const QString &path);
+ void setOutputDir(const QDir &dir);
+ void setOutputFileName(const QString &fileName);
+ void setQmlFileName(const QString &fileName);
+ void setView(QQuickView *mView);
+
+ void start();
+ bool hasStarted() const;
+ void waitForFinish();
+
+private slots:
+ void onError();
+ void onFinished();
+
+private:
+ const QString mProcessName;
+ QProcess mProcess;
+ int mRecordingDuration;
+ bool mRecordCursor;
+ QString mDataDirPath;
+ QDir mOutputDir;
+ QString mOutputFileName;
+ QString mQmlInputFileName;
+ QQuickView *mView;
+ bool mStarted;
+ bool mFinished;
+};
+
+#endif // GIFRECORDER_H
diff --git a/tests/manual/gifs/gifs.pro b/tests/manual/gifs/gifs.pro
new file mode 100644
index 00000000..197b83d5
--- /dev/null
+++ b/tests/manual/gifs/gifs.pro
@@ -0,0 +1,20 @@
+TEMPLATE = app
+TARGET = tst_gifs
+
+QT += quick testlib
+CONFIG += testcase
+osx:CONFIG -= app_bundle
+
+HEADERS += \
+ $$PWD/gifrecorder.h \
+ $$PWD/eventcapturer.h \
+ capturedevent.h
+
+SOURCES += \
+ $$PWD/tst_gifs.cpp \
+ $$PWD/gifrecorder.cpp \
+ $$PWD/eventcapturer.cpp \
+ capturedevent.cpp
+
+TESTDATA += \
+ $$PWD/data/*
diff --git a/tests/manual/gifs/tst_gifs.cpp b/tests/manual/gifs/tst_gifs.cpp
new file mode 100644
index 00000000..d2ff8d50
--- /dev/null
+++ b/tests/manual/gifs/tst_gifs.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later 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 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QtQuick>
+
+#include "gifrecorder.h"
+#include "eventcapturer.h"
+
+//#define GENERATE_EVENT_CODE
+
+class tst_Gifs : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+
+ void tumblerWrap();
+
+private:
+ QQuickView view;
+ QString dataDirPath;
+ QDir outputDir;
+};
+
+void tst_Gifs::initTestCase()
+{
+ dataDirPath = QFINDTESTDATA("data");
+ QVERIFY(!dataDirPath.isEmpty());
+ qInfo() << "data directory:" << dataDirPath;
+
+ outputDir = QDir(QDir::current().filePath("gifs"));
+ QVERIFY(outputDir.exists() || QDir::current().mkpath("gifs"));
+ qInfo() << "output directory:" << outputDir.absolutePath();
+
+ view.setFlags(view.flags() | Qt::FramelessWindowHint);
+}
+
+void tst_Gifs::tumblerWrap()
+{
+ GifRecorder gifRecorder;
+ gifRecorder.setDataDirPath(dataDirPath);
+ gifRecorder.setOutputDir(outputDir);
+ gifRecorder.setRecordingDuration(4);
+ gifRecorder.setQmlFileName("qtquickextras2-tumbler-wrap.qml");
+ gifRecorder.setOutputFileName("wrap.gif");
+ gifRecorder.setView(&view);
+
+ view.show();
+
+ gifRecorder.start();
+
+ // Left as an example. Usually EventCapturer code would be removed after
+ // the GIF has been generated.
+ EventCapturer eventCapturer;
+#ifdef GENERATE_EVENT_CODE
+ eventCapturer.setMoveEventTrimFlags(EventCapturer::TrimAll);
+ eventCapturer.startCapturing(&view, 4000);
+#else
+ QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, QPoint(89, 75), 326);
+ QTest::mouseMove(&view, QPoint(89, 76), 31);
+ QTest::mouseMove(&view, QPoint(89, 80), 10);
+ QTest::mouseMove(&view, QPoint(93, 93), 10);
+ QTest::mouseMove(&view, QPoint(95, 101), 10);
+ QTest::mouseMove(&view, QPoint(97, 109), 11);
+ QTest::mouseMove(&view, QPoint(101, 125), 10);
+ QTest::mouseMove(&view, QPoint(103, 133), 11);
+ QTest::mouseMove(&view, QPoint(103, 141), 11);
+ QTest::mouseMove(&view, QPoint(105, 158), 10);
+ QTest::mouseMove(&view, QPoint(105, 162), 13);
+ QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, QPoint(105, 162), 0);
+ QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, QPoint(154, 130), 1098);
+ QTest::mouseMove(&view, QPoint(154, 129), 50);
+ QTest::mouseMove(&view, QPoint(153, 128), 0);
+ QTest::mouseMove(&view, QPoint(153, 125), 16);
+ QTest::mouseMove(&view, QPoint(152, 121), 0);
+ QTest::mouseMove(&view, QPoint(152, 117), 17);
+ QTest::mouseMove(&view, QPoint(151, 113), 0);
+ QTest::mouseMove(&view, QPoint(151, 106), 16);
+ QTest::mouseMove(&view, QPoint(150, 99), 1);
+ QTest::mouseMove(&view, QPoint(148, 93), 16);
+ QTest::mouseMove(&view, QPoint(148, 88), 0);
+ QTest::mouseMove(&view, QPoint(148, 84), 17);
+ QTest::mouseMove(&view, QPoint(147, 81), 0);
+ QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, QPoint(147, 81), 0);
+ QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, QPoint(147, 74), 550);
+ QTest::mouseMove(&view, QPoint(147, 75), 17);
+ QTest::mouseMove(&view, QPoint(147, 76), 17);
+ QTest::mouseMove(&view, QPoint(147, 80), 0);
+ QTest::mouseMove(&view, QPoint(148, 85), 16);
+ QTest::mouseMove(&view, QPoint(148, 92), 0);
+ QTest::mouseMove(&view, QPoint(148, 103), 17);
+ QTest::mouseMove(&view, QPoint(150, 119), 17);
+ QTest::mouseMove(&view, QPoint(151, 138), 16);
+ QTest::mouseMove(&view, QPoint(151, 145), 1);
+ QTest::mouseMove(&view, QPoint(153, 151), 16);
+ QTest::mouseMove(&view, QPoint(153, 157), 0);
+ QTest::mouseMove(&view, QPoint(153, 163), 17);
+ QTest::mouseMove(&view, QPoint(153, 167), 0);
+ QTest::mouseMove(&view, QPoint(155, 171), 17);
+ QTest::mouseMove(&view, QPoint(155, 175), 0);
+ QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, QPoint(155, 175), 0);
+#endif
+
+ gifRecorder.waitForFinish();
+
+ foreach (CapturedEvent event, eventCapturer.capturedEvents())
+ qDebug().noquote() << event.cppCommand();
+
+}
+
+QTEST_MAIN(tst_Gifs)
+
+#include "tst_gifs.moc"
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
new file mode 100644
index 00000000..787b99e1
--- /dev/null
+++ b/tests/manual/manual.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS += \
+ gifs