Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickshortcut.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtQuick/qquickitem.h>
8#include <QtQuick/qquickwindow.h>
9#include <QtQuick/private/qquickrendercontrol_p.h>
10#include <QtQuick/private/qtquickglobal_p.h>
11#include <QtGui/private/qguiapplication_p.h>
12#include <QtQml/qqmlinfo.h>
13
15
16/*!
17 \qmltype Shortcut
18 \nativetype QQuickShortcut
19 \inqmlmodule QtQuick
20 \since 5.5
21 \ingroup qtquick-input
22 \brief Provides keyboard shortcuts.
23
24 The Shortcut type lets you handle keyboard shortcuts. The shortcut can
25 be set to one of the
26 \l{QKeySequence::StandardKey}{standard keyboard shortcuts},
27 or it can be described with a string containing a sequence of up to four key
28 presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
29
30 \qml
31 Item {
32 id: view
33
34 property int currentIndex
35
36 Shortcut {
37 sequences: [StandardKey.NextChild]
38 onActivated: view.currentIndex++
39 }
40 }
41 \endqml
42
43 It is also possible to set multiple shortcut \l sequences, so that the shortcut
44 can be \l activated via several different sequences of key presses.
45
46 \sa Keys, {Keys::}{shortcutOverride()}
47*/
48
49/*! \qmlsignal QtQuick::Shortcut::activated()
50
51 This signal is emitted when the shortcut is activated.
52*/
53
54/*! \qmlsignal QtQuick::Shortcut::activatedAmbiguously()
55
56 This signal is emitted when the shortcut is activated ambigously,
57 meaning that it matches the start of more than one shortcut.
58*/
59
60static bool qQuickShortcutContextMatcher(QObject *obj, Qt::ShortcutContext context)
61{
62 switch (context) {
63 case Qt::ApplicationShortcut:
64 return true;
65 case Qt::WindowShortcut: {
66 while (obj && !obj->isWindowType()) {
67 obj = obj->parent();
68 if (QQuickItem *item = qobject_cast<QQuickItem *>(obj))
69 obj = item->window();
70 }
71 const QWindow *focusWindow = QGuiApplication::focusWindow();
72 if (!obj || !focusWindow)
73 return false;
74 if (obj == focusWindow)
75 return true;
76 return QQuickRenderControlPrivate::isFocusWindowFor(
77 qobject_cast<QQuickWindow *>(obj), focusWindow);
78 }
79 default:
80 return false;
81 }
82}
83
84typedef bool (*ContextMatcher)(QObject *, Qt::ShortcutContext);
85
86Q_GLOBAL_STATIC_WITH_ARGS(ContextMatcher, ctxMatcher, (qQuickShortcutContextMatcher))
87
88Q_QUICK_EXPORT ContextMatcher qt_quick_shortcut_context_matcher()
89{
90 return *ctxMatcher();
91}
92
94{
95 if (!ctxMatcher.isDestroyed())
96 *ctxMatcher() = matcher;
97}
98
99static QKeySequence valueToKeySequence(const QVariant &value, const QQuickShortcut *const shortcut)
100{
101 if (value.userType() == QMetaType::Int) {
102 const QList<QKeySequence> s =
103 QKeySequence::keyBindings(static_cast<QKeySequence::StandardKey>(value.toInt()));
104 if (s.size() > 1) {
105 const QString templateString = QString::fromUtf16(
106 u"Shortcut: Only binding to one of multiple key bindings associated with %1. "
107 u"Use 'sequences: [ <key> ]' to bind to all of them.");
108 qmlWarning(shortcut)
109 << templateString.arg(static_cast<QKeySequence::StandardKey>(value.toInt()));
110 }
111 return s.size() > 0 ? s[0] : QKeySequence {};
112 } else if (value.userType() == QMetaType::QKeySequence) {
113 return value.value<QKeySequence>();
114 }
115
116 return QKeySequence::fromString(value.toString());
117}
118
119static QList<QKeySequence> valueToKeySequences(const QVariant &value)
120{
121 if (value.userType() == QMetaType::Int) {
122 return QKeySequence::keyBindings(static_cast<QKeySequence::StandardKey>(value.toInt()));
123 } else {
124 QList<QKeySequence> result;
125 if (value.userType() == QMetaType::QKeySequence)
126 result.push_back(value.value<QKeySequence>());
127 else
128 result.push_back(QKeySequence::fromString(value.toString()));
129 return result;
130 }
131}
132
133QQuickShortcut::QQuickShortcut(QObject *parent) : QObject(parent),
134 m_enabled(true), m_completed(false), m_autorepeat(true), m_context(Qt::WindowShortcut)
135{
136}
137
138QQuickShortcut::~QQuickShortcut()
139{
140 ungrabShortcut(m_shortcut);
141 for (Shortcut &shortcut : m_shortcuts)
142 ungrabShortcut(shortcut);
143}
144
145/*!
146 \qmlproperty keysequence QtQuick::Shortcut::sequence
147
148 This property holds the shortcut's key sequence. The key sequence can be set
149 to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, or
150 it can be described with a string containing a sequence of up to four key
151 presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
152
153 The default value is an empty key sequence.
154
155 \qml
156 Shortcut {
157 sequence: "Ctrl+E,Ctrl+W"
158 onActivated: edit.wrapMode = TextEdit.Wrap
159 }
160 \endqml
161
162 \note Given that standard keys can resolve to one shortcut on some
163 platforms, but multiple shortcuts on other platforms, we recommend always
164 using \l{Shortcut::}{sequences} for standard keys.
165
166 \sa sequences
167*/
168QVariant QQuickShortcut::sequence() const
169{
170 return m_shortcut.userValue;
171}
172
173void QQuickShortcut::setSequence(const QVariant &value)
174{
175 if (value == m_shortcut.userValue)
176 return;
177
178 QKeySequence keySequence = valueToKeySequence(value, this);
179
180 ungrabShortcut(m_shortcut);
181 m_shortcut.userValue = value;
182 m_shortcut.keySequence = keySequence;
183 grabShortcut(m_shortcut, m_context);
184 emit sequenceChanged();
185 if (m_shortcuts.isEmpty()) {
186 // The text only changes if m_shortcuts contained no entry
187 // as we prefer the entry from there otherwise
188 emit nativeTextChanged();
189 emit portableTextChanged();
190 }
191}
192
193/*!
194 \qmlproperty list<keysequence> QtQuick::Shortcut::sequences
195 \since 5.9
196
197 This property holds multiple key sequences for the shortcut. The key sequences
198 can be set to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts},
199 or they can be described with strings containing sequences of up to four key
200 presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
201
202 \qml
203 Shortcut {
204 sequences: [StandardKey.Cut, "Ctrl+X", "Shift+Del"]
205 onActivated: edit.cut()
206 }
207 \endqml
208*/
209QVariantList QQuickShortcut::sequences() const
210{
211 QVariantList values;
212 for (const Shortcut &shortcut : m_shortcuts)
213 values += shortcut.userValue;
214 return values;
215}
216
217void QQuickShortcut::setSequences(const QVariantList &values)
218{
219 // convert QVariantList to QList<QKeySequence>
220 QList<Shortcut> requestedShortcuts;
221 for (const QVariant &v : values) {
222 const QList<QKeySequence> list = valueToKeySequences(v);
223 for (const QKeySequence &s : list) {
224 Shortcut sc;
225 sc.userValue = v;
226 sc.keySequence = s;
227 requestedShortcuts.push_back(sc);
228 }
229 }
230
231 // if nothing has changed, just return:
232 if (m_shortcuts.size() == requestedShortcuts.size()) {
233 bool changed = false;
234 for (int i = 0; i < requestedShortcuts.size(); ++i) {
235 const Shortcut &requestedShortcut = requestedShortcuts[i];
236 const Shortcut &shortcut = m_shortcuts[i];
237 if (!(requestedShortcut.userValue == shortcut.userValue
238 && requestedShortcut.keySequence == shortcut.keySequence)) {
239 changed = true;
240 break;
241 }
242 }
243 if (!changed) {
244 return;
245 }
246 }
247
248 const Shortcut oldUsedShortcut = m_shortcuts.isEmpty() ? m_shortcut
249 : m_shortcuts.first();
250
251 for (Shortcut &s : m_shortcuts)
252 ungrabShortcut(s);
253 m_shortcuts = requestedShortcuts;
254 for (Shortcut &s : m_shortcuts)
255 grabShortcut(s, m_context);
256
257 const Shortcut currentUsedShortcut = m_shortcuts.isEmpty() ? m_shortcut
258 : m_shortcuts.first();
259
260 emit sequencesChanged();
261 if (oldUsedShortcut.keySequence != currentUsedShortcut.keySequence) {
262 emit nativeTextChanged();
263 emit portableTextChanged();
264 }
265}
266
267/*!
268 \qmlproperty string QtQuick::Shortcut::nativeText
269 \since 5.6
270
271 This property provides the shortcut's key sequence as a platform specific
272 string. This means that it will be shown translated, and on \macos it will
273 resemble a key sequence from the menu bar. It is best to display this text
274 to the user (for example, on a tooltip).
275
276 \include qquickshortcut.qdocinc multishortcut
277
278 \sa sequence, portableText
279*/
280QString QQuickShortcut::nativeText() const
281{
282 const Shortcut &shortCut = m_shortcuts.isEmpty() ? m_shortcut
283 : m_shortcuts.front();
284 return shortCut.keySequence.toString(QKeySequence::NativeText);
285}
286
287/*!
288 \qmlproperty string QtQuick::Shortcut::portableText
289 \since 5.6
290
291 This property provides the shortcut's key sequence as a string in a
292 "portable" format, suitable for reading and writing to a file. In many
293 cases, it will look similar to the native text on Windows and X11.
294
295 \include qquickshortcut.qdocinc multishortcut
296
297 \sa sequence, nativeText
298*/
299QString QQuickShortcut::portableText() const
300{
301 const Shortcut &shortCut = m_shortcuts.isEmpty() ? m_shortcut
302 : m_shortcuts.front();
303 return shortCut.keySequence.toString(QKeySequence::PortableText);
304}
305
306/*!
307 \qmlproperty bool QtQuick::Shortcut::enabled
308
309 This property holds whether the shortcut is enabled.
310
311 The default value is \c true.
312*/
313bool QQuickShortcut::isEnabled() const
314{
315 return m_enabled;
316}
317
318void QQuickShortcut::setEnabled(bool enabled)
319{
320 if (enabled == m_enabled)
321 return;
322
323 setEnabled(m_shortcut, enabled);
324 for (Shortcut &shortcut : m_shortcuts)
325 setEnabled(shortcut, enabled);
326
327 m_enabled = enabled;
328 emit enabledChanged();
329}
330
331/*!
332 \qmlproperty bool QtQuick::Shortcut::autoRepeat
333
334 This property holds whether the shortcut can auto repeat.
335
336 The default value is \c true.
337*/
338bool QQuickShortcut::autoRepeat() const
339{
340 return m_autorepeat;
341}
342
343void QQuickShortcut::setAutoRepeat(bool repeat)
344{
345 if (repeat == m_autorepeat)
346 return;
347
348 setAutoRepeat(m_shortcut, repeat);
349 for (Shortcut &shortcut : m_shortcuts)
350 setAutoRepeat(shortcut, repeat);
351
352 m_autorepeat = repeat;
353 emit autoRepeatChanged();
354}
355
356/*!
357 \qmlproperty enumeration QtQuick::Shortcut::context
358
359 This property holds the \l{Qt::ShortcutContext}{shortcut context}.
360
361 Supported values are:
362
363 \value Qt.WindowShortcut
364 (default) The shortcut is active when its parent item is in an active top-level window.
365 \value Qt.ApplicationShortcut
366 The shortcut is active when one of the application's windows are active.
367
368 \qml
369 Shortcut {
370 sequence: StandardKey.Quit
371 context: Qt.ApplicationShortcut
372 onActivated: Qt.quit()
373 }
374 \endqml
375*/
376Qt::ShortcutContext QQuickShortcut::context() const
377{
378 return m_context;
379}
380
381void QQuickShortcut::setContext(Qt::ShortcutContext context)
382{
383 if (context == m_context)
384 return;
385
386 ungrabShortcut(m_shortcut);
387 for (auto &s : m_shortcuts)
388 ungrabShortcut(s);
389
390 m_context = context;
391
392 grabShortcut(m_shortcut, context);
393 for (auto &s : m_shortcuts)
394 grabShortcut(s, context);
395
396 emit contextChanged();
397}
398
399void QQuickShortcut::classBegin()
400{
401}
402
403void QQuickShortcut::componentComplete()
404{
405 m_completed = true;
406 grabShortcut(m_shortcut, m_context);
407 for (Shortcut &shortcut : m_shortcuts)
408 grabShortcut(shortcut, m_context);
409}
410
411bool QQuickShortcut::event(QEvent *event)
412{
413 if (m_enabled && event->type() == QEvent::Shortcut) {
414 QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
415 bool match = m_shortcut.matches(se);
416 int i = 0;
417 while (!match && i < m_shortcuts.size())
418 match |= m_shortcuts.at(i++).matches(se);
419 if (match) {
420 if (se->isAmbiguous())
421 emit activatedAmbiguously();
422 else
423 emit activated();
424 return true;
425 }
426 }
427 return false;
428}
429
430bool QQuickShortcut::Shortcut::matches(QShortcutEvent *event) const
431{
432 return event->shortcutId() == id && event->key() == keySequence;
433}
434
435void QQuickShortcut::setEnabled(QQuickShortcut::Shortcut &shortcut, bool enabled)
436{
437 if (shortcut.id)
438 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, shortcut.id, this);
439}
440
441void QQuickShortcut::setAutoRepeat(QQuickShortcut::Shortcut &shortcut, bool repeat)
442{
443 if (shortcut.id)
444 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(repeat, shortcut.id, this);
445}
446
447void QQuickShortcut::grabShortcut(Shortcut &shortcut, Qt::ShortcutContext context)
448{
449 if (m_completed && !shortcut.keySequence.isEmpty()) {
450 QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance();
451 shortcut.id = pApp->shortcutMap.addShortcut(this, shortcut.keySequence, context, *ctxMatcher());
452 if (!m_enabled)
453 pApp->shortcutMap.setShortcutEnabled(false, shortcut.id, this);
454 if (!m_autorepeat)
455 pApp->shortcutMap.setShortcutAutoRepeat(false, shortcut.id, this);
456 }
457}
458
459void QQuickShortcut::ungrabShortcut(Shortcut &shortcut)
460{
461 if (shortcut.id) {
462 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(shortcut.id, this);
463 shortcut.id = 0;
464 }
465}
466
467QT_END_NAMESPACE
468
469#include "moc_qquickshortcut_p.cpp"
Combined button and popup list for selecting options.
static QKeySequence valueToKeySequence(const QVariant &value, const QQuickShortcut *const shortcut)
static QT_BEGIN_NAMESPACE bool qQuickShortcutContextMatcher(QObject *obj, Qt::ShortcutContext context)
\qmltype Shortcut \nativetype QQuickShortcut \inqmlmodule QtQuick
bool(* ContextMatcher)(QObject *, Qt::ShortcutContext)
static QList< QKeySequence > valueToKeySequences(const QVariant &value)
Q_QUICK_EXPORT void qt_quick_set_shortcut_context_matcher(ContextMatcher matcher)