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