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
qquickmenuitemiconlabel.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 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 <QtQuickTemplates2/private/qquickaction_p.h>
11#include <QtQuickTemplates2/private/qquickmenuitem_p.h>
12
14
15QQuickMenuItemIconLabelPrivate::~QQuickMenuItemIconLabelPrivate() = default;
16
17bool QQuickMenuItemIconLabelPrivate::hasShortcut() const
18{
19 // See comment in layout() for why we don't support TextUnderIcon for shortcuts.
20 return (display == QQuickIconLabel::TextOnly || display == QQuickIconLabel::TextBesideIcon)
21 // Don't store this statically because we want to be able to auto-test it.
22 && !QCoreApplication::testAttribute(Qt::AA_DontShowShortcutsInContextMenus)
23 && !shortcut().isEmpty();
24}
25
26QKeySequence QQuickMenuItemIconLabelPrivate::shortcut() const
27{
28 return menuItem && menuItem->action() ? menuItem->action()->shortcut() : QKeySequence();
29}
30
31bool QQuickMenuItemIconLabelPrivate::createShortcutLabel()
32{
33 Q_Q(QQuickIconLabel);
34 if (shortcutLabel)
35 return false;
36
37 shortcutLabel = new QQuickText(q);
38 watchChanges(shortcutLabel);
39 beginClass(shortcutLabel);
40 shortcutLabel->setObjectName(QStringLiteral("shortcutLabel"));
41 shortcutLabel->setFont(font);
42 shortcutLabel->setColor(color);
43 // We don't set elide because it shouldn't.
44 shortcutLabel->setVAlign(QQuickText::AlignVCenter);
45 shortcutLabel->setHAlign(QQuickText::AlignHCenter);
46 shortcutLabel->setText(shortcut().toString(QKeySequence::NativeText));
47 if (componentComplete)
48 completeComponent(shortcutLabel);
49 return true;
50}
51
52bool QQuickMenuItemIconLabelPrivate::destroyShortcutLabel()
53{
54 if (!shortcutLabel)
55 return false;
56
57 unwatchChanges(shortcutLabel);
58 delete shortcutLabel;
59 shortcutLabel = nullptr;
60 return true;
61}
62
63void QQuickMenuItemIconLabelPrivate::updateOrSyncShortcutLabel()
64{
65 if (updateShortcutLabel()) {
66 if (componentComplete) {
67 updateImplicitSize();
68 layout();
69 }
70 } else {
71 syncShortcutLabel();
72 }
73}
74
75// This and layout should be kept in sync with the base class functions.
76// I couldn't think of an elegant way to use polymorphism here.
77void QQuickMenuItemIconLabelPrivate::updateImplicitSize()
78{
79 Q_Q(QQuickMenuItemIconLabel);
80 const bool showIcon = image && hasIcon();
81 const bool showText = label && hasText();
82 const bool showShortcutText = shortcutLabel && !shortcut().isEmpty();
83 const qreal horizontalPadding = leftPadding + rightPadding;
84 const qreal verticalPadding = topPadding + bottomPadding;
85 const qreal iconImplicitWidth = showIcon ? image->implicitWidth() : 0;
86 const qreal iconImplicitHeight = showIcon ? image->implicitHeight() : 0;
87 const qreal textImplicitWidth = showText ? label->implicitWidth() : 0;
88 const qreal textImplicitHeight = showText ? label->implicitHeight() : 0;
89 const qreal shortcutTextImplicitWidth = showShortcutText ? shortcutLabel->implicitWidth() : 0;
90 const qreal shortcutTextImplicitHeight = showShortcutText ? shortcutLabel->implicitHeight() : 0;
91 qreal effectiveSpacing = showText && showIcon && image->implicitWidth() > 0 ? spacing : 0;
92 if (showShortcutText && shortcutLabel->implicitWidth() > 0)
93 effectiveSpacing += spacing;
94 const qreal implicitWidth = display == QQuickIconLabel::TextBesideIcon
95 ? iconImplicitWidth + textImplicitWidth + shortcutTextImplicitHeight + effectiveSpacing
96 : qMax(qMax(iconImplicitWidth, textImplicitWidth), shortcutTextImplicitWidth);
97 const qreal implicitHeight = display == QQuickIconLabel::TextUnderIcon
98 ? iconImplicitHeight + textImplicitHeight + shortcutTextImplicitHeight + effectiveSpacing
99 : qMax(qMax(iconImplicitHeight, textImplicitHeight), shortcutTextImplicitHeight);
100 q->setImplicitSize(implicitWidth + horizontalPadding, implicitHeight + verticalPadding);
101}
102
103void QQuickMenuItemIconLabelPrivate::layout()
104{
105 Q_Q(QQuickIconLabel);
106 if (!componentComplete)
107 return;
108
109 const qreal availableWidth = width - leftPadding - rightPadding;
110 const qreal availableHeight = height - topPadding - bottomPadding;
111
112 auto layoutShortcutLabel = [this, availableWidth, availableHeight](const qreal availableWidthForShortcut){
113 if (availableWidthForShortcut - spacing - shortcutLabel->implicitWidth() > 0) {
114 // There's enough space for everyone.
115 shortcutLabel->setVisible(true);
116
117 // We can simply align ourselves to the right of the entire available space.
118 const QRectF shortcutTextRect = alignedRect(mirrored, Qt::AlignRight | Qt::AlignVCenter, QSizeF(
119 qMin(shortcutLabel->implicitWidth(), availableWidth),
120 qMin(shortcutLabel->implicitHeight(), availableHeight)),
121 QRectF(leftPadding, topPadding, availableWidth, availableHeight));
122 shortcutLabel->setSize(shortcutTextRect.size());
123 shortcutLabel->setPosition(shortcutTextRect.topLeft());
124 } else {
125 // No space for us. As Widgets doesn't bother with eliding, neither do we; we just
126 // don't show the shortcut if there's no room for it. The most important part of the
127 // menu item is the text, anyway.
128 shortcutLabel->setVisible(false);
129 }
130 };
131
132 switch (display) {
133 case QQuickIconLabel::IconOnly:
134 QQuickIconLabelPrivate::layout();
135 return;
136 case QQuickIconLabel::TextOnly: {
137 if (label) {
138 const QRectF textRect = alignedRect(mirrored, alignment, QSizeF(
139 qMin(label->implicitWidth(), availableWidth),
140 qMin(label->implicitHeight(), availableHeight)),
141 QRectF(leftPadding, topPadding, availableWidth, availableHeight));
142 label->setSize(textRect.size());
143 label->setPosition(textRect.topLeft());
144 }
145
146 if (shortcutLabel)
147 layoutShortcutLabel(availableWidth - (label->width() + spacing));
148 break;
149 } case QQuickIconLabel::TextUnderIcon: {
150 // TextUnderIcon doesn't make sense for menu items, but we don't need to break
151 // any existing behaviour, so while we don't support showing shortcuts for it, we can
152 // continue doing what we were doing before.
153 QQuickIconLabelPrivate::layout();
154 return;
155 }
156 case QQuickIconLabel::TextBesideIcon:
157 default:
158 QSizeF iconSize(0, 0);
159 QSizeF textSize(0, 0);
160 QSizeF shortcutLabelSize(0, 0);
161 if (image) {
162 iconSize.setWidth(qMin(image->implicitWidth(), availableWidth));
163 iconSize.setHeight(qMin(image->implicitHeight(), availableHeight));
164 }
165 qreal spacingBetweenIconAndLabel = 0;
166 if (label) {
167 if (!iconSize.isEmpty())
168 spacingBetweenIconAndLabel = spacing;
169 textSize.setWidth(qMin(label->implicitWidth(), availableWidth - iconSize.width()
170 - spacingBetweenIconAndLabel));
171 textSize.setHeight(qMin(label->implicitHeight(), availableHeight));
172 }
173
174 qreal spacingBetweenLabelAndShortcutLabel = 0;
175 if (shortcutLabel) {
176 if (!textSize.isEmpty())
177 spacingBetweenLabelAndShortcutLabel = spacing;
178 shortcutLabelSize.setWidth(qMin(shortcutLabel->implicitWidth(), availableWidth - iconSize.width()
179 - spacingBetweenIconAndLabel - textSize.width() - spacingBetweenLabelAndShortcutLabel));
180 shortcutLabelSize.setHeight(qMin(shortcutLabel->implicitHeight(), availableHeight));
181 }
182
183 /*!
184 For IconLabel, the layout would look like this:
185
186 +-------+-+--------------+
187 | | | |
188 | Icon | | Text |
189 | | | |
190 +-------+-+--------------+
191
192 For us, it looks like this:
193
194 +-------+-+--------------+-+------------+
195 | | | | | |
196 | Icon | | Text | | Shortcut |
197 | | | | | |
198 +-------+-+--------------+-+------------+
199 */
200 const QRectF combinedImageAndLabelRect = alignedRect(mirrored, alignment, QSizeF(
201 iconSize.width() + spacingBetweenIconAndLabel + textSize.width(),
202 qMax(iconSize.height(), textSize.height())),
203 QRectF(leftPadding, topPadding, availableWidth, availableHeight));
204 if (image) {
205 const QRectF iconRect = alignedRect(mirrored, Qt::AlignLeft | Qt::AlignVCenter, iconSize, combinedImageAndLabelRect);
206 image->setSize(iconRect.size());
207 image->snapPositionTo(iconRect.topLeft());
208 }
209 if (label) {
210 const QRectF textRect = alignedRect(mirrored, Qt::AlignRight | Qt::AlignVCenter, textSize, combinedImageAndLabelRect);
211 label->setSize(textRect.size());
212 label->setPosition(textRect.topLeft());
213 }
214 if (shortcutLabel)
215 layoutShortcutLabel(availableWidth - combinedImageAndLabelRect.width());
216 break;
217 }
218
219 q->setBaselineOffset(label ? label->y() + label->baselineOffset() : 0);
220}
221
222void QQuickMenuItemIconLabelPrivate::itemDestroyed(QQuickItem *item)
223{
224 QQuickIconLabelPrivate::itemDestroyed(item);
225
226 if (item == shortcutLabel)
227 shortcutLabel = nullptr;
228}
229
230void QQuickMenuItemIconLabelPrivate::textChange()
231{
232 updateOrSyncShortcutLabel();
233}
234
235void QQuickMenuItemIconLabelPrivate::displayChange()
236{
237 updateOrSyncShortcutLabel();
238}
239
240bool QQuickMenuItemIconLabelPrivate::updateShortcutLabel()
241{
242 if (!hasShortcut())
243 return destroyShortcutLabel();
244 return createShortcutLabel();
245}
246
247void QQuickMenuItemIconLabelPrivate::syncShortcutLabel()
248{
249 if (!shortcutLabel)
250 return;
251
252 shortcutLabel->setText(shortcut().toString(QKeySequence::NativeText));
253}
254
255QQuickMenuItemIconLabel::~QQuickMenuItemIconLabel()
256{
257 Q_D(QQuickMenuItemIconLabel);
258 if (d->shortcutLabel)
259 d->unwatchChanges(d->shortcutLabel);
260}
261
262QQuickMenuItemIconLabel::QQuickMenuItemIconLabel(QQuickItem *parent)
263 : QQuickIconLabel(*(new QQuickMenuItemIconLabelPrivate), parent)
264{
265}
266
267QQuickMenuItem *QQuickMenuItemIconLabel::menuItem() const
268{
269 Q_D(const QQuickMenuItemIconLabel);
270 return d->menuItem;
271}
272
273void QQuickMenuItemIconLabel::setMenuItem(QQuickMenuItem *menuItem)
274{
275 Q_D(QQuickMenuItemIconLabel);
276 if (d->menuItem == menuItem)
277 return;
278
279 d->menuItem = menuItem;
280 d->updateOrSyncShortcutLabel();
281 emit menuItemChanged();
282}
283
284void QQuickMenuItemIconLabel::componentComplete()
285{
286 Q_D(QQuickMenuItemIconLabel);
287 if (d->shortcutLabel)
288 QQuickIconLabelPrivate::completeComponent(d->shortcutLabel);
289
290 QQuickIconLabel::componentComplete();
291
292 // See QQuickIconLabel::componentComplete for why we do this.
293 const auto paintOrderChildItems = QQuickItemPrivate::get(this)->paintOrderChildItems();
294 const auto bottomMostFosterChildIt = std::find_if(paintOrderChildItems.constBegin(),
295 paintOrderChildItems.constEnd(), [d](QQuickItem *item) {
296 return item != d->label && item != d->image && item != d->shortcutLabel;
297 });
298 if (bottomMostFosterChildIt != paintOrderChildItems.constEnd()) {
299 const QQuickItem *bottomMostFosterChild = *bottomMostFosterChildIt;
300 if (d->shortcutLabel)
301 d->shortcutLabel->stackBefore(bottomMostFosterChild);
302 }
303}
304
305QT_END_NAMESPACE
306
307#include "moc_qquickmenuitemiconlabel_p.cpp"
Combined button and popup list for selecting options.