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 | |
4 | #include "qquickmenuitem_p.h" |
5 | #include "qquickmenuitem_p_p.h" |
6 | #include "qquickmenu_p_p.h" |
7 | #include "qquickdeferredexecute_p_p.h" |
8 | |
9 | #include <QtGui/qpa/qplatformtheme.h> |
10 | #include <QtQuick/private/qquickevents_p_p.h> |
11 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | /*! |
15 | \qmltype MenuItem |
16 | \inherits AbstractButton |
17 | //! \nativetype QQuickMenuItem |
18 | \inqmlmodule QtQuick.Controls |
19 | \since 5.7 |
20 | \ingroup qtquickcontrols-menus |
21 | \brief Presents an item within a Menu. |
22 | |
23 | MenuItem is a convenience type that implements the AbstractButton API, |
24 | providing a familiar way to respond to menu items being \l triggered, for |
25 | example. |
26 | |
27 | MenuItem inherits its API from AbstractButton. For instance, you can set |
28 | \l {AbstractButton::text}{text} and \l {Icons in Qt Quick Controls}{icon} |
29 | using the AbstractButton API. |
30 | |
31 | \code |
32 | Button { |
33 | id: fileButton |
34 | text: "File" |
35 | onClicked: menu.open() |
36 | |
37 | Menu { |
38 | id: menu |
39 | |
40 | MenuItem { |
41 | text: "New..." |
42 | onTriggered: document.reset() |
43 | } |
44 | MenuItem { |
45 | text: "Open..." |
46 | onTriggered: openDialog.open() |
47 | } |
48 | MenuItem { |
49 | text: "Save" |
50 | onTriggered: saveDialog.open() |
51 | } |
52 | } |
53 | } |
54 | \endcode |
55 | |
56 | \sa {Customizing Menu}, Menu, {Menu Controls} |
57 | */ |
58 | |
59 | /*! |
60 | \qmlproperty bool QtQuick.Controls::MenuItem::textPadding |
61 | \readonly |
62 | \since 6.8 |
63 | |
64 | This property holds the maximum \l implicitTextPadding found |
65 | among all the menu items inside the same \l menu. |
66 | |
67 | This property can be used by the style to ensure that all MenuItems |
68 | inside the same Menu end up aligned with respect to the |
69 | \l {AbstractButton::} {text}. |
70 | |
71 | A \l Menu can consist of meny different MenuItems, some can be checkable, |
72 | some can have an icon, and some will just contain text. And very often, |
73 | a style wants to make sure that the text inside all of them ends up |
74 | left-aligned (or right-aligned for \l {Control::} {mirrored} items). |
75 | By letting each MenuItem assign its own minimum text padding to |
76 | \l implicitTextPadding (taking icons and checkmarks into account), but |
77 | using \l textPadding to actually position the |
78 | \l {AbstractButton::} {text}, all MenuItems should end up being aligned |
79 | |
80 | In order for this to work, all MenuItems should set \l implicitTextPadding |
81 | to be the minimum space needed from the left edge of the |
82 | \l {Control::} {contentItem} to the text. |
83 | |
84 | \sa implicitTextPadding |
85 | */ |
86 | |
87 | /*! |
88 | \qmlproperty bool QtQuick.Controls::MenuItem::implicitTextPadding |
89 | \since 6.8 |
90 | |
91 | This property holds the minimum space needed from the left edge of the |
92 | \l {Control::} {contentItem} to the text. It's used to calculate a common |
93 | \l textPadding among all the MenuItems inside a \l Menu. |
94 | |
95 | \sa textPadding |
96 | */ |
97 | |
98 | void QQuickMenuItemPrivate::(QQuickMenu *) |
99 | { |
100 | Q_Q(QQuickMenuItem); |
101 | if (menu == newMenu) |
102 | return; |
103 | |
104 | menu = newMenu; |
105 | emit q->menuChanged(); |
106 | } |
107 | |
108 | void QQuickMenuItemPrivate::(QQuickMenu *) |
109 | { |
110 | Q_Q(QQuickMenuItem); |
111 | if (subMenu == newSubMenu) |
112 | return; |
113 | |
114 | if (subMenu) { |
115 | QObject::disconnect(sender: subMenu, signal: &QQuickMenu::titleChanged, receiver: q, slot: &QQuickAbstractButton::setText); |
116 | QObject::disconnect(sender: subMenu, signal: &QQuickMenu::iconChanged, receiver: q, slot: &QQuickAbstractButton::setIcon); |
117 | QObjectPrivate::disconnect(sender: subMenu, signal: &QQuickPopup::enabledChanged, receiverPrivate: this, slot: &QQuickMenuItemPrivate::updateEnabled); |
118 | } |
119 | |
120 | if (newSubMenu) { |
121 | QObject::connect(sender: newSubMenu, signal: &QQuickMenu::titleChanged, context: q, slot: &QQuickAbstractButton::setText); |
122 | QObject::connect(sender: newSubMenu, signal: &QQuickMenu::iconChanged, context: q, slot: &QQuickAbstractButton::setIcon); |
123 | QObjectPrivate::connect(sender: newSubMenu, signal: &QQuickPopup::enabledChanged, receiverPrivate: this, slot: &QQuickMenuItemPrivate::updateEnabled); |
124 | q->setText(newSubMenu->title()); |
125 | q->setIcon(newSubMenu->icon()); |
126 | } |
127 | |
128 | subMenu = newSubMenu; |
129 | updateEnabled(); |
130 | emit q->subMenuChanged(); |
131 | } |
132 | |
133 | void QQuickMenuItemPrivate::() |
134 | { |
135 | Q_Q(QQuickMenuItem); |
136 | q->setEnabled(subMenu && subMenu->isEnabled()); |
137 | } |
138 | |
139 | static inline QString arrowName() { return QStringLiteral("arrow" ); } |
140 | |
141 | void QQuickMenuItemPrivate::() |
142 | { |
143 | Q_Q(QQuickAbstractButton); |
144 | quickCancelDeferred(object: q, property: arrowName()); |
145 | } |
146 | |
147 | void QQuickMenuItemPrivate::(bool complete) |
148 | { |
149 | Q_Q(QQuickMenuItem); |
150 | if (arrow.wasExecuted()) |
151 | return; |
152 | |
153 | if (!arrow || complete) |
154 | quickBeginDeferred(object: q, property: arrowName(), delegate&: arrow); |
155 | if (complete) |
156 | quickCompleteDeferred(object: q, property: arrowName(), delegate&: arrow); |
157 | } |
158 | |
159 | bool QQuickMenuItemPrivate::(Qt::Key key) const |
160 | { |
161 | return key == Qt::Key_Return || key == Qt::Key_Enter |
162 | || QQuickAbstractButtonPrivate::acceptKeyClick(key); |
163 | } |
164 | |
165 | QPalette QQuickMenuItemPrivate::() const |
166 | { |
167 | return QQuickTheme::palette(scope: QQuickTheme::Menu); |
168 | } |
169 | |
170 | /*! |
171 | \qmlsignal void QtQuick.Controls::MenuItem::triggered() |
172 | |
173 | This signal is emitted when the menu item is triggered by the user. |
174 | */ |
175 | |
176 | QQuickMenuItem::(QQuickItem *parent) |
177 | : QQuickAbstractButton(*(new QQuickMenuItemPrivate), parent) |
178 | { |
179 | connect(sender: this, signal: &QQuickAbstractButton::clicked, context: this, slot: &QQuickMenuItem::triggered); |
180 | } |
181 | |
182 | /*! |
183 | \qmlproperty bool QtQuick.Controls::MenuItem::highlighted |
184 | |
185 | This property holds whether the menu item is highlighted by the user. |
186 | |
187 | A menu item can be highlighted by mouse hover or keyboard navigation. |
188 | |
189 | The default value is \c false. |
190 | |
191 | \sa Menu::currentIndex |
192 | */ |
193 | bool QQuickMenuItem::() const |
194 | { |
195 | Q_D(const QQuickMenuItem); |
196 | return d->highlighted; |
197 | } |
198 | |
199 | void QQuickMenuItem::(bool highlighted) |
200 | { |
201 | Q_D(QQuickMenuItem); |
202 | if (highlighted == d->highlighted) |
203 | return; |
204 | |
205 | d->highlighted = highlighted; |
206 | emit highlightedChanged(); |
207 | } |
208 | |
209 | /*! |
210 | \since QtQuick.Controls 2.3 (Qt 5.10) |
211 | \qmlproperty Item QtQuick.Controls::MenuItem::arrow |
212 | |
213 | This property holds the sub-menu arrow item. |
214 | |
215 | \sa {Customizing Menu} |
216 | */ |
217 | QQuickItem *QQuickMenuItem::() const |
218 | { |
219 | QQuickMenuItemPrivate *d = const_cast<QQuickMenuItemPrivate *>(d_func()); |
220 | if (!d->arrow) |
221 | d->executeArrow(); |
222 | return d->arrow; |
223 | } |
224 | |
225 | void QQuickMenuItem::(QQuickItem *arrow) |
226 | { |
227 | Q_D(QQuickMenuItem); |
228 | if (d->arrow == arrow) |
229 | return; |
230 | |
231 | if (!d->arrow.isExecuting()) |
232 | d->cancelArrow(); |
233 | |
234 | QQuickControlPrivate::hideOldItem(item: d->arrow); |
235 | d->arrow = arrow; |
236 | if (arrow && !arrow->parentItem()) |
237 | arrow->setParentItem(this); |
238 | if (!d->arrow.isExecuting()) |
239 | emit arrowChanged(); |
240 | } |
241 | |
242 | /*! |
243 | \since QtQuick.Controls 2.3 (Qt 5.10) |
244 | \qmlproperty Menu QtQuick.Controls::MenuItem::menu |
245 | \readonly |
246 | |
247 | This property holds the menu that contains this menu item, |
248 | or \c null if the item is not in a menu. |
249 | */ |
250 | QQuickMenu *QQuickMenuItem::() const |
251 | { |
252 | Q_D(const QQuickMenuItem); |
253 | return d->menu; |
254 | } |
255 | |
256 | /*! |
257 | \since QtQuick.Controls 2.3 (Qt 5.10) |
258 | \qmlproperty Menu QtQuick.Controls::MenuItem::subMenu |
259 | \readonly |
260 | |
261 | This property holds the sub-menu that this item presents in |
262 | the parent menu, or \c null if this item is not a sub-menu item. |
263 | */ |
264 | QQuickMenu *QQuickMenuItem::() const |
265 | { |
266 | Q_D(const QQuickMenuItem); |
267 | return d->subMenu; |
268 | } |
269 | |
270 | void QQuickMenuItem::() |
271 | { |
272 | Q_D(QQuickMenuItem); |
273 | d->executeArrow(complete: true); |
274 | QQuickAbstractButton::componentComplete(); |
275 | } |
276 | |
277 | QFont QQuickMenuItem::() const |
278 | { |
279 | return QQuickTheme::font(scope: QQuickTheme::Menu); |
280 | } |
281 | |
282 | qreal QQuickMenuItem::() const |
283 | { |
284 | return d_func()->implicitTextPadding; |
285 | } |
286 | |
287 | void QQuickMenuItem::(qreal newImplicitTextPadding) |
288 | { |
289 | Q_D(QQuickMenuItem); |
290 | if (qFuzzyCompare(p1: d->implicitTextPadding, p2: newImplicitTextPadding)) |
291 | return; |
292 | d->implicitTextPadding = newImplicitTextPadding; |
293 | emit implicitTextPaddingChanged(); |
294 | } |
295 | |
296 | qreal QQuickMenuItem::() const |
297 | { |
298 | Q_D(const QQuickMenuItem); |
299 | return d->menu ? QQuickMenuPrivate::get(menu: d->menu)->textPadding : 0; |
300 | } |
301 | |
302 | #if QT_CONFIG(accessibility) |
303 | QAccessible::Role QQuickMenuItem::() const |
304 | { |
305 | return QAccessible::MenuItem; |
306 | } |
307 | #endif |
308 | |
309 | #ifndef QT_NO_DEBUG_STREAM |
310 | QDebug (QDebug debug, const QQuickMenuItem *) |
311 | { |
312 | QDebugStateSaver saver(debug); |
313 | debug.nospace(); |
314 | if (!menuItem) { |
315 | debug << "QQuickMenuItem(nullptr)" ; |
316 | return debug; |
317 | } |
318 | |
319 | debug << menuItem->metaObject()->className() << '(' << static_cast<const void *>(menuItem); |
320 | if (!menuItem->objectName().isEmpty()) |
321 | debug << ", name=" << menuItem->objectName(); |
322 | debug << ", text=" << menuItem->text(); |
323 | debug << ')'; |
324 | return debug; |
325 | } |
326 | #endif // QT_NO_DEBUG_STREAM |
327 | |
328 | QT_END_NAMESPACE |
329 | |
330 | #include "moc_qquickmenuitem_p.cpp" |
331 | |