1// Copyright (C) 2019 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
4#include "qshortcut.h"
5#include "qshortcut_p.h"
6
7#include <qevent.h>
8#include <qguiapplication.h>
9#include <qwindow.h>
10#include <private/qguiapplication_p.h>
11#include <qpa/qplatformmenu.h>
12
13QT_BEGIN_NAMESPACE
14
15#define QAPP_CHECK(functionName) \
16 if (Q_UNLIKELY(!qApp)) { \
17 qWarning("QShortcut: Initialize QGuiApplication before calling '" functionName "'."); \
18 return; \
19 }
20
21/*!
22 \class QShortcut
23 \brief The QShortcut class is used to create keyboard shortcuts.
24
25 \ingroup events
26 \inmodule QtGui
27
28 The QShortcut class provides a way of connecting keyboard
29 shortcuts to Qt's \l{signals and slots} mechanism, so that
30 objects can be informed when a shortcut is executed. The shortcut
31 can be set up to contain all the key presses necessary to
32 describe a keyboard shortcut, including the states of modifier
33 keys such as \uicontrol Shift, \uicontrol Ctrl, and \uicontrol Alt.
34
35 \target mnemonic
36
37 In widget applications, certain widgets can use '&' in front of a character.
38 This will automatically create a mnemonic (a shortcut) for that character,
39 e.g. "E&xit" will create the shortcut \uicontrol Alt+X (use '&&'
40 to display an actual ampersand). The widget might consume and perform
41 an action on a given shortcut. On X11 the ampersand will not be
42 shown and the character will be underlined. On Windows, shortcuts
43 are normally not displayed until the user presses the \uicontrol Alt
44 key, but this is a setting the user can change. On Mac, shortcuts
45 are disabled by default. Call \l qt_set_sequence_auto_mnemonic() to
46 enable them. However, because mnemonic shortcuts do not fit in
47 with Aqua's guidelines, Qt will not show the shortcut character
48 underlined.
49
50 For applications that use menus, it may be more convenient to
51 use the convenience functions provided in the QMenu class to
52 assign keyboard shortcuts to menu items as they are created.
53 Alternatively, shortcuts may be associated with other types of
54 actions in the QAction class.
55
56 The simplest way to create a shortcut for a particular widget is
57 to construct the shortcut with a key sequence. For example:
58
59 \snippet code/src_gui_kernel_qshortcut.cpp 0
60
61 When the user types the \l{QKeySequence}{key sequence}
62 for a given shortcut, the shortcut's activated() signal is
63 emitted. (In the case of ambiguity, the activatedAmbiguously()
64 signal is emitted.) A shortcut is "listened for" by Qt's event
65 loop when the shortcut's parent widget is receiving events.
66
67 A shortcut's key sequence can be set with setKey() and retrieved
68 with key(). A shortcut can be enabled or disabled with
69 setEnabled(), and can have "What's This?" help text set with
70 setWhatsThis().
71
72 \sa QShortcutEvent, QKeySequence, QAction
73*/
74
75/*!
76 \fn void QShortcut::activated()
77
78 This signal is emitted when the user types the shortcut's key
79 sequence.
80
81 \sa activatedAmbiguously()
82*/
83
84/*!
85 \fn void QShortcut::activatedAmbiguously()
86
87 When a key sequence is being typed at the keyboard, it is said to
88 be ambiguous as long as it matches the start of more than one
89 shortcut.
90
91 When a shortcut's key sequence is completed,
92 activatedAmbiguously() is emitted if the key sequence is still
93 ambiguous (i.e., it is the start of one or more other shortcuts).
94 The activated() signal is not emitted in this case.
95
96 \sa activated()
97*/
98
99static bool simpleContextMatcher(QObject *object, Qt::ShortcutContext context)
100{
101 auto guiShortcut = qobject_cast<QShortcut *>(object);
102 if (QGuiApplication::applicationState() != Qt::ApplicationActive || guiShortcut == nullptr)
103 return false;
104 if (context == Qt::ApplicationShortcut)
105 return true;
106 auto focusWindow = QGuiApplication::focusWindow();
107 if (!focusWindow)
108 return false;
109 auto window = qobject_cast<const QWindow *>(object: guiShortcut->parent());
110 if (!window)
111 return false;
112 if (focusWindow == window && focusWindow->isTopLevel())
113 return context == Qt::WindowShortcut || context == Qt::WidgetWithChildrenShortcut;
114 return focusWindow->isAncestorOf(child: window, mode: QWindow::ExcludeTransients);
115}
116
117QShortcutMap::ContextMatcher QShortcutPrivate::contextMatcher() const
118{
119 return simpleContextMatcher;
120}
121
122void QShortcutPrivate::redoGrab(QShortcutMap &map)
123{
124 Q_Q(QShortcut);
125 if (Q_UNLIKELY(!parent)) {
126 qWarning(msg: "QShortcut: No window parent defined");
127 return;
128 }
129
130 for (int id : std::as_const(t&: sc_ids))
131 map.removeShortcut(id, owner: q);
132
133 sc_ids.clear();
134 if (sc_sequences.isEmpty())
135 return;
136 sc_ids.reserve(asize: sc_sequences.size());
137 for (const auto &keySequence : std::as_const(t&: sc_sequences)) {
138 if (keySequence.isEmpty())
139 continue;
140 int id = map.addShortcut(owner: q, key: keySequence, context: sc_context, matcher: contextMatcher());
141 sc_ids.append(t: id);
142 if (!sc_enabled)
143 map.setShortcutEnabled(enable: false, id, owner: q);
144 if (!sc_autorepeat)
145 map.setShortcutAutoRepeat(on: false, id, owner: q);
146 }
147}
148
149QShortcutPrivate *QGuiApplicationPrivate::createShortcutPrivate() const
150{
151 return new QShortcutPrivate;
152}
153
154/*!
155 Constructs a QShortcut object for the \a parent, which should be a
156 QWindow or a QWidget.
157
158 Since no shortcut key sequence is specified, the shortcut will not emit any
159 signals.
160
161 \sa setKey()
162*/
163QShortcut::QShortcut(QObject *parent)
164 : QObject(*QGuiApplicationPrivate::instance()->createShortcutPrivate(), parent)
165{
166 Q_ASSERT(parent != nullptr);
167}
168
169/*!
170 Constructs a QShortcut object for the \a parent, which should be a
171 QWindow or a QWidget.
172
173 The shortcut operates on its parent, listening for \l{QShortcutEvent}s that
174 match the \a key sequence. Depending on the ambiguity of the event, the
175 shortcut will call the \a member function, or the \a ambiguousMember function,
176 if the key press was in the shortcut's \a context.
177*/
178QShortcut::QShortcut(const QKeySequence &key, QObject *parent,
179 const char *member, const char *ambiguousMember,
180 Qt::ShortcutContext context)
181 : QShortcut(parent)
182{
183 Q_D(QShortcut);
184 d->sc_context = context;
185 if (!key.isEmpty()) {
186 d->sc_sequences = { key };
187 d->redoGrab(map&: QGuiApplicationPrivate::instance()->shortcutMap);
188 }
189 if (member)
190 connect(sender: this, SIGNAL(activated()), receiver: parent, member);
191 if (ambiguousMember)
192 connect(sender: this, SIGNAL(activatedAmbiguously()), receiver: parent, member: ambiguousMember);
193}
194
195/*!
196 \since 6.0
197 Constructs a QShortcut object for the \a parent, which should be a
198 QWindow or a QWidget.
199
200 The shortcut operates on its parent, listening for \l{QShortcutEvent}s that
201 match the \a standardKey. Depending on the ambiguity of the event, the
202 shortcut will call the \a member function, or the \a ambiguousMember function,
203 if the key press was in the shortcut's \a context.
204*/
205QShortcut::QShortcut(QKeySequence::StandardKey standardKey, QObject *parent,
206 const char *member, const char *ambiguousMember,
207 Qt::ShortcutContext context)
208 : QShortcut(parent)
209{
210 Q_D(QShortcut);
211 d->sc_context = context;
212 d->sc_sequences = QKeySequence::keyBindings(key: standardKey);
213 d->redoGrab(map&: QGuiApplicationPrivate::instance()->shortcutMap);
214 if (member)
215 connect(sender: this, SIGNAL(activated()), receiver: parent, member);
216 if (ambiguousMember)
217 connect(sender: this, SIGNAL(activatedAmbiguously()), receiver: parent, member: ambiguousMember);
218}
219
220
221/*!
222 \fn template<typename Functor> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
223 \since 5.15
224 \overload
225
226 This is a QShortcut convenience constructor which connects the shortcut's
227 \l{QShortcut::activated()}{activated()} signal to the \a functor.
228*/
229/*!
230 \fn template<typename Functor> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
231 \since 5.15
232 \overload
233
234 This is a QShortcut convenience constructor which connects the shortcut's
235 \l{QShortcut::activated()}{activated()} signal to the \a functor.
236
237 The \a functor can be a pointer to a member function of the \a context object.
238
239 If the \a context object is destroyed, the \a functor will not be called.
240*/
241/*!
242 \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context, Functor functor, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
243 \since 5.15
244 \overload
245
246 This is a QShortcut convenience constructor which connects the shortcut's
247 \l{QShortcut::activated()}{activated()} signal to the \a functor and
248 \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
249 signal to the \a functorAmbiguous.
250
251 The \a functor and \a functorAmbiguous can be a pointer to a member
252 function of the \a context object.
253
254 If the \a context object is destroyed, the \a functor and
255 \a functorAmbiguous will not be called.
256*/
257/*!
258 \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context1, Functor functor, const QObject *context2, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
259 \since 5.15
260 \overload
261
262 This is a QShortcut convenience constructor which connects the shortcut's
263 \l{QShortcut::activated()}{activated()} signal to the \a functor and
264 \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
265 signal to the \a functorAmbiguous.
266
267 The \a functor can be a pointer to a member function of the
268 \a context1 object.
269 The \a functorAmbiguous can be a pointer to a member function of the
270 \a context2 object.
271
272 If the \a context1 object is destroyed, the \a functor will not be called.
273 If the \a context2 object is destroyed, the \a functorAmbiguous
274 will not be called.
275*/
276
277/*!
278 \fn template<typename Functor> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
279 \since 6.0
280 \overload
281
282 This is a QShortcut convenience constructor which connects the shortcut's
283 \l{QShortcut::activated()}{activated()} signal to the \a functor.
284*/
285/*!
286 \fn template<typename Functor> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, const QObject *context, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
287 \since 6.0
288 \overload
289
290 This is a QShortcut convenience constructor which connects the shortcut's
291 \l{QShortcut::activated()}{activated()} signal to the \a functor.
292
293 The \a functor can be a pointer to a member function of the \a context object.
294
295 If the \a context object is destroyed, the \a functor will not be called.
296*/
297/*!
298 \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, const QObject *context, Functor functor, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
299 \since 6.0
300 \overload
301
302 This is a QShortcut convenience constructor which connects the shortcut's
303 \l{QShortcut::activated()}{activated()} signal to the \a functor and
304 \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
305 signal to the \a functorAmbiguous.
306
307 The \a functor and \a functorAmbiguous can be a pointer to a member
308 function of the \a context object.
309
310 If the \a context object is destroyed, the \a functor and
311 \a functorAmbiguous will not be called.
312*/
313/*!
314 \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, const QObject *context1, Functor functor, const QObject *context2, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
315 \since 6.0
316 \overload
317
318 This is a QShortcut convenience constructor which connects the shortcut's
319 \l{QShortcut::activated()}{activated()} signal to the \a functor and
320 \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
321 signal to the \a functorAmbiguous.
322
323 The \a functor can be a pointer to a member function of the
324 \a context1 object.
325 The \a functorAmbiguous can be a pointer to a member function of the
326 \a context2 object.
327
328 If the \a context1 object is destroyed, the \a functor will not be called.
329 If the \a context2 object is destroyed, the \a functorAmbiguous
330 will not be called.
331*/
332
333/*!
334 Destroys the shortcut.
335*/
336QShortcut::~QShortcut()
337{
338 Q_D(QShortcut);
339 if (qApp) {
340 for (int id : std::as_const(t&: d->sc_ids))
341 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, owner: this);
342 }
343}
344
345/*!
346 \property QShortcut::key
347 \brief the shortcut's primary key sequence
348
349 This is a key sequence with an optional combination of Shift, Ctrl,
350 and Alt. The key sequence may be supplied in a number of ways:
351
352 \snippet code/src_gui_kernel_qshortcut.cpp 1
353
354 By default, this property contains an empty key sequence.
355*/
356void QShortcut::setKey(const QKeySequence &key)
357{
358 if (key.isEmpty())
359 setKeys({});
360 else
361 setKeys({ key });
362}
363
364QKeySequence QShortcut::key() const
365{
366 Q_D(const QShortcut);
367 if (d->sc_sequences.isEmpty())
368 return QKeySequence();
369 return d->sc_sequences.first();
370}
371
372/*!
373 Sets \a keys as the list of key sequences that trigger the
374 shortcut.
375
376 \since 6.0
377
378 \sa key, keys()
379*/
380void QShortcut::setKeys(const QList<QKeySequence> &keys)
381{
382 Q_D(QShortcut);
383 if (d->sc_sequences == keys)
384 return;
385 QAPP_CHECK("setKeys");
386 d->sc_sequences = keys;
387 d->redoGrab(map&: QGuiApplicationPrivate::instance()->shortcutMap);
388}
389
390/*!
391 Sets the triggers to those matching the standard key \a key.
392
393 \since 6.0
394
395 \sa key, keys()
396*/
397void QShortcut::setKeys(QKeySequence::StandardKey key)
398{
399 setKeys(QKeySequence::keyBindings(key));
400}
401
402/*!
403 Returns the list of key sequences which trigger this
404 shortcut.
405
406 \since 6.0
407 \sa key, setKeys()
408*/
409QList<QKeySequence> QShortcut::keys() const
410{
411 Q_D(const QShortcut);
412 return d->sc_sequences;
413}
414
415/*!
416 \property QShortcut::enabled
417 \brief whether the shortcut is enabled
418
419 An enabled shortcut emits the activated() or activatedAmbiguously()
420 signal when a QShortcutEvent occurs that matches the shortcut's
421 key() sequence.
422
423 If the application is in \c WhatsThis mode the shortcut will not emit
424 the signals, but will show the "What's This?" text instead.
425
426 By default, this property is \c true.
427
428 \sa whatsThis
429*/
430void QShortcut::setEnabled(bool enable)
431{
432 Q_D(QShortcut);
433 if (d->sc_enabled == enable)
434 return;
435 QAPP_CHECK("setEnabled");
436 d->sc_enabled = enable;
437 for (int id : d->sc_ids)
438 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable, id, owner: this);
439}
440
441bool QShortcut::isEnabled() const
442{
443 Q_D(const QShortcut);
444 return d->sc_enabled;
445}
446
447/*!
448 \property QShortcut::context
449 \brief the context in which the shortcut is valid
450
451 A shortcut's context decides in which circumstances a shortcut is
452 allowed to be triggered. The normal context is Qt::WindowShortcut,
453 which allows the shortcut to trigger if the parent (the widget
454 containing the shortcut) is a subwidget of the active top-level
455 window.
456
457 By default, this property is set to Qt::WindowShortcut.
458*/
459void QShortcut::setContext(Qt::ShortcutContext context)
460{
461 Q_D(QShortcut);
462 if (d->sc_context == context)
463 return;
464 QAPP_CHECK("setContext");
465 d->sc_context = context;
466 d->redoGrab(map&: QGuiApplicationPrivate::instance()->shortcutMap);
467}
468
469Qt::ShortcutContext QShortcut::context() const
470{
471 Q_D(const QShortcut);
472 return d->sc_context;
473}
474
475/*!
476 \property QShortcut::autoRepeat
477 \brief whether the shortcut can auto repeat
478
479 If true, the shortcut will auto repeat when the keyboard shortcut
480 combination is held down, provided that keyboard auto repeat is
481 enabled on the system.
482 The default value is true.
483*/
484void QShortcut::setAutoRepeat(bool on)
485{
486 Q_D(QShortcut);
487 if (d->sc_autorepeat == on)
488 return;
489 QAPP_CHECK("setAutoRepeat");
490 d->sc_autorepeat = on;
491 for (int id : d->sc_ids)
492 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(on, id, owner: this);
493}
494
495bool QShortcut::autoRepeat() const
496{
497 Q_D(const QShortcut);
498 return d->sc_autorepeat;
499}
500
501
502/*!
503 Sets the shortcut's "What's This?" help \a text.
504
505 The text will be shown when a widget application is in "What's
506 This?" mode and the user types the shortcut key() sequence.
507
508 To set "What's This?" help on a menu item (with or without a
509 shortcut key), set the help on the item's action.
510
511 By default, the help text is an empty string.
512
513 This function has no effect in applications that don't use
514 widgets.
515
516 \sa QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis()
517*/
518void QShortcut::setWhatsThis(const QString &text)
519{
520 Q_D(QShortcut);
521 d->sc_whatsthis = text;
522}
523
524/*!
525 Returns the shortcut's "What's This?" help text.
526
527 \sa setWhatsThis()
528*/
529QString QShortcut::whatsThis() const
530{
531 Q_D(const QShortcut);
532 return d->sc_whatsthis;
533}
534
535#if QT_DEPRECATED_SINCE(6,0)
536/*!
537 Returns the primary key binding's ID.
538
539 \deprecated
540
541 \sa QShortcutEvent::shortcutId()
542*/
543int QShortcut::id() const
544{
545 Q_D(const QShortcut);
546 if (d->sc_ids.isEmpty())
547 return 0;
548 return d->sc_ids.first();
549}
550#endif
551
552/*!
553 \fn QWidget *QShortcut::parentWidget() const
554
555 Returns the shortcut's parent widget.
556*/
557
558/*!
559 \internal
560*/
561bool QShortcut::event(QEvent *e)
562{
563 Q_D(QShortcut);
564 if (d->sc_enabled && e->type() == QEvent::Shortcut) {
565 auto se = static_cast<QShortcutEvent *>(e);
566 if (!d->handleWhatsThis()) {
567 Q_ASSERT_X(d->sc_ids.contains(se->shortcutId()), "QShortcut::event", "Received shortcut event from wrong shortcut");
568 if (se->isAmbiguous())
569 emit activatedAmbiguously();
570 else
571 emit activated();
572 return true;
573 }
574 }
575 return QObject::event(event: e);
576}
577
578QT_END_NAMESPACE
579
580#include "moc_qshortcut.cpp"
581

source code of qtbase/src/gui/kernel/qshortcut.cpp