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
4#include "qwidgetaction.h"
5#include "qwidget.h"
6#include "qdebug.h"
7
8#include "qwidgetaction_p.h"
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \class QWidgetAction
14 \since 4.2
15 \brief The QWidgetAction class extends QAction by an interface
16 for inserting custom widgets into action based containers, such
17 as toolbars.
18
19 \ingroup mainwindow-classes
20 \inmodule QtWidgets
21
22 Most actions in an application are represented as items in menus or
23 buttons in toolbars. However sometimes more complex widgets are
24 necessary. For example a zoom action in a word processor may be
25 realized using a QComboBox in a QToolBar, presenting a range
26 of different zoom levels. QToolBar provides QToolBar::insertWidget()
27 as convenience function for inserting a single widget.
28 However if you want to implement an action that uses custom
29 widgets for visualization in multiple containers then you have to
30 subclass QWidgetAction.
31
32 If a QWidgetAction is added for example to a QToolBar then
33 QWidgetAction::createWidget() is called. Reimplementations of that
34 function should create a new custom widget with the specified parent.
35
36 If the action is removed from a container widget then
37 QWidgetAction::deleteWidget() is called with the previously created custom
38 widget as argument. The default implementation hides the widget and deletes
39 it using QObject::deleteLater().
40
41 If you have only one single custom widget then you can set it as default
42 widget using setDefaultWidget(). That widget will then be used if the
43 action is added to a QToolBar, or in general to an action container that
44 supports QWidgetAction. If a QWidgetAction with only a default widget is
45 added to two toolbars at the same time then the default widget is shown
46 only in the first toolbar the action was added to. QWidgetAction takes
47 over ownership of the default widget.
48
49 Note that it is up to the widget to activate the action, for example by
50 reimplementing mouse event handlers and calling QAction::trigger().
51
52 \b {\macos}: If you add a widget to a menu in the application's menu
53 bar on \macos, the widget will be added and it will function but with some
54 limitations:
55 \list 1
56 \li The widget is reparented away from the QMenu to the native menu
57 view. If you show the menu in some other place (e.g. as a popup menu),
58 the widget will not be there.
59 \li Focus/Keyboard handling of the widget is not possible.
60 \li Due to Apple's design, mouse tracking on the widget currently does
61 not work.
62 \li Connecting the triggered() signal to a slot that opens a modal
63 dialog will cause a crash in \macos 10.4 (known bug acknowledged
64 by Apple), a workaround is to use a QueuedConnection instead of a
65 DirectConnection.
66 \endlist
67
68 \sa QAction, QActionGroup, QWidget
69*/
70
71/*!
72 Constructs an action with \a parent.
73*/
74QWidgetAction::QWidgetAction(QObject *parent)
75 : QAction(*(new QWidgetActionPrivate), parent)
76{
77}
78
79/*!
80 Destroys the object and frees allocated resources.
81*/
82QWidgetAction::~QWidgetAction()
83{
84 Q_D(QWidgetAction);
85 for (int i = 0; i < d->createdWidgets.size(); ++i)
86 disconnect(sender: d->createdWidgets.at(i), SIGNAL(destroyed(QObject*)),
87 receiver: this, SLOT(_q_widgetDestroyed(QObject*)));
88 QList<QWidget *> widgetsToDelete = d->createdWidgets;
89 d->createdWidgets.clear();
90 qDeleteAll(c: widgetsToDelete);
91 delete d->defaultWidget;
92}
93
94/*!
95 Sets \a widget to be the default widget. The ownership is
96 transferred to QWidgetAction. Unless createWidget() is
97 reimplemented by a subclass to return a new widget the default
98 widget is used when a container widget requests a widget through
99 requestWidget().
100*/
101void QWidgetAction::setDefaultWidget(QWidget *widget)
102{
103 Q_D(QWidgetAction);
104 if (widget == d->defaultWidget || d->defaultWidgetInUse)
105 return;
106 delete d->defaultWidget;
107 d->defaultWidget = widget;
108 if (!widget)
109 return;
110
111 setVisible(!(widget->isHidden() && widget->testAttribute(attribute: Qt::WA_WState_ExplicitShowHide)));
112 d->defaultWidget->hide();
113 d->defaultWidget->setParent(nullptr);
114 d->defaultWidgetInUse = false;
115 if (!isEnabled())
116 d->defaultWidget->setEnabled(false);
117}
118
119/*!
120 Returns the default widget.
121*/
122QWidget *QWidgetAction::defaultWidget() const
123{
124 Q_D(const QWidgetAction);
125 return d->defaultWidget;
126}
127
128/*!
129 Returns a widget that represents the action, with the given \a
130 parent.
131
132 Container widgets that support actions can call this function to
133 request a widget as visual representation of the action.
134
135 \sa releaseWidget(), createWidget(), defaultWidget()
136*/
137QWidget *QWidgetAction::requestWidget(QWidget *parent)
138{
139 Q_D(QWidgetAction);
140
141 QWidget *w = createWidget(parent);
142 if (!w) {
143 if (d->defaultWidgetInUse || !d->defaultWidget)
144 return nullptr;
145 d->defaultWidget->setParent(parent);
146 d->defaultWidgetInUse = true;
147 return d->defaultWidget;
148 }
149
150 connect(sender: w, SIGNAL(destroyed(QObject*)),
151 receiver: this, SLOT(_q_widgetDestroyed(QObject*)));
152 d->createdWidgets.append(t: w);
153 return w;
154}
155
156/*!
157 Releases the specified \a widget.
158
159 Container widgets that support actions call this function when a widget
160 action is removed.
161
162 \sa requestWidget(), deleteWidget(), defaultWidget()
163*/
164void QWidgetAction::releaseWidget(QWidget *widget)
165{
166 Q_D(QWidgetAction);
167
168 if (widget == d->defaultWidget) {
169 d->defaultWidget->hide();
170 d->defaultWidget->setParent(nullptr);
171 d->defaultWidgetInUse = false;
172 return;
173 }
174
175 if (!d->createdWidgets.contains(t: widget))
176 return;
177
178 disconnect(sender: widget, SIGNAL(destroyed(QObject*)),
179 receiver: this, SLOT(_q_widgetDestroyed(QObject*)));
180 d->createdWidgets.removeAll(t: widget);
181 deleteWidget(widget);
182}
183
184/*!
185 \reimp
186*/
187bool QWidgetAction::event(QEvent *event)
188{
189 Q_D(QWidgetAction);
190 if (event->type() == QEvent::ActionChanged) {
191 if (d->defaultWidget)
192 d->defaultWidget->setEnabled(isEnabled());
193 for (int i = 0; i < d->createdWidgets.size(); ++i)
194 d->createdWidgets.at(i)->setEnabled(isEnabled());
195 }
196 return QAction::event(event);
197}
198
199/*!
200 \reimp
201 */
202bool QWidgetAction::eventFilter(QObject *obj, QEvent *event)
203{
204 return QAction::eventFilter(watched: obj,event);
205}
206
207/*!
208 This function is called whenever the action is added to a container widget
209 that supports custom widgets. If you don't want a custom widget to be
210 used as representation of the action in the specified \a parent widget then
211 0 should be returned.
212
213 \sa deleteWidget()
214*/
215QWidget *QWidgetAction::createWidget(QWidget *parent)
216{
217 Q_UNUSED(parent);
218 return nullptr;
219}
220
221/*!
222 This function is called whenever the action is removed from a
223 container widget that displays the action using a custom \a
224 widget previously created using createWidget(). The default
225 implementation hides the \a widget and schedules it for deletion
226 using QObject::deleteLater().
227
228 \sa createWidget()
229*/
230void QWidgetAction::deleteWidget(QWidget *widget)
231{
232 widget->hide();
233 widget->deleteLater();
234}
235
236/*!
237 Returns the list of widgets that have been using createWidget() and
238 are currently in use by widgets the action has been added to.
239*/
240QList<QWidget *> QWidgetAction::createdWidgets() const
241{
242 Q_D(const QWidgetAction);
243 return d->createdWidgets;
244}
245
246QT_END_NAMESPACE
247
248#include "moc_qwidgetaction.cpp"
249

source code of qtbase/src/widgets/kernel/qwidgetaction.cpp