1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#ifndef QQUICKVISUALTESTUTILS_P_H
5#define QQUICKVISUALTESTUTILS_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtGui/private/qguiapplication_p.h>
19#include <QtGui/qpa/qplatformintegration.h>
20#include <QtQml/qqmlexpression.h>
21#include <QtQuick/private/qquickitem_p.h>
22
23#include <private/qmlutils_p.h>
24
25#include <QtCore/qpointer.h>
26
27QT_BEGIN_NAMESPACE
28
29class QQuickItemView;
30class QQuickWindow;
31
32namespace QQuickVisualTestUtils
33{
34 QQuickItem *findVisibleChild(QQuickItem *parent, const QString &objectName);
35
36 void dumpTree(QQuickItem *parent, int depth = 0);
37
38 void moveMouseAway(QQuickWindow *window);
39 void centerOnScreen(QQuickWindow *window);
40
41 template<typename F>
42 void forEachStep(int steps, F &&func)
43 {
44 if (steps == 1) {
45 // that's odd usage, but cut to the chase then
46 func(qreal(1));
47 return;
48 }
49
50 for (int i = 0; i < steps; ++i) {
51 // - 1 because that gives us {0, 0.5, 1} for progress (if steps == 3),
52 // rather than {0, 0.33, 0.66}.
53 const qreal progress = qreal(i) / (steps - 1);
54 func(progress);
55 }
56 }
57
58 [[nodiscard]] QPoint lerpPoints(const QPoint &point1, const QPoint &point2, qreal t);
59
60 class [[nodiscard]] PointLerper
61 {
62 public:
63 PointLerper(QQuickWindow *window,
64 const QPoint &startingPosition = QPoint(0, 0),
65 const QPointingDevice *pointingDevice = QPointingDevice::primaryPointingDevice());
66
67 void move(const QPoint &pos, int steps = 10, int delayInMilliseconds = 1);
68 void move(int x, int y, int steps = 10, int delayInMilliseconds = 1);
69
70 private:
71 QQuickWindow *mWindow = nullptr;
72 const QPointingDevice *mPointingDevice = nullptr;
73 QPoint mFrom;
74 };
75
76 [[nodiscard]] bool isDelegateVisible(QQuickItem *item);
77
78 /*
79 Find an item with the specified objectName. If index is supplied then the
80 item must also evaluate the {index} expression equal to index
81 */
82 template<typename T>
83 T *findItem(QQuickItem *parent, const QString &objectName, int index = -1)
84 {
85 using namespace Qt::StringLiterals;
86
87 const QMetaObject &mo = T::staticMetaObject;
88 for (int i = 0; i < parent->childItems().size(); ++i) {
89 QQuickItem *item = qobject_cast<QQuickItem*>(o: parent->childItems().at(i));
90 if (!item)
91 continue;
92 if (mo.cast(obj: item) && (objectName.isEmpty() || item->objectName() == objectName)) {
93 if (index != -1) {
94 QQmlContext *context = qmlContext(item);
95 if (!context->isValid())
96 continue;
97 QQmlExpression e(context, item, u"index"_s);
98 if (e.evaluate().toInt() == index)
99 return static_cast<T*>(item);
100 } else {
101 return static_cast<T*>(item);
102 }
103 }
104 item = findItem<T>(item, objectName, index);
105 if (item)
106 return static_cast<T*>(item);
107 }
108
109 return 0;
110 }
111
112 template<typename T>
113 QList<T*> findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly = true)
114 {
115 QList<T*> items;
116 const QMetaObject &mo = T::staticMetaObject;
117 for (int i = 0; i < parent->childItems().size(); ++i) {
118 QQuickItem *item = qobject_cast<QQuickItem*>(o: parent->childItems().at(i));
119 if (!item || (visibleOnly && (!item->isVisible() || QQuickItemPrivate::get(item)->culled)))
120 continue;
121 if (mo.cast(obj: item) && (objectName.isEmpty() || item->objectName() == objectName))
122 items.append(static_cast<T*>(item));
123 items += findItems<T>(item, objectName);
124 }
125
126 return items;
127 }
128
129 template<typename T>
130 QList<T*> findItems(QQuickItem *parent, const QString &objectName, const QList<int> &indexes)
131 {
132 QList<T*> items;
133 for (int i=0; i<indexes.size(); i++)
134 items << qobject_cast<QQuickItem*>(findItem<T>(parent, objectName, indexes[i]));
135 return items;
136 }
137
138 bool compareImages(const QImage &ia, const QImage &ib, QString *errorMessage);
139
140 struct SignalMultiSpy : public QObject
141 {
142 Q_OBJECT
143 public:
144 QList<QObject *> senders;
145 QList<QByteArray> signalNames;
146
147 template <typename Func1>
148 QMetaObject::Connection connectToSignal(const typename QtPrivate::FunctionPointer<Func1>::Object *obj, Func1 signal,
149 Qt::ConnectionType type = Qt::AutoConnection)
150 {
151 return connect(obj, signal, this, &SignalMultiSpy::receive, type);
152 }
153
154 void clear() {
155 senders.clear();
156 signalNames.clear();
157 }
158
159 public Q_SLOTS:
160 void receive() {
161 QMetaMethod m = sender()->metaObject()->method(index: senderSignalIndex());
162 senders << sender();
163 signalNames << m.name();
164 }
165 };
166
167 enum class FindViewDelegateItemFlag {
168 None = 0x0,
169 PositionViewAtIndex = 0x01
170 };
171 Q_DECLARE_FLAGS(FindViewDelegateItemFlags, FindViewDelegateItemFlag)
172
173#if QT_CONFIG(quick_itemview)
174 QQuickItem* findViewDelegateItem(QQuickItemView *itemView, int index,
175 FindViewDelegateItemFlags flags = FindViewDelegateItemFlag::PositionViewAtIndex);
176#endif
177
178 /*!
179 \internal
180
181 Same as above except allows use in QTRY_* functions without having to call it again
182 afterwards to assign the delegate.
183 */
184 template<typename T>
185 [[nodiscard]] bool findViewDelegateItem(QQuickItemView *itemView, int index, T &delegateItem,
186 FindViewDelegateItemFlags flags = FindViewDelegateItemFlag::PositionViewAtIndex)
187 {
188 delegateItem = qobject_cast<T>(findViewDelegateItem(itemView, index, flags));
189 return delegateItem != nullptr;
190 }
191
192 class QQuickApplicationHelper
193 {
194 public:
195 QQuickApplicationHelper(QQmlDataTest *testCase, const QString &testFilePath,
196 const QVariantMap &initialProperties = {},
197 const QStringList &qmlImportPaths = {});
198
199 // Return a C-style string instead of QString because that's what QTest uses for error messages,
200 // so it saves code at the calling site.
201 inline const char *failureMessage() const
202 {
203 return errorMessage.constData();
204 }
205
206 QQmlEngine engine;
207 QScopedPointer<QObject> cleanup;
208 QQuickWindow *window = nullptr;
209
210 bool ready = false;
211 // Store as a byte array so that we can return its raw data safely;
212 // using qPrintable() in failureMessage() will construct a throwaway QByteArray
213 // that is destroyed before the function returns.
214 QByteArray errorMessage;
215 };
216
217 class MnemonicKeySimulator
218 {
219 Q_DISABLE_COPY(MnemonicKeySimulator)
220 public:
221 explicit MnemonicKeySimulator(QWindow *window);
222
223 void press(Qt::Key key);
224 void release(Qt::Key key);
225 void click(Qt::Key key);
226
227 private:
228 QPointer<QWindow> m_window;
229 Qt::KeyboardModifiers m_modifiers;
230 };
231
232 QPoint mapCenterToWindow(const QQuickItem *item);
233 QPoint mapToWindow(const QQuickItem *item, qreal relativeX, qreal relativeY);
234 QPoint mapToWindow(const QQuickItem *item, const QPointF &relativePos);
235}
236
237#define SKIP_IF_NO_WINDOW_ACTIVATION \
238do { \
239 if (!(QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) \
240 QSKIP("Window activation is not supported on this platform"); \
241} while (false)
242
243#define SKIP_IF_NO_WINDOW_GRAB \
244do { \
245 if (QGuiApplication::platformName() == QLatin1String("minimal")) \
246 QSKIP("grabWindow is not supported on the minimal platform"); \
247} while (false)
248
249#define SKIP_IF_NO_MOUSE_HOVER \
250do { \
251 if ((QGuiApplication::platformName() == QLatin1String("offscreen")) \
252 || (QGuiApplication::platformName() == QLatin1String("minimal"))) \
253 QSKIP("Mouse hovering is not supported on the offscreen/minimal platforms"); \
254} while (false)
255
256QT_END_NAMESPACE
257
258#endif // QQUICKVISUALTESTUTILS_P_H
259

source code of qtdeclarative/src/quicktestutils/quick/visualtestutils_p.h