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