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