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 QtQuick module 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 <QtTest/qtest.h>
30#include <QtQuick/qquickwindow.h>
31#include <QtQml/qqmlapplicationengine.h>
32#include <QtQuick/qquickitem.h>
33#include <QtTest/qsignalspy.h>
34#ifdef QT_QUICKWIDGETS_LIB
35#include <QtQuickWidgets/qquickwidget.h>
36#endif
37
38#include "../../shared/util.h"
39
40class tst_QQuickShortcut : public QQmlDataTest
41{
42 Q_OBJECT
43
44private slots:
45 void standardShortcuts_data();
46 void standardShortcuts();
47 void shortcuts_data();
48 void shortcuts();
49 void sequence_data();
50 void sequence();
51 void context_data();
52 void context();
53 void contextChange_data();
54 void contextChange();
55 void matcher_data();
56 void matcher();
57 void multiple_data();
58 void multiple();
59#ifdef QT_QUICKWIDGETS_LIB
60 void renderControlShortcuts_data();
61 void renderControlShortcuts();
62#endif
63};
64
65Q_DECLARE_METATYPE(Qt::Key)
66Q_DECLARE_METATYPE(Qt::KeyboardModifiers)
67
68static const bool EnabledShortcut = true;
69static const bool DisabledShortcut = false;
70
71static QVariant shortcutMap(const QVariant &sequence, Qt::ShortcutContext context, bool enabled = true, bool autoRepeat = true)
72{
73 QVariantMap s;
74 s["sequence"] = sequence;
75 s["enabled"] = enabled;
76 s["context"] = context;
77 s["autoRepeat"] = autoRepeat;
78 return s;
79}
80
81static QVariant shortcutMap(const QVariant &key, bool enabled = true, bool autoRepeat = true)
82{
83 return shortcutMap(sequence: key, context: Qt::WindowShortcut, enabled, autoRepeat);
84}
85
86void tst_QQuickShortcut::standardShortcuts_data()
87{
88 QTest::addColumn<QKeySequence::StandardKey>(name: "standardKey");
89 QTest::newRow(dataTag: "Close") << QKeySequence::Close;
90 QTest::newRow(dataTag: "Cut") << QKeySequence::Cut;
91 QTest::newRow(dataTag: "NextChild") << QKeySequence::NextChild;
92 QTest::newRow(dataTag: "PreviousChild") << QKeySequence::PreviousChild;
93 QTest::newRow(dataTag: "FindNext") << QKeySequence::FindNext;
94 QTest::newRow(dataTag: "FindPrevious") << QKeySequence::FindPrevious;
95 QTest::newRow(dataTag: "FullScreen") << QKeySequence::FullScreen;
96}
97
98void tst_QQuickShortcut::standardShortcuts()
99{
100 QFETCH(QKeySequence::StandardKey, standardKey);
101
102 QQmlApplicationEngine engine;
103
104 engine.load(url: testFileUrl(fileName: "multiple.qml"));
105 QQuickWindow *window = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 0));
106 QVERIFY(window);
107 window->show();
108 QVERIFY(QTest::qWaitForWindowExposed(window));
109 window->requestActivate();
110 QVERIFY(QTest::qWaitForWindowActive(window));
111
112 QObject *shortcut = window->property(name: "shortcut").value<QObject *>();
113 QVERIFY(shortcut);
114
115 // create list of shortcuts
116 QVariantList shortcuts;
117 shortcuts.push_back(t: standardKey);
118 shortcut->setProperty(name: "sequences", value: shortcuts);
119
120 // test all:
121 QList<QKeySequence> allsequences = QKeySequence::keyBindings(key: standardKey);
122 for (const QKeySequence &s : allsequences) {
123 window->setProperty(name: "activated", value: false);
124 QTest::keySequence(window, keySequence: s);
125 QCOMPARE(window->property("activated").toBool(), true);
126 }
127}
128
129void tst_QQuickShortcut::shortcuts_data()
130{
131 QTest::addColumn<QVariantList>(name: "shortcuts");
132 QTest::addColumn<Qt::Key>(name: "key");
133 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers");
134 QTest::addColumn<QString>(name: "activatedShortcut");
135 QTest::addColumn<QString>(name: "ambiguousShortcut");
136
137 QVariantList shortcuts;
138 shortcuts << shortcutMap(key: "M")
139 << shortcutMap(key: "Alt+M")
140 << shortcutMap(key: "Ctrl+M")
141 << shortcutMap(key: "Shift+M")
142 << shortcutMap(key: "Ctrl+Alt+M")
143 << shortcutMap(key: "+")
144 << shortcutMap(key: "F1")
145 << shortcutMap(key: "Shift+F1");
146
147 QTest::newRow(dataTag: "M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "M" << "";
148 QTest::newRow(dataTag: "Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "Alt+M" << "";
149 QTest::newRow(dataTag: "Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "Ctrl+M" << "";
150 QTest::newRow(dataTag: "Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+M" << "";
151 QTest::newRow(dataTag: "Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "Ctrl+Alt+M" << "";
152 QTest::newRow(dataTag: "+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "+" << "";
153 QTest::newRow(dataTag: "F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "F1" << "";
154 QTest::newRow(dataTag: "Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+F1" << "";
155
156 // ambiguous
157 shortcuts << shortcutMap(key: "M")
158 << shortcutMap(key: "Alt+M")
159 << shortcutMap(key: "Ctrl+M")
160 << shortcutMap(key: "Shift+M")
161 << shortcutMap(key: "Ctrl+Alt+M")
162 << shortcutMap(key: "+")
163 << shortcutMap(key: "F1")
164 << shortcutMap(key: "Shift+F1");
165
166 QTest::newRow(dataTag: "?M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "M";
167 QTest::newRow(dataTag: "?Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "" << "Alt+M";
168 QTest::newRow(dataTag: "?Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "" << "Ctrl+M";
169 QTest::newRow(dataTag: "?Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "Shift+M";
170 QTest::newRow(dataTag: "?Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "" << "Ctrl+Alt+M";
171 QTest::newRow(dataTag: "?+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "+";
172 QTest::newRow(dataTag: "?F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "F1";
173 QTest::newRow(dataTag: "?Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "Shift+F1";
174
175 // disabled
176 shortcuts.clear();
177 shortcuts << shortcutMap(key: "M", enabled: DisabledShortcut)
178 << shortcutMap(key: "Alt+M", enabled: DisabledShortcut)
179 << shortcutMap(key: "Ctrl+M", enabled: DisabledShortcut)
180 << shortcutMap(key: "Shift+M", enabled: DisabledShortcut)
181 << shortcutMap(key: "Ctrl+Alt+M", enabled: DisabledShortcut)
182 << shortcutMap(key: "+", enabled: DisabledShortcut)
183 << shortcutMap(key: "F1", enabled: DisabledShortcut)
184 << shortcutMap(key: "Shift+F1", enabled: DisabledShortcut);
185
186 QTest::newRow(dataTag: "!M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "";
187 QTest::newRow(dataTag: "!Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "" << "";
188 QTest::newRow(dataTag: "!Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "" << "";
189 QTest::newRow(dataTag: "!Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "";
190 QTest::newRow(dataTag: "!Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "" << "";
191 QTest::newRow(dataTag: "!+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "";
192 QTest::newRow(dataTag: "!F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "";
193 QTest::newRow(dataTag: "!Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "";
194
195 // unambigous because others disabled
196 shortcuts << shortcutMap(key: "M")
197 << shortcutMap(key: "Alt+M")
198 << shortcutMap(key: "Ctrl+M")
199 << shortcutMap(key: "Shift+M")
200 << shortcutMap(key: "Ctrl+Alt+M")
201 << shortcutMap(key: "+")
202 << shortcutMap(key: "F1")
203 << shortcutMap(key: "Shift+F1");
204
205 QTest::newRow(dataTag: "/M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "M" << "";
206 QTest::newRow(dataTag: "/Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "Alt+M" << "";
207 QTest::newRow(dataTag: "/Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "Ctrl+M" << "";
208 QTest::newRow(dataTag: "/Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+M" << "";
209 QTest::newRow(dataTag: "/Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "Ctrl+Alt+M" << "";
210 QTest::newRow(dataTag: "/+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "+" << "";
211 QTest::newRow(dataTag: "/F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "F1" << "";
212 QTest::newRow(dataTag: "/Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+F1" << "";
213}
214
215void tst_QQuickShortcut::shortcuts()
216{
217 QFETCH(QVariantList, shortcuts);
218 QFETCH(Qt::Key, key);
219 QFETCH(Qt::KeyboardModifiers, modifiers);
220 QFETCH(QString, activatedShortcut);
221 QFETCH(QString, ambiguousShortcut);
222
223 QQmlApplicationEngine engine;
224 engine.load(url: testFileUrl(fileName: "shortcuts.qml"));
225
226 QQuickWindow *window = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 0));
227 QVERIFY(window);
228 window->show();
229 QVERIFY(QTest::qWaitForWindowExposed(window));
230 window->requestActivate();
231 QVERIFY(QTest::qWaitForWindowActive(window));
232
233 window->setProperty(name: "shortcuts", value: shortcuts);
234
235 QTest::keyPress(window, key, modifier: modifiers);
236 QTest::keyRelease(window, key, modifier: modifiers);
237 QCOMPARE(window->property("activatedShortcut").toString(), activatedShortcut);
238 QCOMPARE(window->property("ambiguousShortcut").toString(), ambiguousShortcut);
239}
240
241void tst_QQuickShortcut::sequence_data()
242{
243 QTest::addColumn<QVariantList>(name: "shortcuts");
244 QTest::addColumn<Qt::Key>(name: "key1");
245 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers1");
246 QTest::addColumn<Qt::Key>(name: "key2");
247 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers2");
248 QTest::addColumn<Qt::Key>(name: "key3");
249 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers3");
250 QTest::addColumn<Qt::Key>(name: "key4");
251 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers4");
252 QTest::addColumn<QString>(name: "activatedShortcut");
253 QTest::addColumn<QString>(name: "ambiguousShortcut");
254
255 QVariantList shortcuts;
256 shortcuts << shortcutMap(key: "Escape,W")
257 << shortcutMap(key: "Ctrl+X,Ctrl+C")
258 << shortcutMap(key: "Shift+Ctrl+4,Space")
259 << shortcutMap(key: "Alt+T,Ctrl+R,Shift+J,H");
260
261 QTest::newRow(dataTag: "Escape,W") << shortcuts << Qt::Key_Escape << Qt::KeyboardModifiers(Qt::NoModifier)
262 << Qt::Key_W << Qt::KeyboardModifiers(Qt::NoModifier)
263 << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier)
264 << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier)
265 << "Escape,W" << "";
266
267 QTest::newRow(dataTag: "Ctrl+X,Ctrl+C") << shortcuts << Qt::Key_X << Qt::KeyboardModifiers(Qt::ControlModifier)
268 << Qt::Key_C << Qt::KeyboardModifiers(Qt::ControlModifier)
269 << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier)
270 << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier)
271 << "Ctrl+X,Ctrl+C" << "";
272
273 QTest::newRow(dataTag: "Shift+Ctrl+4,Space") << shortcuts << Qt::Key_4 << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::ShiftModifier)
274 << Qt::Key_Space << Qt::KeyboardModifiers(Qt::NoModifier)
275 << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier)
276 << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier)
277 << "Shift+Ctrl+4,Space" << "";
278
279 QTest::newRow(dataTag: "Alt+T,Ctrl+R,Shift+J,H") << shortcuts << Qt::Key_T << Qt::KeyboardModifiers(Qt::AltModifier)
280 << Qt::Key_R << Qt::KeyboardModifiers(Qt::ControlModifier)
281 << Qt::Key_J << Qt::KeyboardModifiers(Qt::ShiftModifier)
282 << Qt::Key_H << Qt::KeyboardModifiers(Qt::NoModifier)
283 << "Alt+T,Ctrl+R,Shift+J,H" << "";
284}
285
286void tst_QQuickShortcut::sequence()
287{
288 QFETCH(QVariantList, shortcuts);
289 QFETCH(Qt::Key, key1);
290 QFETCH(Qt::KeyboardModifiers, modifiers1);
291 QFETCH(Qt::Key, key2);
292 QFETCH(Qt::KeyboardModifiers, modifiers2);
293 QFETCH(Qt::Key, key3);
294 QFETCH(Qt::KeyboardModifiers, modifiers3);
295 QFETCH(Qt::Key, key4);
296 QFETCH(Qt::KeyboardModifiers, modifiers4);
297 QFETCH(QString, activatedShortcut);
298 QFETCH(QString, ambiguousShortcut);
299
300 QQmlApplicationEngine engine;
301 engine.load(url: testFileUrl(fileName: "shortcuts.qml"));
302
303 QQuickWindow *window = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 0));
304 QVERIFY(window);
305 window->show();
306 QVERIFY(QTest::qWaitForWindowExposed(window));
307 window->requestActivate();
308 QVERIFY(QTest::qWaitForWindowActive(window));
309
310 window->setProperty(name: "shortcuts", value: shortcuts);
311
312 if (key1 != 0) {
313 QTest::keyPress(window, key: key1, modifier: modifiers1);
314 QTest::keyRelease(window, key: key1, modifier: modifiers1);
315 }
316 if (key2 != 0) {
317 QTest::keyPress(window, key: key2, modifier: modifiers2);
318 QTest::keyRelease(window, key: key2, modifier: modifiers2);
319 }
320 if (key3 != 0) {
321 QTest::keyPress(window, key: key3, modifier: modifiers3);
322 QTest::keyRelease(window, key: key3, modifier: modifiers3);
323 }
324 if (key4 != 0) {
325 QTest::keyPress(window, key: key4, modifier: modifiers4);
326 QTest::keyRelease(window, key: key4, modifier: modifiers4);
327 }
328
329 QCOMPARE(window->property("activatedShortcut").toString(), activatedShortcut);
330 QCOMPARE(window->property("ambiguousShortcut").toString(), ambiguousShortcut);
331}
332
333void tst_QQuickShortcut::context_data()
334{
335 QTest::addColumn<Qt::Key>(name: "key");
336 QTest::addColumn<QVariantList>(name: "activeWindowShortcuts");
337 QTest::addColumn<QVariantList>(name: "inactiveWindowShortcuts");
338 QTest::addColumn<QString>(name: "activeWindowActivatedShortcut");
339 QTest::addColumn<QString>(name: "inactiveWindowActivatedShortcut");
340 QTest::addColumn<QString>(name: "ambiguousShortcut");
341
342 // APP: F1,(F2),F3,(F4) / WND: F7,(F8),F9,(F10)
343 QVariantList activeWindowShortcuts;
344 activeWindowShortcuts << shortcutMap(sequence: "F1", context: Qt::ApplicationShortcut, enabled: EnabledShortcut)
345 << shortcutMap(sequence: "F2", context: Qt::ApplicationShortcut, enabled: DisabledShortcut)
346 << shortcutMap(sequence: "F3", context: Qt::ApplicationShortcut, enabled: EnabledShortcut)
347 << shortcutMap(sequence: "F4", context: Qt::ApplicationShortcut, enabled: DisabledShortcut)
348 << shortcutMap(sequence: "F7", context: Qt::WindowShortcut, enabled: EnabledShortcut)
349 << shortcutMap(sequence: "F8", context: Qt::WindowShortcut, enabled: DisabledShortcut)
350 << shortcutMap(sequence: "F9", context: Qt::WindowShortcut, enabled: EnabledShortcut)
351 << shortcutMap(sequence: "F10", context: Qt::WindowShortcut, enabled: DisabledShortcut);
352
353 // APP: F3,(F4),F5,(F6) / WND: F9,(F10),F11(F12)
354 QVariantList inactiveWindowShortcuts;
355 inactiveWindowShortcuts << shortcutMap(sequence: "F3", context: Qt::ApplicationShortcut, enabled: EnabledShortcut)
356 << shortcutMap(sequence: "F4", context: Qt::ApplicationShortcut, enabled: DisabledShortcut)
357 << shortcutMap(sequence: "F5", context: Qt::ApplicationShortcut, enabled: EnabledShortcut)
358 << shortcutMap(sequence: "F6", context: Qt::ApplicationShortcut, enabled: DisabledShortcut)
359 << shortcutMap(sequence: "F9", context: Qt::WindowShortcut, enabled: EnabledShortcut)
360 << shortcutMap(sequence: "F10", context: Qt::WindowShortcut, enabled: DisabledShortcut)
361 << shortcutMap(sequence: "F11", context: Qt::WindowShortcut, enabled: EnabledShortcut)
362 << shortcutMap(sequence: "F12", context: Qt::WindowShortcut, enabled: DisabledShortcut);
363
364 // APP
365 QTest::newRow(dataTag: "F1") << Qt::Key_F1 << activeWindowShortcuts << inactiveWindowShortcuts << "F1" << "" << "";
366 QTest::newRow(dataTag: "F2") << Qt::Key_F2 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "";
367 QTest::newRow(dataTag: "F3") << Qt::Key_F3 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "F3";
368 QTest::newRow(dataTag: "F4") << Qt::Key_F4 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "";
369 QTest::newRow(dataTag: "F5") << Qt::Key_F5 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "F5" << "";
370 QTest::newRow(dataTag: "F6") << Qt::Key_F6 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "";
371
372 // WND
373 QTest::newRow(dataTag: "F7") << Qt::Key_F7 << activeWindowShortcuts << inactiveWindowShortcuts << "F7" << "" << "";
374 QTest::newRow(dataTag: "F8") << Qt::Key_F8 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "";
375 QTest::newRow(dataTag: "F9") << Qt::Key_F9 << activeWindowShortcuts << inactiveWindowShortcuts << "F9" << "" << "";
376 QTest::newRow(dataTag: "F10") << Qt::Key_F10 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "";
377 QTest::newRow(dataTag: "F11") << Qt::Key_F11 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "";
378 QTest::newRow(dataTag: "F12") << Qt::Key_F12 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "";
379}
380
381void tst_QQuickShortcut::context()
382{
383 QFETCH(Qt::Key, key);
384 QFETCH(QVariantList, activeWindowShortcuts);
385 QFETCH(QVariantList, inactiveWindowShortcuts);
386 QFETCH(QString, activeWindowActivatedShortcut);
387 QFETCH(QString, inactiveWindowActivatedShortcut);
388 QFETCH(QString, ambiguousShortcut);
389
390 QQmlApplicationEngine engine;
391
392 engine.load(url: testFileUrl(fileName: "shortcuts.qml"));
393 QQuickWindow *inactiveWindow = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 0));
394 QVERIFY(inactiveWindow);
395 inactiveWindow->show();
396 QVERIFY(QTest::qWaitForWindowExposed(inactiveWindow));
397 inactiveWindow->setProperty(name: "shortcuts", value: inactiveWindowShortcuts);
398
399 engine.load(url: testFileUrl(fileName: "shortcuts.qml"));
400 QQuickWindow *activeWindow = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 1));
401 QVERIFY(activeWindow);
402 activeWindow->show();
403 QVERIFY(QTest::qWaitForWindowExposed(activeWindow));
404 activeWindow->requestActivate();
405 QVERIFY(QTest::qWaitForWindowActive(activeWindow));
406 activeWindow->setProperty(name: "shortcuts", value: activeWindowShortcuts);
407
408 QTest::keyPress(window: activeWindow, key);
409 QTest::keyRelease(window: activeWindow, key);
410
411 QCOMPARE(activeWindow->property("activatedShortcut").toString(), activeWindowActivatedShortcut);
412 QCOMPARE(inactiveWindow->property("activatedShortcut").toString(), inactiveWindowActivatedShortcut);
413 QVERIFY(activeWindow->property("ambiguousShortcut").toString() == ambiguousShortcut
414 || inactiveWindow->property("ambiguousShortcut").toString() == ambiguousShortcut);
415}
416
417typedef bool (*ShortcutContextMatcher)(QObject *, Qt::ShortcutContext);
418extern ShortcutContextMatcher qt_quick_shortcut_context_matcher();
419extern void qt_quick_set_shortcut_context_matcher(ShortcutContextMatcher matcher);
420
421static ShortcutContextMatcher lastMatcher = nullptr;
422
423static bool trueMatcher(QObject *, Qt::ShortcutContext)
424{
425 lastMatcher = trueMatcher;
426 return true;
427}
428
429static bool falseMatcher(QObject *, Qt::ShortcutContext)
430{
431 lastMatcher = falseMatcher;
432 return false;
433}
434
435Q_DECLARE_METATYPE(ShortcutContextMatcher)
436
437void tst_QQuickShortcut::matcher_data()
438{
439 QTest::addColumn<ShortcutContextMatcher>(name: "matcher");
440 QTest::addColumn<Qt::Key>(name: "key");
441 QTest::addColumn<QVariant>(name: "shortcut");
442 QTest::addColumn<QString>(name: "activatedShortcut");
443
444 ShortcutContextMatcher tm = trueMatcher;
445 ShortcutContextMatcher fm = falseMatcher;
446
447 QTest::newRow(dataTag: "F1") << tm << Qt::Key_F1 << shortcutMap(sequence: "F1", context: Qt::ApplicationShortcut) << "F1";
448 QTest::newRow(dataTag: "F2") << fm << Qt::Key_F2 << shortcutMap(sequence: "F2", context: Qt::ApplicationShortcut) << "";
449}
450
451void tst_QQuickShortcut::matcher()
452{
453 QFETCH(ShortcutContextMatcher, matcher);
454 QFETCH(Qt::Key, key);
455 QFETCH(QVariant, shortcut);
456 QFETCH(QString, activatedShortcut);
457
458 ShortcutContextMatcher defaultMatcher = qt_quick_shortcut_context_matcher();
459 QVERIFY(defaultMatcher);
460
461 qt_quick_set_shortcut_context_matcher(matcher);
462 QVERIFY(qt_quick_shortcut_context_matcher() == matcher);
463
464 QQmlApplicationEngine engine(testFileUrl(fileName: "shortcuts.qml"));
465 QQuickWindow *window = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 0));
466 QVERIFY(window);
467 window->show();
468 QVERIFY(QTest::qWaitForWindowExposed(window));
469 window->requestActivate();
470 QVERIFY(QTest::qWaitForWindowActive(window));
471
472 window->setProperty(name: "shortcuts", value: QVariantList() << shortcut);
473 QTest::keyClick(window, key);
474
475 QVERIFY(lastMatcher == matcher);
476 QCOMPARE(window->property("activatedShortcut").toString(), activatedShortcut);
477
478 qt_quick_set_shortcut_context_matcher(matcher: defaultMatcher);
479}
480
481void tst_QQuickShortcut::multiple_data()
482{
483 QTest::addColumn<QStringList>(name: "sequences");
484 QTest::addColumn<Qt::Key>(name: "key");
485 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers");
486 QTest::addColumn<bool>(name: "enabled");
487 QTest::addColumn<bool>(name: "activated");
488
489 // first
490 QTest::newRow(dataTag: "Ctrl+X,(Shift+Del)") << (QStringList() << "Ctrl+X" << "Shift+Del") << Qt::Key_X << Qt::KeyboardModifiers(Qt::ControlModifier) << true << true;
491 // second
492 QTest::newRow(dataTag: "(Ctrl+X),Shift+Del") << (QStringList() << "Ctrl+X" << "Shift+Del") << Qt::Key_Delete << Qt::KeyboardModifiers(Qt::ShiftModifier) << true << true;
493 // disabled
494 QTest::newRow(dataTag: "(Ctrl+X,Shift+Del)") << (QStringList() << "Ctrl+X" << "Shift+Del") << Qt::Key_X << Qt::KeyboardModifiers(Qt::ControlModifier) << false << false;
495}
496
497void tst_QQuickShortcut::multiple()
498{
499 QFETCH(QStringList, sequences);
500 QFETCH(Qt::Key, key);
501 QFETCH(Qt::KeyboardModifiers, modifiers);
502 QFETCH(bool, enabled);
503 QFETCH(bool, activated);
504
505 QQmlApplicationEngine engine;
506
507 engine.load(url: testFileUrl(fileName: "multiple.qml"));
508 QQuickWindow *window = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 0));
509 QVERIFY(window);
510 window->show();
511 QVERIFY(QTest::qWaitForWindowExposed(window));
512 window->requestActivate();
513 QVERIFY(QTest::qWaitForWindowActive(window));
514
515 QObject *shortcut = window->property(name: "shortcut").value<QObject *>();
516 QVERIFY(shortcut);
517
518 shortcut->setProperty(name: "enabled", value: enabled);
519 shortcut->setProperty(name: "sequences", value: sequences);
520
521 QTest::keyPress(window, key, modifier: modifiers);
522 QTest::keyRelease(window, key, modifier: modifiers);
523
524 QCOMPARE(window->property("activated").toBool(), activated);
525
526 // check it still works after rotating the sequences
527 QStringList rotatedSequences;
528 for (int i = 1; i < sequences.size(); ++i)
529 rotatedSequences.push_back(t: sequences[i]);
530 if (sequences.size())
531 rotatedSequences.push_back(t: sequences[0]);
532
533 window->setProperty(name: "activated", value: false);
534 shortcut->setProperty(name: "sequences", value: rotatedSequences);
535
536 QTest::keyPress(window, key, modifier: modifiers);
537 QTest::keyRelease(window, key, modifier: modifiers);
538 QCOMPARE(window->property("activated").toBool(), activated);
539
540 // check setting to no shortcuts
541 QStringList emptySequence;
542
543 window->setProperty(name: "activated", value: false);
544 shortcut->setProperty(name: "sequences", value: emptySequence);
545
546 QTest::keyPress(window, key, modifier: modifiers);
547 QTest::keyRelease(window, key, modifier: modifiers);
548 QCOMPARE(window->property("activated").toBool(), false);
549}
550
551void tst_QQuickShortcut::contextChange_data()
552{
553 multiple_data();
554}
555void tst_QQuickShortcut::contextChange()
556{
557 QFETCH(QStringList, sequences);
558 QFETCH(Qt::Key, key);
559 QFETCH(Qt::KeyboardModifiers, modifiers);
560 QFETCH(bool, enabled);
561 QFETCH(bool, activated);
562
563 QQmlApplicationEngine engine;
564
565 engine.load(url: testFileUrl(fileName: "multiple.qml"));
566 QQuickWindow *inactivewindow = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 0));
567 QVERIFY(inactivewindow);
568 inactivewindow->show();
569 QVERIFY(QTest::qWaitForWindowExposed(inactivewindow));
570
571 QObject *shortcut = inactivewindow->property(name: "shortcut").value<QObject *>();
572 QVERIFY(shortcut);
573
574 shortcut->setProperty(name: "enabled", value: enabled);
575 shortcut->setProperty(name: "sequences", value: sequences);
576 shortcut->setProperty(name: "context", value: Qt::WindowShortcut);
577
578 engine.load(url: testFileUrl(fileName: "multiple.qml"));
579 QQuickWindow *activewindow = qobject_cast<QQuickWindow *>(object: engine.rootObjects().value(i: 1));
580 QVERIFY(activewindow);
581 activewindow->show();
582 QVERIFY(QTest::qWaitForWindowExposed(activewindow));
583
584 QTest::keyPress(window: activewindow, key, modifier: modifiers);
585 QCOMPARE(inactivewindow->property("activated").toBool(), false);
586
587 shortcut->setProperty(name: "context", value: Qt::ApplicationShortcut);
588
589 QTest::keyPress(window: activewindow, key, modifier: modifiers);
590 QCOMPARE(inactivewindow->property("activated").toBool(), activated);
591}
592
593#ifdef QT_QUICKWIDGETS_LIB
594void tst_QQuickShortcut::renderControlShortcuts_data()
595{
596 QTest::addColumn<QVariantList>(name: "shortcuts");
597 QTest::addColumn<Qt::Key>(name: "key");
598 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers");
599 QTest::addColumn<QString>(name: "activatedShortcut");
600 QTest::addColumn<QString>(name: "ambiguousShortcut");
601
602 QVariantList shortcuts;
603 shortcuts << shortcutMap(key: "M")
604 << shortcutMap(key: "Alt+M")
605 << shortcutMap(key: "Ctrl+M")
606 << shortcutMap(key: "Shift+M")
607 << shortcutMap(key: "Ctrl+Alt+M")
608 << shortcutMap(key: "+")
609 << shortcutMap(key: "F1")
610 << shortcutMap(key: "Shift+F1");
611
612 QTest::newRow(dataTag: "M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "M" << "";
613 QTest::newRow(dataTag: "Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "Alt+M" << "";
614 QTest::newRow(dataTag: "Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "Ctrl+M" << "";
615 QTest::newRow(dataTag: "Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+M" << "";
616 QTest::newRow(dataTag: "Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "Ctrl+Alt+M" << "";
617 QTest::newRow(dataTag: "+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "+" << "";
618 QTest::newRow(dataTag: "F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "F1" << "";
619 QTest::newRow(dataTag: "Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+F1" << "";
620
621 // ambiguous
622 shortcuts << shortcutMap(key: "M")
623 << shortcutMap(key: "Alt+M")
624 << shortcutMap(key: "Ctrl+M")
625 << shortcutMap(key: "Shift+M")
626 << shortcutMap(key: "Ctrl+Alt+M")
627 << shortcutMap(key: "+")
628 << shortcutMap(key: "F1")
629 << shortcutMap(key: "Shift+F1");
630
631 QTest::newRow(dataTag: "?M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "M";
632 QTest::newRow(dataTag: "?Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "" << "Alt+M";
633 QTest::newRow(dataTag: "?Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "" << "Ctrl+M";
634 QTest::newRow(dataTag: "?Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "Shift+M";
635 QTest::newRow(dataTag: "?Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "" << "Ctrl+Alt+M";
636 QTest::newRow(dataTag: "?+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "+";
637 QTest::newRow(dataTag: "?F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "F1";
638 QTest::newRow(dataTag: "?Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "Shift+F1";
639
640 // disabled
641 shortcuts.clear();
642 shortcuts << shortcutMap(key: "M", enabled: DisabledShortcut)
643 << shortcutMap(key: "Alt+M", enabled: DisabledShortcut)
644 << shortcutMap(key: "Ctrl+M", enabled: DisabledShortcut)
645 << shortcutMap(key: "Shift+M", enabled: DisabledShortcut)
646 << shortcutMap(key: "Ctrl+Alt+M", enabled: DisabledShortcut)
647 << shortcutMap(key: "+", enabled: DisabledShortcut)
648 << shortcutMap(key: "F1", enabled: DisabledShortcut)
649 << shortcutMap(key: "Shift+F1", enabled: DisabledShortcut);
650
651 QTest::newRow(dataTag: "!M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "";
652 QTest::newRow(dataTag: "!Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "" << "";
653 QTest::newRow(dataTag: "!Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "" << "";
654 QTest::newRow(dataTag: "!Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "";
655 QTest::newRow(dataTag: "!Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "" << "";
656 QTest::newRow(dataTag: "!+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "";
657 QTest::newRow(dataTag: "!F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "";
658 QTest::newRow(dataTag: "!Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "";
659
660 // unambigous because others disabled
661 shortcuts << shortcutMap(key: "M")
662 << shortcutMap(key: "Alt+M")
663 << shortcutMap(key: "Ctrl+M")
664 << shortcutMap(key: "Shift+M")
665 << shortcutMap(key: "Ctrl+Alt+M")
666 << shortcutMap(key: "+")
667 << shortcutMap(key: "F1")
668 << shortcutMap(key: "Shift+F1");
669
670 QTest::newRow(dataTag: "/M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "M" << "";
671 QTest::newRow(dataTag: "/Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "Alt+M" << "";
672 QTest::newRow(dataTag: "/Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "Ctrl+M" << "";
673 QTest::newRow(dataTag: "/Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+M" << "";
674 QTest::newRow(dataTag: "/Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "Ctrl+Alt+M" << "";
675 QTest::newRow(dataTag: "/+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "+" << "";
676 QTest::newRow(dataTag: "/F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "F1" << "";
677 QTest::newRow(dataTag: "/Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+F1" << "";
678}
679
680void tst_QQuickShortcut::renderControlShortcuts()
681{
682 QFETCH(QVariantList, shortcuts);
683 QFETCH(Qt::Key, key);
684 QFETCH(Qt::KeyboardModifiers, modifiers);
685 QFETCH(QString, activatedShortcut);
686 QFETCH(QString, ambiguousShortcut);
687
688 QScopedPointer<QQuickWidget> quickWidget(new QQuickWidget);
689 quickWidget->resize(w: 300,h: 300);
690
691 QSignalSpy spy(qApp, &QGuiApplication::focusObjectChanged);
692
693 quickWidget->setSource(testFileUrl(fileName: "shortcutsRect.qml"));
694 quickWidget->show();
695
696 spy.wait();
697
698 QVERIFY(qobject_cast<QQuickWidget*>(qApp->focusObject()) == quickWidget.data());
699
700 QQuickItem* item = quickWidget->rootObject();
701 item->setProperty(name: "shortcuts", value: shortcuts);
702 QTest::keyPress(window: quickWidget->quickWindow(), key, modifier: modifiers, delay: 1500);
703 QTest::keyRelease(window: quickWidget->quickWindow(), key, modifier: modifiers, delay: 1600);
704 QCOMPARE(item->property("activatedShortcut").toString(), activatedShortcut);
705 QCOMPARE(item->property("ambiguousShortcut").toString(), ambiguousShortcut);
706}
707#endif // QT_QUICKWIDGETS_LIB
708
709QTEST_MAIN(tst_QQuickShortcut)
710
711#include "tst_qquickshortcut.moc"
712

source code of qtdeclarative/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp