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