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
qquickaction.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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
9
10#include <QtCore/qpointer.h>
11#include <QtCore/qloggingcategory.h>
12#include <QtGui/qevent.h>
13#if QT_CONFIG(shortcut)
14# include <QtGui/private/qshortcutmap_p.h>
15#endif
16#include <QtGui/private/qguiapplication_p.h>
17#include <QtQuick/private/qquickitem_p.h>
18
20
21Q_STATIC_LOGGING_CATEGORY(lcAction, "qt.quick.controls.action")
22
23/*!
24 \qmltype Action
25 \inherits QtObject
26//! \nativetype QQuickAction
27 \inqmlmodule QtQuick.Controls
28 \since 5.10
29 \ingroup utilities
30 \brief Abstract user interface action.
31
32 Action represents an abstract user interface action that can have shortcuts
33 and can be assigned to menu items and toolbar buttons.
34
35 Actions may contain \l text, an \l icon, and a \l shortcut. Actions are normally
36 \l triggered by the user via menu items, toolbar buttons, or keyboard shortcuts.
37 A \l checkable Action toggles its \l checked state when triggered.
38
39 \snippet qtquickcontrols-action.qml action
40
41 Action is commonly used to implement application commands that can be invoked
42 via menu items, toolbar buttons, and keyboard shortcuts. Since the user expects
43 the commands to be performed in the same way, regardless of the user interface
44 used, it is useful to represent the commands as shareable actions.
45
46 Action can be also used to separate the logic and the visual presentation. For
47 example, when declaring buttons and menu items in \c .ui.qml files, actions can
48 be declared elsewhere and assigned from the outside.
49
50 \snippet qtquickcontrols-action.qml toolbutton
51
52 When an action is paired with buttons and menu items, the \c enabled, \c checkable,
53 and \c checked states are synced automatically. For example, in a word processor,
54 if the user clicks a "Bold" toolbar button, the "Bold" menu item will automatically
55 be checked. Buttons and menu items get their \c text and \c icon from the action by
56 default. An action-specific \c text or \c icon can be overridden for a specific
57 control by specifying \c text or \c icon directly on the control.
58
59 \snippet qtquickcontrols-action.qml menuitem
60
61 Since Action presents a user interface action, it is intended to be assigned to
62 a \l MenuItem, \l ToolButton, or any other control that inherits \l AbstractButton.
63 For keyboard shortcuts, the simpler \l Shortcut type is more appropriate.
64
65 \sa MenuItem, ToolButton, Shortcut
66*/
67
68/*!
69 \qmlsignal QtQuick.Controls::Action::toggled(QtObject source)
70
71 This signal is emitted when the action is toggled. The \a source argument
72 identifies the object that toggled the action.
73
74 For example, if the action is assigned to a menu item and a toolbar button, the
75 action is toggled when the control is toggled, the shortcut is activated, or
76 when \l toggle() is called directly.
77*/
78
79/*!
80 \qmlsignal QtQuick.Controls::Action::triggered(QtObject source)
81
82 This signal is emitted when the action is triggered. The \a source argument
83 identifies the object that triggered the action.
84
85 For example, if the action is assigned to a menu item and a toolbar button, the
86 action is triggered when the control is clicked, the shortcut is activated, or
87 when \l trigger() is called directly.
88*/
89
90#if QT_CONFIG(shortcut)
91static QKeySequence variantToKeySequence(const QVariant &var)
92{
93 if (var.metaType().id() == QMetaType::Int)
94 return QKeySequence(static_cast<QKeySequence::StandardKey>(var.toInt()));
95 return QKeySequence::fromString(var.toString());
96}
97
98QQuickActionPrivate::ShortcutEntry::ShortcutEntry(QObject *target)
99 : m_target(target)
100{
101}
102
103QQuickActionPrivate::ShortcutEntry::~ShortcutEntry()
104{
105 ungrab();
106}
107
108QObject *QQuickActionPrivate::ShortcutEntry::target() const
109{
110 return m_target;
111}
112
113int QQuickActionPrivate::ShortcutEntry::shortcutId() const
114{
115 return m_shortcutId;
116}
117
118void QQuickActionPrivate::ShortcutEntry::grab(const QKeySequence &shortcut, bool enabled)
119{
120 if (shortcut.isEmpty() || m_shortcutId)
121 return;
122
123 Qt::ShortcutContext context = Qt::WindowShortcut; // TODO
124 m_shortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(m_target, shortcut, context, QQuickShortcutContext::matcher);
125
126 if (!enabled)
127 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(false, m_shortcutId, m_target);
128}
129
130void QQuickActionPrivate::ShortcutEntry::ungrab()
131{
132 if (!m_shortcutId)
133 return;
134
135 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_shortcutId, m_target);
136 m_shortcutId = 0;
137}
138
139void QQuickActionPrivate::ShortcutEntry::setEnabled(bool enabled)
140{
141 if (!m_shortcutId)
142 return;
143
144 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, m_shortcutId, m_target);
145}
146
147QVariant QQuickActionPrivate::shortcut() const
148{
149 return vshortcut;
150}
151
152void QQuickActionPrivate::setShortcut(const QVariant &var)
153{
154 Q_Q(QQuickAction);
155 if (vshortcut == var)
156 return;
157
158 defaultShortcutEntry->ungrab();
159 for (QQuickActionPrivate::ShortcutEntry *entry : std::as_const(shortcutEntries))
160 entry->ungrab();
161
162 vshortcut = var;
163 keySequence = variantToKeySequence(var);
164
165 defaultShortcutEntry->grab(keySequence, enabled);
166 for (QQuickActionPrivate::ShortcutEntry *entry : std::as_const(shortcutEntries))
167 entry->grab(keySequence, enabled);
168
169 emit q->shortcutChanged(keySequence);
170}
171#endif // QT_CONFIG(shortcut)
172
173void QQuickActionPrivate::setEnabled(bool enable)
174{
175 Q_Q(QQuickAction);
176 if (enabled == enable)
177 return;
178
179 enabled = enable;
180
181#if QT_CONFIG(shortcut)
182 defaultShortcutEntry->setEnabled(enable);
183 for (QQuickActionPrivate::ShortcutEntry *entry : std::as_const(shortcutEntries))
184 entry->setEnabled(enable);
185#endif
186
187 emit q->enabledChanged(enable);
188}
189
190bool QQuickActionPrivate::watchItem(QQuickItem *item)
191{
192 Q_Q(QQuickAction);
193 if (!item)
194 return false;
195
196 item->installEventFilter(q);
197 QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed);
198 return true;
199}
200
201bool QQuickActionPrivate::unwatchItem(QQuickItem *item)
202{
203 Q_Q(QQuickAction);
204 if (!item)
205 return false;
206
207 item->removeEventFilter(q);
208 QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed);
209 return true;
210}
211
212void QQuickActionPrivate::registerItem(QQuickItem *item)
213{
214 if (!watchItem(item))
215 return;
216
217#if QT_CONFIG(shortcut)
218 QQuickActionPrivate::ShortcutEntry *entry = new QQuickActionPrivate::ShortcutEntry(item);
219 if (item->isVisible())
220 entry->grab(keySequence, enabled);
221 shortcutEntries += entry;
222
223 updateDefaultShortcutEntry();
224#endif
225}
226
227void QQuickActionPrivate::unregisterItem(QQuickItem *item)
228{
229#if QT_CONFIG(shortcut)
230 QQuickActionPrivate::ShortcutEntry *entry = findShortcutEntry(item);
231 if (!entry || !unwatchItem(item))
232 return;
233
234 shortcutEntries.removeOne(entry);
235 delete entry;
236
237 updateDefaultShortcutEntry();
238#else
239 Q_UNUSED(item);
240#endif
241}
242
244{
245#if QT_CONFIG(shortcut)
246 QQuickActionPrivate::ShortcutEntry *entry = findShortcutEntry(item);
247 if (!entry)
248 return;
249
250 if (item->isVisible())
251 entry->grab(keySequence, enabled);
252 else
253 entry->ungrab();
254
255 updateDefaultShortcutEntry();
256#else
257 Q_UNUSED(item);
258#endif
259}
260
261void QQuickActionPrivate::itemDestroyed(QQuickItem *item)
262{
263 unregisterItem(item);
264}
265
266#if QT_CONFIG(shortcut)
267bool QQuickActionPrivate::handleShortcutEvent(QObject *object, QShortcutEvent *event)
268{
269 Q_Q(QQuickAction);
270 if (event->key() != keySequence)
271 return false;
272
273 QQuickActionPrivate::ShortcutEntry *entry = findShortcutEntry(object);
274 if (!entry || event->shortcutId() != entry->shortcutId())
275 return false;
276
277 q->trigger(entry->target());
278 return true;
279}
280
281QQuickActionPrivate::ShortcutEntry *QQuickActionPrivate::findShortcutEntry(QObject *target) const
282{
283 Q_Q(const QQuickAction);
284 if (target == q)
285 return defaultShortcutEntry;
286 for (QQuickActionPrivate::ShortcutEntry *entry : shortcutEntries) {
287 if (entry->target() == target)
288 return entry;
289 }
290 return nullptr;
291}
292
293void QQuickActionPrivate::updateDefaultShortcutEntry()
294{
295 bool hasActiveShortcutEntries = false;
296 for (QQuickActionPrivate::ShortcutEntry *entry : std::as_const(shortcutEntries)) {
297 if (entry->shortcutId()) {
298 hasActiveShortcutEntries = true;
299 break;
300 }
301 }
302
303 if (hasActiveShortcutEntries)
304 defaultShortcutEntry->ungrab();
305 else if (!defaultShortcutEntry->shortcutId())
306 defaultShortcutEntry->grab(keySequence, enabled);
307}
308#endif // QT_CONFIG(shortcut)
309
310QQuickAction::QQuickAction(QObject *parent)
311 : QObject(*(new QQuickActionPrivate), parent)
312{
313#if QT_CONFIG(shortcut)
314 Q_D(QQuickAction);
315 d->defaultShortcutEntry = new QQuickActionPrivate::ShortcutEntry(this);
316#endif
317}
318
319QQuickAction::~QQuickAction()
320{
321 Q_D(QQuickAction);
322 qCDebug(lcAction) << "destroying" << this << d->text;
323 if (d->group)
324 d->group->removeAction(this);
325
326#if QT_CONFIG(shortcut)
327 for (QQuickActionPrivate::ShortcutEntry *entry : std::as_const(d->shortcutEntries))
328 d->unwatchItem(qobject_cast<QQuickItem *>(entry->target()));
329
330 qDeleteAll(d->shortcutEntries);
331 delete d->defaultShortcutEntry;
332#endif
333}
334
335/*!
336 \qmlproperty string QtQuick.Controls::Action::text
337
338 This property holds a textual description of the action.
339*/
340QString QQuickAction::text() const
341{
342 Q_D(const QQuickAction);
343 return d->text;
344}
345
346void QQuickAction::setText(const QString &text)
347{
348 Q_D(QQuickAction);
349 if (d->text == text)
350 return;
351
352 d->text = text;
353 emit textChanged(text);
354}
355
356/*!
357 \qmlproperty string QtQuick.Controls::Action::icon.name
358 \qmlproperty url QtQuick.Controls::Action::icon.source
359 \qmlproperty int QtQuick.Controls::Action::icon.width
360 \qmlproperty int QtQuick.Controls::Action::icon.height
361 \qmlproperty color QtQuick.Controls::Action::icon.color
362 \qmlproperty bool QtQuick.Controls::Action::icon.cache
363
364 \include qquickicon.qdocinc grouped-properties
365*/
366QQuickIcon QQuickAction::icon() const
367{
368 Q_D(const QQuickAction);
369 return d->icon;
370}
371
372void QQuickAction::setIcon(const QQuickIcon &icon)
373{
374 Q_D(QQuickAction);
375 if (d->icon == icon)
376 return;
377
378 d->icon = icon;
379 d->icon.ensureRelativeSourceResolved(this);
380 emit iconChanged(icon);
381}
382
383/*!
384 \qmlproperty bool QtQuick.Controls::Action::enabled
385
386 This property holds whether the action is enabled. The default value is \c true.
387*/
388bool QQuickAction::isEnabled() const
389{
390 Q_D(const QQuickAction);
391 return d->enabled && (!d->group || d->group->isEnabled());
392}
393
394void QQuickAction::setEnabled(bool enabled)
395{
396 Q_D(QQuickAction);
397 d->explicitEnabled = true;
398 d->setEnabled(enabled);
399}
400
401void QQuickAction::resetEnabled()
402{
403 Q_D(QQuickAction);
404 if (!d->explicitEnabled)
405 return;
406
407 d->explicitEnabled = false;
408 d->setEnabled(true);
409}
410
411/*!
412 \qmlproperty bool QtQuick.Controls::Action::checked
413
414 This property holds whether the action is checked.
415
416 \sa checkable
417*/
418bool QQuickAction::isChecked() const
419{
420 Q_D(const QQuickAction);
421 return d->checked;
422}
423
424void QQuickAction::setChecked(bool checked)
425{
426 Q_D(QQuickAction);
427 if (d->checked == checked)
428 return;
429
430 d->checked = checked;
431 emit checkedChanged(checked);
432}
433
434/*!
435 \qmlproperty bool QtQuick.Controls::Action::checkable
436
437 This property holds whether the action is checkable. The default value is \c false.
438
439 A checkable action toggles between checked (on) and unchecked (off) when triggered.
440
441 \sa checked
442*/
443bool QQuickAction::isCheckable() const
444{
445 Q_D(const QQuickAction);
446 return d->checkable;
447}
448
449void QQuickAction::setCheckable(bool checkable)
450{
451 Q_D(QQuickAction);
452 if (d->checkable == checkable)
453 return;
454
455 d->checkable = checkable;
456 emit checkableChanged(checkable);
457}
458
459#if QT_CONFIG(shortcut)
460/*!
461 \qmlproperty keysequence QtQuick.Controls::Action::shortcut
462
463 This property holds the action's shortcut. The key sequence can be set
464 to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts},
465 or it can be described with a string containing a sequence of up to four
466 key presses that are needed to trigger the shortcut.
467
468 \code
469 Action {
470 shortcut: "Ctrl+E,Ctrl+W"
471 onTriggered: edit.wrapMode = TextEdit.Wrap
472 }
473 \endcode
474*/
475QKeySequence QQuickAction::shortcut() const
476{
477 Q_D(const QQuickAction);
478 return d->keySequence;
479}
480
481void QQuickAction::setShortcut(const QKeySequence &shortcut)
482{
483 Q_D(QQuickAction);
484 d->setShortcut(shortcut.toString());
485}
486#endif // QT_CONFIG(shortcut)
487
488/*!
489 \qmlmethod void QtQuick.Controls::Action::toggle(QtObject source)
490
491 Toggles the action and emits \l toggled() if enabled, with an optional \a source object defined.
492*/
493void QQuickAction::toggle(QObject *source)
494{
495 Q_D(QQuickAction);
496 if (!d->enabled)
497 return;
498
499 if (d->checkable)
500 setChecked(!d->checked);
501
502 emit toggled(source);
503}
504
505/*!
506 \qmlmethod void QtQuick.Controls::Action::trigger(QtObject source)
507
508 Triggers the action and emits \l triggered() if enabled, with an optional \a source object defined.
509*/
510void QQuickAction::trigger(QObject *source)
511{
512 Q_D(QQuickAction);
513 d->trigger(source, true);
514}
515
516void QQuickActionPrivate::trigger(QObject* source, bool doToggle)
517{
518 Q_Q(QQuickAction);
519 if (!enabled)
520 return;
521
522 QPointer<QObject> guard = q;
523 // the checked action of an exclusive group cannot be unchecked
524 if (checkable && (!checked || !group || !group->isExclusive() || group->checkedAction() != q)) {
525 if (doToggle)
526 q->toggle(source);
527 else
528 emit q->toggled(source);
529 }
530
531 if (!guard.isNull())
532 emit q->triggered(source);
533}
534
535bool QQuickAction::event(QEvent *event)
536{
537#if QT_CONFIG(shortcut)
538 Q_D(QQuickAction);
539 if (event->type() == QEvent::Shortcut)
540 return d->handleShortcutEvent(this, static_cast<QShortcutEvent *>(event));
541#endif
542 return QObject::event(event);
543}
544
545bool QQuickAction::eventFilter(QObject *object, QEvent *event)
546{
547#if QT_CONFIG(shortcut)
548 Q_D(QQuickAction);
549 if (event->type() == QEvent::Shortcut)
550 return d->handleShortcutEvent(object, static_cast<QShortcutEvent *>(event));
551#else
552 Q_UNUSED(object);
553 Q_UNUSED(event);
554#endif
555 return false;
556}
557
558QT_END_NAMESPACE
559
560#include "moc_qquickaction_p.cpp"
void itemDestroyed(QQuickItem *item) override
void registerItem(QQuickItem *item)
bool watchItem(QQuickItem *item)
void itemVisibilityChanged(QQuickItem *item) override
bool unwatchItem(QQuickItem *item)
void unregisterItem(QQuickItem *item)