1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <qtest.h> |
30 | #include <QtQml/qqmlcomponent.h> |
31 | #include <QtQml/qqmlengine.h> |
32 | #include <QtQuick/qquickitem.h> |
33 | #include <QtQuick/qquickview.h> |
34 | #include <QtGui/qinputmethod.h> |
35 | #include <QtGui/qstylehints.h> |
36 | #include <qpa/qwindowsysteminterface.h> |
37 | #include <qpa/qplatformintegration.h> |
38 | #include <private/qguiapplication_p.h> |
39 | #include "../../shared/util.h" |
40 | |
41 | class tst_qquickapplication : public QQmlDataTest |
42 | { |
43 | Q_OBJECT |
44 | public: |
45 | tst_qquickapplication(); |
46 | |
47 | private slots: |
48 | void active(); |
49 | void state(); |
50 | void layoutDirection(); |
51 | void font(); |
52 | void inputMethod(); |
53 | void styleHints(); |
54 | void cleanup(); |
55 | void displayName(); |
56 | void platformName(); |
57 | |
58 | private: |
59 | QQmlEngine engine; |
60 | }; |
61 | |
62 | tst_qquickapplication::tst_qquickapplication() |
63 | { |
64 | } |
65 | |
66 | void tst_qquickapplication::cleanup() |
67 | { |
68 | if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::ApplicationState)) { |
69 | QWindowSystemInterface::handleApplicationStateChanged(newState: Qt::ApplicationInactive); |
70 | QCoreApplication::processEvents(); |
71 | } |
72 | } |
73 | |
74 | void tst_qquickapplication::active() |
75 | { |
76 | QQmlComponent component(&engine); |
77 | component.setData("import QtQuick 2.0; " |
78 | "Item { " |
79 | " property bool active: Qt.application.active; " |
80 | " property bool active2: false; " |
81 | " Connections { " |
82 | " target: Qt.application; " |
83 | " onActiveChanged: active2 = Qt.application.active; " |
84 | " } " |
85 | "}" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
86 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
87 | QVERIFY(item); |
88 | QQuickWindow window; |
89 | item->setParentItem(window.contentItem()); |
90 | |
91 | // If the platform plugin has the ApplicationState capability, app activation originate from it |
92 | // as a result of a system event. We therefore have to simulate these events here. |
93 | if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::ApplicationState)) { |
94 | |
95 | // Flush pending events, in case the platform have already queued real application state events |
96 | QWindowSystemInterface::flushWindowSystemEvents(); |
97 | |
98 | QWindowSystemInterface::handleApplicationStateChanged(newState: Qt::ApplicationActive); |
99 | QWindowSystemInterface::flushWindowSystemEvents(); |
100 | QVERIFY(item->property("active" ).toBool()); |
101 | QVERIFY(item->property("active2" ).toBool()); |
102 | |
103 | QWindowSystemInterface::handleApplicationStateChanged(newState: Qt::ApplicationInactive); |
104 | QWindowSystemInterface::flushWindowSystemEvents(); |
105 | QVERIFY(!item->property("active" ).toBool()); |
106 | QVERIFY(!item->property("active2" ).toBool()); |
107 | } else { |
108 | // Otherwise, app activation is triggered by window activation. |
109 | window.show(); |
110 | window.requestActivate(); |
111 | QVERIFY(QTest::qWaitForWindowActive(&window)); |
112 | QCOMPARE(QGuiApplication::focusWindow(), &window); |
113 | QVERIFY(item->property("active" ).toBool()); |
114 | QVERIFY(item->property("active2" ).toBool()); |
115 | |
116 | // not active again |
117 | QWindowSystemInterface::handleWindowActivated(window: nullptr); |
118 | QTRY_VERIFY(QGuiApplication::focusWindow() != &window); |
119 | QVERIFY(!item->property("active" ).toBool()); |
120 | QVERIFY(!item->property("active2" ).toBool()); |
121 | } |
122 | } |
123 | |
124 | void tst_qquickapplication::state() |
125 | { |
126 | QQmlComponent component(&engine); |
127 | component.setData("import QtQuick 2.0; " |
128 | "Item { " |
129 | " property int state: Qt.application.state; " |
130 | " property int state2: Qt.ApplicationInactive; " |
131 | " Connections { " |
132 | " target: Qt.application; " |
133 | " onStateChanged: state2 = Qt.application.state; " |
134 | " } " |
135 | " Component.onCompleted: state2 = Qt.application.state; " |
136 | "}" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
137 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
138 | QVERIFY(item); |
139 | QQuickWindow window; |
140 | item->setParentItem(window.contentItem()); |
141 | |
142 | // If the platform plugin has the ApplicationState capability, state changes originate from it |
143 | // as a result of a system event. We therefore have to simulate these events here. |
144 | if (QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::ApplicationState)) { |
145 | |
146 | // Flush pending events, in case the platform have already queued real application state events |
147 | QWindowSystemInterface::flushWindowSystemEvents(); |
148 | |
149 | QWindowSystemInterface::handleApplicationStateChanged(newState: Qt::ApplicationActive); |
150 | QWindowSystemInterface::flushWindowSystemEvents(); |
151 | QCOMPARE(Qt::ApplicationState(item->property("state" ).toInt()), Qt::ApplicationActive); |
152 | QCOMPARE(Qt::ApplicationState(item->property("state2" ).toInt()), Qt::ApplicationActive); |
153 | |
154 | QWindowSystemInterface::handleApplicationStateChanged(newState: Qt::ApplicationInactive); |
155 | QWindowSystemInterface::flushWindowSystemEvents(); |
156 | QCOMPARE(Qt::ApplicationState(item->property("state" ).toInt()), Qt::ApplicationInactive); |
157 | QCOMPARE(Qt::ApplicationState(item->property("state2" ).toInt()), Qt::ApplicationInactive); |
158 | |
159 | QWindowSystemInterface::handleApplicationStateChanged(newState: Qt::ApplicationSuspended); |
160 | QWindowSystemInterface::flushWindowSystemEvents(); |
161 | QCOMPARE(Qt::ApplicationState(item->property("state" ).toInt()), Qt::ApplicationSuspended); |
162 | QCOMPARE(Qt::ApplicationState(item->property("state2" ).toInt()), Qt::ApplicationSuspended); |
163 | |
164 | QWindowSystemInterface::handleApplicationStateChanged(newState: Qt::ApplicationHidden); |
165 | QWindowSystemInterface::flushWindowSystemEvents(); |
166 | QCOMPARE(Qt::ApplicationState(item->property("state" ).toInt()), Qt::ApplicationHidden); |
167 | QCOMPARE(Qt::ApplicationState(item->property("state2" ).toInt()), Qt::ApplicationHidden); |
168 | |
169 | } else { |
170 | // Otherwise, the application can only be in two states, Active and Inactive. These are |
171 | // triggered by window activation. |
172 | window.show(); |
173 | window.requestActivate(); |
174 | QVERIFY(QTest::qWaitForWindowActive(&window)); |
175 | QCOMPARE(QGuiApplication::focusWindow(), &window); |
176 | QCOMPARE(Qt::ApplicationState(item->property("state" ).toInt()), Qt::ApplicationActive); |
177 | QCOMPARE(Qt::ApplicationState(item->property("state2" ).toInt()), Qt::ApplicationActive); |
178 | |
179 | // not active again |
180 | QWindowSystemInterface::handleWindowActivated(window: nullptr); |
181 | QTRY_VERIFY(QGuiApplication::focusWindow() != &window); |
182 | QCOMPARE(Qt::ApplicationState(item->property("state" ).toInt()), Qt::ApplicationInactive); |
183 | QCOMPARE(Qt::ApplicationState(item->property("state2" ).toInt()), Qt::ApplicationInactive); |
184 | } |
185 | } |
186 | |
187 | void tst_qquickapplication::layoutDirection() |
188 | { |
189 | |
190 | QQmlComponent component(&engine); |
191 | component.setData("import QtQuick 2.0; Item { property bool layoutDirection: Qt.application.layoutDirection }" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
192 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
193 | QVERIFY(item); |
194 | QQuickView view; |
195 | item->setParentItem(view.rootObject()); |
196 | |
197 | // not mirrored |
198 | QCOMPARE(Qt::LayoutDirection(item->property("layoutDirection" ).toInt()), Qt::LeftToRight); |
199 | |
200 | // mirrored |
201 | QGuiApplication::setLayoutDirection(Qt::RightToLeft); |
202 | QCOMPARE(Qt::LayoutDirection(item->property("layoutDirection" ).toInt()), Qt::RightToLeft); |
203 | |
204 | // not mirrored again |
205 | QGuiApplication::setLayoutDirection(Qt::LeftToRight); |
206 | QCOMPARE(Qt::LayoutDirection(item->property("layoutDirection" ).toInt()), Qt::LeftToRight); |
207 | } |
208 | |
209 | void tst_qquickapplication::font() |
210 | { |
211 | QQmlComponent component(&engine); |
212 | component.setData("import QtQuick 2.0; Item { property font defaultFont: Qt.application.font }" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
213 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
214 | QVERIFY(item); |
215 | QQuickView view; |
216 | item->setParentItem(view.rootObject()); |
217 | |
218 | QVariant defaultFontProperty = item->property(name: "defaultFont" ); |
219 | QVERIFY(defaultFontProperty.isValid()); |
220 | QCOMPARE(defaultFontProperty.type(), QVariant::Font); |
221 | QCOMPARE(defaultFontProperty.value<QFont>(), qApp->font()); |
222 | } |
223 | |
224 | void tst_qquickapplication::inputMethod() |
225 | { |
226 | // technically not in QQuickApplication, but testing anyway here |
227 | QQmlComponent component(&engine); |
228 | component.setData("import QtQuick 2.0; Item { property variant inputMethod: Qt.inputMethod }" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
229 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
230 | QVERIFY(item); |
231 | QQuickView view; |
232 | item->setParentItem(view.rootObject()); |
233 | |
234 | // check that the inputMethod property maches with application's input method |
235 | QCOMPARE(qvariant_cast<QObject*>(item->property("inputMethod" )), qApp->inputMethod()); |
236 | } |
237 | |
238 | void tst_qquickapplication::styleHints() |
239 | { |
240 | // technically not in QQuickApplication, but testing anyway here |
241 | QQmlComponent component(&engine); |
242 | component.setData("import QtQuick 2.0; Item { property variant styleHints: Qt.styleHints }" , baseUrl: QUrl::fromLocalFile(localfile: "" )); |
243 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
244 | QVERIFY(item); |
245 | QQuickView view; |
246 | item->setParentItem(view.rootObject()); |
247 | |
248 | QCOMPARE(qvariant_cast<QObject*>(item->property("styleHints" )), qApp->styleHints()); |
249 | } |
250 | |
251 | void tst_qquickapplication::displayName() |
252 | { |
253 | QString name[3] = { QStringLiteral("APP NAME 0" ), |
254 | QStringLiteral("APP NAME 1" ), |
255 | QStringLiteral("APP NAME 2" ) |
256 | }; |
257 | |
258 | QQmlComponent component(&engine, testFileUrl(fileName: "tst_displayname.qml" )); |
259 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
260 | QVERIFY(item); |
261 | QQuickView view; |
262 | item->setParentItem(view.rootObject()); |
263 | |
264 | QCoreApplication::setApplicationName(name[0]); |
265 | QCOMPARE(qvariant_cast<QString>(item->property("displayName" )), name[0]); |
266 | |
267 | QGuiApplication::setApplicationName(name[1]); |
268 | QCOMPARE(qvariant_cast<QString>(item->property("displayName" )), name[1]); |
269 | |
270 | QMetaObject::invokeMethod(obj: item, member: "updateDisplayName" , Q_ARG(QVariant, QVariant(name[2]))); |
271 | QCOMPARE(QGuiApplication::applicationDisplayName(), name[2]); |
272 | } |
273 | |
274 | void tst_qquickapplication::platformName() |
275 | { |
276 | // Set up QML component |
277 | QQmlComponent component(&engine, testFileUrl(fileName: "tst_platformname.qml" )); |
278 | QQuickItem *item = qobject_cast<QQuickItem *>(object: component.create()); |
279 | QVERIFY(item); |
280 | QQuickView view; |
281 | item->setParentItem(view.rootObject()); |
282 | |
283 | // Get native platform name |
284 | QString guiApplicationPlatformName = QGuiApplication::platformName(); |
285 | QVERIFY(!guiApplicationPlatformName.isEmpty()); |
286 | |
287 | // Get platform name from QML component and verify it's same |
288 | QString qmlPlatformName = qvariant_cast<QString>(v: item->property(name: "platformName" )); |
289 | QCOMPARE(qmlPlatformName, guiApplicationPlatformName); |
290 | } |
291 | |
292 | QTEST_MAIN(tst_qquickapplication) |
293 | |
294 | #include "tst_qquickapplication.moc" |
295 | |