diff options
| author | Richard Moe Gustavsen <[email protected]> | 2024-05-22 22:15:53 +0200 |
|---|---|---|
| committer | Richard Moe Gustavsen <[email protected]> | 2024-05-29 18:48:25 +0200 |
| commit | a5b306650bdaf4d5dc05b2b014e86bfb85bab4e1 (patch) | |
| tree | 2c534e03fd4fd96be4f11933901339920b03014a | |
| parent | 2393c588bc9a5368c013952a4fb195c5b4017543 (diff) | |
QQuickMenuItem: add implicitTextPadding and textPadding
Add the properties "implicitTextPadding" and "textPadding"
to MenuItem. They can be used by the style to ensure that
all MenuItems inside the same Menu end up left-aligned
WRT text.
Each MenuItem should set implicitTextPadding to be the
needed space from the left edge of the contentItem to
the text label. QQuickMenu will then iterate over all
the MenuItems inside the same Menu, and set textPadding
to be the maximum implicitTextPadding found.
All MenuItems should then use textPadding (which will end
up the same for all MenuItems) to position the text.
This API is meant to solve the problem that MenuItems
inside a single Menu can have different contents. Some
can be checkable, some can have an icon, some are just
plain text. And for several of our styles (e.g macOS and
Windows), we want the text to be left-aligned regardless
of that. Without this API, The checkmark inside a
checkable MenuItem would be left-aligned with the text
inside a plain MenuItem etc.
[ChangeLog][Controls][MenuItem] A MenuItem now has two
new properties (implicitTextPadding and textPadding)
that can be used for aligning the text across all
MenuItems inside a Menu.
Change-Id: I1f2248b31c63d6b9780d8fc77229a8b902362f70
Reviewed-by: Oliver Eftevaag <[email protected]>
| -rw-r--r-- | src/quickcontrols/macos/MenuItem.qml | 7 | ||||
| -rw-r--r-- | src/quickcontrols/windows/MenuItem.qml | 7 | ||||
| -rw-r--r-- | src/quicktemplates/qquickmenu.cpp | 31 | ||||
| -rw-r--r-- | src/quicktemplates/qquickmenu_p_p.h | 2 | ||||
| -rw-r--r-- | src/quicktemplates/qquickmenuitem.cpp | 60 | ||||
| -rw-r--r-- | src/quicktemplates/qquickmenuitem_p.h | 8 | ||||
| -rw-r--r-- | src/quicktemplates/qquickmenuitem_p_p.h | 1 | ||||
| -rw-r--r-- | tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp | 62 |
8 files changed, 171 insertions, 7 deletions
diff --git a/src/quickcontrols/macos/MenuItem.qml b/src/quickcontrols/macos/MenuItem.qml index a0f3bdce5a..109c61e6a8 100644 --- a/src/quickcontrols/macos/MenuItem.qml +++ b/src/quickcontrols/macos/MenuItem.qml @@ -24,11 +24,12 @@ T.MenuItem { icon.width: 16 icon.height: 16 + implicitTextPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0 + contentItem: IconLabel { readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0 - readonly property real indicatorPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0 - leftPadding: !control.mirrored ? indicatorPadding : arrowPadding - rightPadding: control.mirrored ? indicatorPadding : arrowPadding + leftPadding: !control.mirrored ? control.textPadding : arrowPadding + rightPadding: control.mirrored ? control.textPadding : arrowPadding spacing: control.spacing mirrored: control.mirrored diff --git a/src/quickcontrols/windows/MenuItem.qml b/src/quickcontrols/windows/MenuItem.qml index b07bdeb70f..0d3c4f89f6 100644 --- a/src/quickcontrols/windows/MenuItem.qml +++ b/src/quickcontrols/windows/MenuItem.qml @@ -24,11 +24,12 @@ T.MenuItem { icon.width: 16 icon.height: 16 + implicitTextPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0 + contentItem: IconLabel { readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0 - readonly property real indicatorPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0 - leftPadding: !control.mirrored ? indicatorPadding : arrowPadding - rightPadding: control.mirrored ? indicatorPadding : arrowPadding + leftPadding: !control.mirrored ? control.textPadding : arrowPadding + rightPadding: control.mirrored ? control.textPadding : arrowPadding spacing: control.spacing mirrored: control.mirrored diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp index f216c4985f..7f06c9682f 100644 --- a/src/quicktemplates/qquickmenu.cpp +++ b/src/quicktemplates/qquickmenu.cpp @@ -572,6 +572,8 @@ void QQuickMenuPrivate::insertItem(int index, QQuickItem *item) if (QQuickMenu *subMenu = menuItem->subMenu()) QQuickMenuPrivate::get(subMenu)->setParentMenu(q); QObjectPrivate::connect(menuItem, &QQuickMenuItem::triggered, this, &QQuickMenuPrivate::onItemTriggered); + QObjectPrivate::connect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged, this, &QQuickMenuPrivate::updateTextPadding); + QObjectPrivate::connect(menuItem, &QQuickMenuItem::visibleChanged, this, &QQuickMenuPrivate::updateTextPadding); QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged); QObjectPrivate::connect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered); } @@ -581,6 +583,8 @@ void QQuickMenuPrivate::insertItem(int index, QQuickItem *item) if (lcMenu().isDebugEnabled()) printContentModelItems(); + + updateTextPadding(); } void QQuickMenuPrivate::maybeCreateAndInsertNativeItem(int index, QQuickItem *item) @@ -661,6 +665,8 @@ void QQuickMenuPrivate::removeItem(int index, QQuickItem *item, DestructionPolic if (QQuickMenu *subMenu = menuItem->subMenu()) QQuickMenuPrivate::get(subMenu)->setParentMenu(nullptr); QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::triggered, this, &QQuickMenuPrivate::onItemTriggered); + QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged, this, &QQuickMenuPrivate::updateTextPadding); + QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::visibleChanged, this, &QQuickMenuPrivate::updateTextPadding); QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged); QObjectPrivate::disconnect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered); } @@ -990,6 +996,30 @@ void QQuickMenuPrivate::onItemActiveFocusChanged() setCurrentIndex(indexOfItem, control ? control->focusReason() : Qt::OtherFocusReason); } +void QQuickMenuPrivate::updateTextPadding() +{ + Q_Q(QQuickMenu); + if (!complete) + return; + + qreal padding = 0; + for (int i = 0; i < q->count(); ++i) { + if (const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i))) + if (menuItem->isVisible()) + padding = qMax(padding, menuItem->implicitTextPadding()); + } + + if (padding == textPadding) + return; + + textPadding = padding; + + for (int i = 0; i < q->count(); ++i) { + if (const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i))) + emit menuItem->textPaddingChanged(); + } +} + QQuickMenu *QQuickMenuPrivate::currentSubMenu() const { if (!currentItem) @@ -1983,6 +2013,7 @@ void QQuickMenu::componentComplete() Q_D(QQuickMenu); QQuickPopup::componentComplete(); d->resizeItems(); + d->updateTextPadding(); d->syncWithUseNativeMenu(); } diff --git a/src/quicktemplates/qquickmenu_p_p.h b/src/quicktemplates/qquickmenu_p_p.h index d2b0084d6b..368623e873 100644 --- a/src/quicktemplates/qquickmenu_p_p.h +++ b/src/quicktemplates/qquickmenu_p_p.h @@ -97,6 +97,7 @@ public: void onItemHovered(); void onItemTriggered(); void onItemActiveFocusChanged(); + void updateTextPadding(); QQuickMenu *currentSubMenu() const; void setParentMenu(QQuickMenu *parent); @@ -125,6 +126,7 @@ public: int hoverTimer = 0; int currentIndex = -1; qreal overlap = 0; + qreal textPadding = 0; QPointer<QQuickMenu> parentMenu; QPointer<QQuickMenuItem> currentItem; QQuickItem *contentItem = nullptr; // TODO: cleanup diff --git a/src/quicktemplates/qquickmenuitem.cpp b/src/quicktemplates/qquickmenuitem.cpp index 292c098aaa..32a0719150 100644 --- a/src/quicktemplates/qquickmenuitem.cpp +++ b/src/quicktemplates/qquickmenuitem.cpp @@ -3,7 +3,7 @@ #include "qquickmenuitem_p.h" #include "qquickmenuitem_p_p.h" -#include "qquickmenu_p.h" +#include "qquickmenu_p_p.h" #include "qquickdeferredexecute_p_p.h" #include <QtGui/qpa/qplatformtheme.h> @@ -56,6 +56,44 @@ QT_BEGIN_NAMESPACE \sa {Customizing Menu}, Menu, {Menu Controls} */ +/*! + \qmlproperty bool QtQuick.Controls::MenuItem::textPadding + \readonly + \since 6.8 + + This property holds the maximum \l implicitTextPadding found + among all the menu items inside the same \l menu. + + This property can be used by the style to ensure that all MenuItems + inside the same Menu end up aligned with respect to the \l text. + + A \l Menu can consist of meny different MenuItems, some can be checkable, + some can have an icon, and some will just contain text. And very often, + a style wants to make sure that the text inside all of them ends up + left-aligned (or right-aligned for \l mirrored items). + By letting each MenuItem assign its own minimum text padding to + \l implicitTextPadding (taking icons and checkmarks into account), but + using \l textPadding to actually position the \l text, all MenuItems should + end up being aligned + + In order for this to work, all MenuItems should set \l implicitTextPadding + to be the minimum space needed from the left edge of the \l contentItem to + the text. + + \sa implicitTextPadding +*/ + +/*! + \qmlproperty bool QtQuick.Controls::MenuItem::implicitTextPadding + \since 6.8 + + This property holds the minimum space needed from the left edge of the + \l contentItem to the text. It's used to calculate a common \l textPadding + among all the MenuItems inside a \l Menu. + + \sa textPadding +*/ + void QQuickMenuItemPrivate::setMenu(QQuickMenu *newMenu) { Q_Q(QQuickMenuItem); @@ -240,6 +278,26 @@ QFont QQuickMenuItem::defaultFont() const return QQuickTheme::font(QQuickTheme::Menu); } +qreal QQuickMenuItem::implicitTextPadding() const +{ + return d_func()->implicitTextPadding; +} + +void QQuickMenuItem::setImplicitTextPadding(qreal newImplicitTextPadding) +{ + Q_D(QQuickMenuItem); + if (qFuzzyCompare(d->implicitTextPadding, newImplicitTextPadding)) + return; + d->implicitTextPadding = newImplicitTextPadding; + emit implicitTextPaddingChanged(); +} + +qreal QQuickMenuItem::textPadding() const +{ + Q_D(const QQuickMenuItem); + return d->menu ? QQuickMenuPrivate::get(d->menu)->textPadding : 0; +} + #if QT_CONFIG(accessibility) QAccessible::Role QQuickMenuItem::accessibleRole() const { diff --git a/src/quicktemplates/qquickmenuitem_p.h b/src/quicktemplates/qquickmenuitem_p.h index fb121e6bc3..786b6f8ae6 100644 --- a/src/quicktemplates/qquickmenuitem_p.h +++ b/src/quicktemplates/qquickmenuitem_p.h @@ -33,6 +33,8 @@ class Q_QUICKTEMPLATES2_EXPORT QQuickMenuItem : public QQuickAbstractButton Q_PROPERTY(QQuickItem *arrow READ arrow WRITE setArrow NOTIFY arrowChanged FINAL REVISION(2, 3)) Q_PROPERTY(QQuickMenu *menu READ menu NOTIFY menuChanged FINAL REVISION(2, 3)) Q_PROPERTY(QQuickMenu *subMenu READ subMenu NOTIFY subMenuChanged FINAL REVISION(2, 3)) + Q_PROPERTY(qreal implicitTextPadding READ implicitTextPadding WRITE setImplicitTextPadding NOTIFY implicitTextPaddingChanged REVISION(6, 8)) + Q_PROPERTY(qreal textPadding READ textPadding NOTIFY textPaddingChanged REVISION(6, 8)) Q_CLASSINFO("DeferredPropertyNames", "arrow,background,contentItem,indicator") QML_NAMED_ELEMENT(MenuItem) QML_ADDED_IN_VERSION(2, 0) @@ -50,6 +52,10 @@ public: QQuickMenu *menu() const; QQuickMenu *subMenu() const; + qreal textPadding() const; + qreal implicitTextPadding() const; + void setImplicitTextPadding(qreal newImplicitTextPadding); + Q_SIGNALS: void triggered(); void highlightedChanged(); @@ -57,6 +63,8 @@ Q_SIGNALS: Q_REVISION(2, 3) void arrowChanged(); Q_REVISION(2, 3) void menuChanged(); Q_REVISION(2, 3) void subMenuChanged(); + Q_REVISION(6, 8) void implicitTextPaddingChanged(); + Q_REVISION(6, 8) void textPaddingChanged(); protected: void componentComplete() override; diff --git a/src/quicktemplates/qquickmenuitem_p_p.h b/src/quicktemplates/qquickmenuitem_p_p.h index 63bcfa33f6..3ef4981570 100644 --- a/src/quicktemplates/qquickmenuitem_p_p.h +++ b/src/quicktemplates/qquickmenuitem_p_p.h @@ -48,6 +48,7 @@ public: QQuickDeferredPointer<QQuickItem> arrow; QQuickMenu *menu = nullptr; QQuickMenu *subMenu = nullptr; + qreal implicitTextPadding; }; QT_END_NAMESPACE diff --git a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp index 6ec7ed3b5a..232b8e935e 100644 --- a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp +++ b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp @@ -102,6 +102,7 @@ private slots: void nativeMenuSeparator(); void dontUseNativeMenuWindowsChanges(); void nativeMixedItems(); + void textPadding(); private: bool nativeMenuSupported = false; @@ -2624,6 +2625,67 @@ void tst_QQuickMenu::nativeMixedItems() } } +void tst_QQuickMenu::textPadding() +{ + // Check that you can set implicitTextPadding on each MenuItem, and that + // textPadding will end up as the maximum implicitTextPadding among all the + // MenuItems in the same Menu. + QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows); + + QQuickControlsApplicationHelper helper(this, QLatin1String("nativeMixedItems.qml")); + QVERIFY2(helper.ready, helper.failureMessage()); + QQuickApplicationWindow *window = helper.appWindow; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>(); + QVERIFY(contextMenu); + + contextMenu->setVisible(true); + + // Go through all MenuItems, and give them an implicitTextPadding of 0 + for (int i = 0; i < contextMenu->count(); ++i) { + auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i)); + QVERIFY(menuItem); + menuItem->setImplicitTextPadding(0); + QCOMPARE(menuItem->implicitTextPadding(), 0); + } + + // Check that all MenuItems now has a textPadding of 0 + for (int i = 0; i < contextMenu->count(); ++i) { + auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i)); + QCOMPARE(menuItem->textPadding(), 0); + } + + // Let the first MenuItem get a implicitTextPadding of 100. This will + // make all MenuItems get a textPadding of 100. + auto firstItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0)); + firstItem->setImplicitTextPadding(100); + QCOMPARE(firstItem->implicitTextPadding(), 100); + QCOMPARE(firstItem->textPadding(), 100); + for (int i = 1; i < contextMenu->count(); ++i) { + auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i)); + QCOMPARE(menuItem->implicitTextPadding(), 0); + QCOMPARE(menuItem->textPadding(), 100); + } + + // Hide the MenuItem with implicitTextPadding set to 100. This + // should make all the MenuItems get a textPadding of 0 again. + firstItem->setVisible(false); + QCOMPARE(firstItem->implicitTextPadding(), 100); + for (int i = 0; i < contextMenu->count(); ++i) { + auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i)); + QCOMPARE(menuItem->textPadding(), 0); + } + + // Show it again + firstItem->setVisible(true); + for (int i = 0; i < contextMenu->count(); ++i) { + auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i)); + QCOMPARE(menuItem->textPadding(), 100); + } +} + QTEST_QUICKCONTROLS_MAIN(tst_QQuickMenu) #include "tst_qquickmenu.moc" |
