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/qquickrendercontrol.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 if (QWindow *renderWindow = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow *>(obj)))
72 obj = renderWindow;
73 return obj && obj == QGuiApplication::focusWindow();
74 default:
75 return false;
76 }
77}
78
79typedef bool (*ContextMatcher)(QObject *, Qt::ShortcutContext);
80
81Q_GLOBAL_STATIC_WITH_ARGS(ContextMatcher, ctxMatcher, (qQuickShortcutContextMatcher))
82
83Q_QUICK_EXPORT ContextMatcher qt_quick_shortcut_context_matcher()
84{
85 return *ctxMatcher();
86}
87
89{
90 if (!ctxMatcher.isDestroyed())
91 *ctxMatcher() = matcher;
92}
93
94static QKeySequence valueToKeySequence(const QVariant &value, const QQuickShortcut *const shortcut)
95{
96 if (value.userType() == QMetaType::Int) {
97 const QVector<QKeySequence> s =
98 QKeySequence::keyBindings(static_cast<QKeySequence::StandardKey>(value.toInt()));
99 if (s.size() > 1) {
100 const QString templateString = QString::fromUtf16(
101 u"Shortcut: Only binding to one of multiple key bindings associated with %1. "
102 u"Use 'sequences: [ <key> ]' to bind to all of them.");
103 qmlWarning(shortcut)
104 << templateString.arg(static_cast<QKeySequence::StandardKey>(value.toInt()));
105 }
106 return s.size() > 0 ? s[0] : QKeySequence {};
107 }
108
109 return QKeySequence::fromString(value.toString());
110}
111
112static QList<QKeySequence> valueToKeySequences(const QVariant &value)
113{
114 if (value.userType() == QMetaType::Int) {
115 return QKeySequence::keyBindings(static_cast<QKeySequence::StandardKey>(value.toInt()));
116 } else {
117 QList<QKeySequence> result;
118 result.push_back(QKeySequence::fromString(value.toString()));
119 return result;
120 }
121}
122
123QQuickShortcut::QQuickShortcut(QObject *parent) : QObject(parent),
124 m_enabled(true), m_completed(false), m_autorepeat(true), m_context(Qt::WindowShortcut)
125{
126}
127
128QQuickShortcut::~QQuickShortcut()
129{
130 ungrabShortcut(m_shortcut);
131 for (Shortcut &shortcut : m_shortcuts)
132 ungrabShortcut(shortcut);
133}
134
135/*!
136 \qmlproperty keysequence QtQuick::Shortcut::sequence
137
138 This property holds the shortcut's key sequence. The key sequence can be set
139 to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, or
140 it can be described with a string containing a sequence of up to four key
141 presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
142
143 The default value is an empty key sequence.
144
145 \qml
146 Shortcut {
147 sequence: "Ctrl+E,Ctrl+W"
148 onActivated: edit.wrapMode = TextEdit.Wrap
149 }
150 \endqml
151
152 \note Given that standard keys can resolve to one shortcut on some
153 platforms, but multiple shortcuts on other platforms, we recommend always
154 using \l{Shortcut::}{sequences} for standard keys.
155
156 \sa sequences
157*/
158QVariant QQuickShortcut::sequence() const
159{
160 return m_shortcut.userValue;
161}
162
163void QQuickShortcut::setSequence(const QVariant &value)
164{
165 if (value == m_shortcut.userValue)
166 return;
167
168 QKeySequence keySequence = valueToKeySequence(value, this);
169
170 ungrabShortcut(m_shortcut);
171 m_shortcut.userValue = value;
172 m_shortcut.keySequence = keySequence;
173 grabShortcut(m_shortcut, m_context);
174 emit sequenceChanged();
175 if (m_shortcuts.isEmpty()) {
176 // The text only changes if m_shortcuts contained no entry
177 // as we prefer the entry from there otherwise
178 emit nativeTextChanged();
179 emit portableTextChanged();
180 }
181}
182
183/*!
184 \qmlproperty list<keysequence> QtQuick::Shortcut::sequences
185 \since 5.9
186
187 This property holds multiple key sequences for the shortcut. The key sequences
188 can be set to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts},
189 or they can be described with strings containing sequences of up to four key
190 presses that are needed to \l{Shortcut::activated}{activate} the shortcut.
191
192 \qml
193 Shortcut {
194 sequences: [StandardKey.Cut, "Ctrl+X", "Shift+Del"]
195 onActivated: edit.cut()
196 }
197 \endqml
198*/
199QVariantList QQuickShortcut::sequences() const
200{
201 QVariantList values;
202 for (const Shortcut &shortcut : m_shortcuts)
203 values += shortcut.userValue;
204 return values;
205}
206
207void QQuickShortcut::setSequences(const QVariantList &values)
208{
209 // convert QVariantList to QVector<QKeySequence>
210 QVector<Shortcut> requestedShortcuts;
211 for (const QVariant &v : values) {
212 const QVector<QKeySequence> list = valueToKeySequences(v);
213 for (const QKeySequence &s : list) {
214 Shortcut sc;
215 sc.userValue = v;
216 sc.keySequence = s;
217 requestedShortcuts.push_back(sc);
218 }
219 }
220
221 // if nothing has changed, just return:
222 if (m_shortcuts.size() == requestedShortcuts.size()) {
223 bool changed = false;
224 for (int i = 0; i < requestedShortcuts.size(); ++i) {
225 const Shortcut &requestedShortcut = requestedShortcuts[i];
226 const Shortcut &shortcut = m_shortcuts[i];
227 if (!(requestedShortcut.userValue == shortcut.userValue
228 && requestedShortcut.keySequence == shortcut.keySequence)) {
229 changed = true;
230 break;
231 }
232 }
233 if (!changed) {
234 return;
235 }
236 }
237
238 const Shortcut oldUsedShortcut = m_shortcuts.isEmpty() ? m_shortcut
239 : m_shortcuts.first();
240
241 for (Shortcut &s : m_shortcuts)
242 ungrabShortcut(s);
243 m_shortcuts = requestedShortcuts;
244 for (Shortcut &s : m_shortcuts)
245 grabShortcut(s, m_context);
246
247 const Shortcut currentUsedShortcut = m_shortcuts.isEmpty() ? m_shortcut
248 : m_shortcuts.first();
249
250 emit sequencesChanged();
251 if (oldUsedShortcut.keySequence != currentUsedShortcut.keySequence) {
252 emit nativeTextChanged();
253 emit portableTextChanged();
254 }
255}
256
257/*!
258 \qmlproperty string QtQuick::Shortcut::nativeText
259 \since 5.6
260
261 This property provides the shortcut's key sequence as a platform specific
262 string. This means that it will be shown translated, and on \macos it will
263 resemble a key sequence from the menu bar. It is best to display this text
264 to the user (for example, on a tooltip).
265
266 \include qquickshortcut.qdocinc [multishortcut]
267
268 \sa sequence, portableText
269*/
270QString QQuickShortcut::nativeText() const
271{
272 const Shortcut &shortCut = m_shortcuts.isEmpty() ? m_shortcut
273 : m_shortcuts.front();
274 return shortCut.keySequence.toString(QKeySequence::NativeText);
275}
276
277/*!
278 \qmlproperty string QtQuick::Shortcut::portableText
279 \since 5.6
280
281 This property provides the shortcut's key sequence as a string in a
282 "portable" format, suitable for reading and writing to a file. In many
283 cases, it will look similar to the native text on Windows and X11.
284
285 \include qquickshortcut.qdocinc [multishortcut]
286
287 \sa sequence, nativeText
288*/
289QString QQuickShortcut::portableText() const
290{
291 const Shortcut &shortCut = m_shortcuts.isEmpty() ? m_shortcut
292 : m_shortcuts.front();
293 return shortCut.keySequence.toString(QKeySequence::PortableText);
294}
295
296/*!
297 \qmlproperty bool QtQuick::Shortcut::enabled
298
299 This property holds whether the shortcut is enabled.
300
301 The default value is \c true.
302*/
303bool QQuickShortcut::isEnabled() const
304{
305 return m_enabled;
306}
307
308void QQuickShortcut::setEnabled(bool enabled)
309{
310 if (enabled == m_enabled)
311 return;
312
313 setEnabled(m_shortcut, enabled);
314 for (Shortcut &shortcut : m_shortcuts)
315 setEnabled(shortcut, enabled);
316
317 m_enabled = enabled;
318 emit enabledChanged();
319}
320
321/*!
322 \qmlproperty bool QtQuick::Shortcut::autoRepeat
323
324 This property holds whether the shortcut can auto repeat.
325
326 The default value is \c true.
327*/
328bool QQuickShortcut::autoRepeat() const
329{
330 return m_autorepeat;
331}
332
333void QQuickShortcut::setAutoRepeat(bool repeat)
334{
335 if (repeat == m_autorepeat)
336 return;
337
338 setAutoRepeat(m_shortcut, repeat);
339 for (Shortcut &shortcut : m_shortcuts)
340 setAutoRepeat(shortcut, repeat);
341
342 m_autorepeat = repeat;
343 emit autoRepeatChanged();
344}
345
346/*!
347 \qmlproperty enumeration QtQuick::Shortcut::context
348
349 This property holds the \l{Qt::ShortcutContext}{shortcut context}.
350
351 Supported values are:
352
353 \value Qt.WindowShortcut
354 (default) The shortcut is active when its parent item is in an active top-level window.
355 \value Qt.ApplicationShortcut
356 The shortcut is active when one of the application's windows are active.
357
358 \qml
359 Shortcut {
360 sequence: StandardKey.Quit
361 context: Qt.ApplicationShortcut
362 onActivated: Qt.quit()
363 }
364 \endqml
365*/
366Qt::ShortcutContext QQuickShortcut::context() const
367{
368 return m_context;
369}
370
371void QQuickShortcut::setContext(Qt::ShortcutContext context)
372{
373 if (context == m_context)
374 return;
375
376 ungrabShortcut(m_shortcut);
377 for (auto &s : m_shortcuts)
378 ungrabShortcut(s);
379
380 m_context = context;
381
382 grabShortcut(m_shortcut, context);
383 for (auto &s : m_shortcuts)
384 grabShortcut(s, context);
385
386 emit contextChanged();
387}
388
389void QQuickShortcut::classBegin()
390{
391}
392
393void QQuickShortcut::componentComplete()
394{
395 m_completed = true;
396 grabShortcut(m_shortcut, m_context);
397 for (Shortcut &shortcut : m_shortcuts)
398 grabShortcut(shortcut, m_context);
399}
400
401bool QQuickShortcut::event(QEvent *event)
402{
403 if (m_enabled && event->type() == QEvent::Shortcut) {
404 QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
405 bool match = m_shortcut.matches(se);
406 int i = 0;
407 while (!match && i < m_shortcuts.size())
408 match |= m_shortcuts.at(i++).matches(se);
409 if (match) {
410 if (se->isAmbiguous())
411 emit activatedAmbiguously();
412 else
413 emit activated();
414 return true;
415 }
416 }
417 return false;
418}
419
420bool QQuickShortcut::Shortcut::matches(QShortcutEvent *event) const
421{
422 return event->shortcutId() == id && event->key() == keySequence;
423}
424
425void QQuickShortcut::setEnabled(QQuickShortcut::Shortcut &shortcut, bool enabled)
426{
427 if (shortcut.id)
428 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, shortcut.id, this);
429}
430
431void QQuickShortcut::setAutoRepeat(QQuickShortcut::Shortcut &shortcut, bool repeat)
432{
433 if (shortcut.id)
434 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(repeat, shortcut.id, this);
435}
436
437void QQuickShortcut::grabShortcut(Shortcut &shortcut, Qt::ShortcutContext context)
438{
439 if (m_completed && !shortcut.keySequence.isEmpty()) {
440 QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance();
441 shortcut.id = pApp->shortcutMap.addShortcut(this, shortcut.keySequence, context, *ctxMatcher());
442 if (!m_enabled)
443 pApp->shortcutMap.setShortcutEnabled(false, shortcut.id, this);
444 if (!m_autorepeat)
445 pApp->shortcutMap.setShortcutAutoRepeat(false, shortcut.id, this);
446 }
447}
448
449void QQuickShortcut::ungrabShortcut(Shortcut &shortcut)
450{
451 if (shortcut.id) {
452 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(shortcut.id, this);
453 shortcut.id = 0;
454 }
455}
456
457QT_END_NAMESPACE
458
459#include "moc_qquickshortcut_p.cpp"
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)