QQuickMenu: let a child menu use the same popup type as the parent
We currently allow a child menu to have a popupType different from the parent menu. This will not work in practice, since all menus in the same hierarchy have to have the same type. Rather than placing the burden on the application developer to set the same popup type on all (sub)menus, we let QQuickMenu do this automatically based on the what the root menu ends up using. This is also the documented behavior. In order to implement this change, add a virtual QQuickPopup::resolvedPopupType(). The other sub-classes of QQuickPopup can then override this function and resolve the actual popup type at run-time. This also allows us to remove using Qt::Widget as a popupWindowType(), since e.g a Drawer will never resolve the popup type to be QQuickPopup::Window anyway. Pick-to: 6.8 Change-Id: I9cf95bb02fb0a912497c77d2df25a9cb76c153cd Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io>
This commit is contained in:
parent
2370cb0eb9
commit
7ceaa9693e
|
@ -596,9 +596,10 @@ bool QQuickDrawerPrivate::prepareExitTransition()
|
|||
return QQuickPopupPrivate::prepareExitTransition();
|
||||
}
|
||||
|
||||
Qt::WindowFlags QQuickDrawerPrivate::popupWindowType() const
|
||||
QQuickPopup::PopupType QQuickDrawerPrivate::resolvedPopupType() const
|
||||
{
|
||||
return Qt::Widget;
|
||||
// For now, a drawer will always be shown in-scene
|
||||
return QQuickPopup::Item;
|
||||
}
|
||||
|
||||
bool QQuickDrawerPrivate::setEdge(Qt::Edge e)
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
bool prepareEnterTransition() override;
|
||||
bool prepareExitTransition() override;
|
||||
|
||||
Qt::WindowFlags popupWindowType() const override;
|
||||
QQuickPopup::PopupType resolvedPopupType() const override;
|
||||
|
||||
bool setEdge(Qt::Edge edge);
|
||||
Qt::Edge effectiveEdge() const;
|
||||
|
|
|
@ -338,6 +338,21 @@ QQuickMenu *QQuickMenuPrivate::rootMenu() const
|
|||
return const_cast<QQuickMenu *>(rootMenu);
|
||||
}
|
||||
|
||||
QQuickPopup::PopupType QQuickMenuPrivate::resolvedPopupType() const
|
||||
{
|
||||
// The resolved popup type is decided by the root
|
||||
// menu (which can be this menu, unless it's a child menu).
|
||||
QQuickMenuPrivate *root_d = QQuickMenuPrivate::get(rootMenu());
|
||||
|
||||
// If the root menu is native, then so should we. We assume here that
|
||||
// the root menu is always shown and created first, before we try to
|
||||
// show and create a child menu.
|
||||
if (root_d->maybeNativeHandle())
|
||||
return QQuickPopup::PopupType::Native;
|
||||
|
||||
return root_d->QQuickPopupPrivate::resolvedPopupType();
|
||||
}
|
||||
|
||||
bool QQuickMenuPrivate::useNativeMenu() const
|
||||
{
|
||||
// If we're inside a MenuBar, it'll decide whether or not we
|
||||
|
@ -346,7 +361,7 @@ bool QQuickMenuPrivate::useNativeMenu() const
|
|||
QQuickMenu *root = rootMenu();
|
||||
if (auto menuBar = QQuickMenuPrivate::get(root)->menuBar.get())
|
||||
return QQuickMenuBarPrivate::get(menuBar)->useNativeMenu(q_func());
|
||||
return m_popupType == QQuickPopup::Native;
|
||||
return root->popupType() == QQuickPopup::Native;
|
||||
}
|
||||
|
||||
QPlatformMenu *QQuickMenuPrivate::nativeHandle()
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
static void contentData_clear(QQmlListProperty<QObject> *prop);
|
||||
|
||||
QPalette defaultPalette() const override;
|
||||
virtual QQuickPopup::PopupType resolvedPopupType() const override;
|
||||
|
||||
bool cascade = false;
|
||||
bool triedToCreateNativeMenu = false;
|
||||
|
|
|
@ -974,11 +974,28 @@ QPalette QQuickPopupPrivate::defaultPalette() const
|
|||
return QQuickTheme::palette(QQuickTheme::System);
|
||||
}
|
||||
|
||||
QQuickPopup::PopupType QQuickPopupPrivate::resolvedPopupType() const
|
||||
{
|
||||
// Whether or not the resolved popup type ends up the same as the preferred popup type
|
||||
// depends on platform capabilities, the popup subclass, and sometimes also the location
|
||||
// of the popup in the parent hierarchy (menus). This function can therefore be overridden
|
||||
// to return the actual popup type that should be used, based on the knowledge the popup
|
||||
// has just before it's about to be shown.
|
||||
|
||||
// PopupType::Native is not directly supported by QQuickPopup (only by subclasses).
|
||||
// So for that case, we fall back to use PopupType::Window, if supported.
|
||||
if (m_popupType == QQuickPopup::PopupType::Window
|
||||
|| m_popupType == QQuickPopup::PopupType::Native) {
|
||||
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::Capability::MultipleWindows))
|
||||
return QQuickPopup::PopupType::Window;
|
||||
}
|
||||
|
||||
return QQuickPopup::PopupType::Item;
|
||||
}
|
||||
|
||||
bool QQuickPopupPrivate::usePopupWindow() const
|
||||
{
|
||||
return QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::Capability::MultipleWindows)
|
||||
&& m_popupType == QQuickPopup::PopupType::Window
|
||||
&& popupWindowType() != Qt::Widget; // We use Qt::Widget here, to allow some popup derived types, like drawer, to opt out of using separate windows.
|
||||
return resolvedPopupType() == QQuickPopup::PopupType::Window;
|
||||
}
|
||||
|
||||
void QQuickPopupPrivate::adjustPopupItemParentAndWindow()
|
||||
|
|
|
@ -104,6 +104,9 @@ public:
|
|||
void destroyDimmer();
|
||||
void toggleOverlay();
|
||||
void updateContentPalettes(const QPalette& parentPalette);
|
||||
|
||||
virtual QQuickPopup::PopupType resolvedPopupType() const;
|
||||
|
||||
virtual void showDimmer();
|
||||
virtual void hideDimmer();
|
||||
virtual void resizeDimmer();
|
||||
|
|
|
@ -81,6 +81,7 @@ private slots:
|
|||
void subMenuPosition();
|
||||
void subMenuWithIcon();
|
||||
void addRemoveSubMenus();
|
||||
void subMenuPopupType();
|
||||
void scrollable_data();
|
||||
void scrollable();
|
||||
void disableWhenTriggered_data();
|
||||
|
@ -110,6 +111,7 @@ private slots:
|
|||
|
||||
private:
|
||||
bool nativeMenuSupported = false;
|
||||
bool popupWindowsSupported = false;
|
||||
};
|
||||
|
||||
// This allows us to use QQuickMenuItem's more descriptive operator<< output
|
||||
|
@ -126,6 +128,7 @@ tst_QQuickMenu::tst_QQuickMenu()
|
|||
{
|
||||
std::unique_ptr<QPlatformMenu> platformMenu(QGuiApplicationPrivate::platformTheme()->createPlatformMenu());
|
||||
nativeMenuSupported = platformMenu != nullptr;
|
||||
popupWindowsSupported = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::Capability::MultipleWindows);
|
||||
}
|
||||
|
||||
void tst_QQuickMenu::init()
|
||||
|
@ -1836,6 +1839,92 @@ void tst_QQuickMenu::addRemoveSubMenus()
|
|||
QVERIFY(subSubMenu1Item.isNull());
|
||||
}
|
||||
|
||||
void tst_QQuickMenu::subMenuPopupType()
|
||||
{
|
||||
// Check that all sub-menus will end up with an effective popup
|
||||
// type equal to the root menu.
|
||||
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenus.qml"));
|
||||
QVERIFY2(helper.ready, helper.failureMessage());
|
||||
QQuickWindow *window = helper.window;
|
||||
window->show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
|
||||
auto *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
||||
auto *subMenu1 = window->property("subMenu1").value<QQuickMenu *>();
|
||||
auto *subSubMenu1 = window->property("subSubMenu1").value<QQuickMenu *>();
|
||||
QVERIFY(mainMenu);
|
||||
QVERIFY(subMenu1);
|
||||
QVERIFY(subSubMenu1);
|
||||
auto *mainMenu_d = QQuickMenuPrivate::get(mainMenu);
|
||||
auto *subMenu1_d = QQuickMenuPrivate::get(subMenu1);
|
||||
auto *subSubMenu1_d = QQuickMenuPrivate::get(subSubMenu1);
|
||||
|
||||
mainMenu->setPopupType(QQuickPopup::Item);
|
||||
QCOMPARE(mainMenu->popupType(), QQuickPopup::Item);
|
||||
mainMenu->open();
|
||||
QTRY_VERIFY(mainMenu->isOpened());
|
||||
QCOMPARE(mainMenu_d->resolvedPopupType(), QQuickPopup::Item);
|
||||
QCOMPARE(subMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
||||
QCOMPARE(subSubMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
||||
mainMenu->close();
|
||||
QTRY_VERIFY(!mainMenu->isVisible());
|
||||
|
||||
// Even if we set QQuickPopup::Window as preferred popup type for
|
||||
// subMenu1, the the effective type will still be the same as the
|
||||
// parent menu: QQuickPopup::Item
|
||||
subMenu1->setPopupType(QQuickPopup::Window);
|
||||
QCOMPARE(subMenu1->popupType(), QQuickPopup::Window);
|
||||
QCOMPARE(mainMenu->popupType(), QQuickPopup::Item);
|
||||
mainMenu->open();
|
||||
QTRY_VERIFY(mainMenu->isOpened());
|
||||
QCOMPARE(mainMenu_d->resolvedPopupType(), QQuickPopup::Item);
|
||||
QCOMPARE(subMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
||||
QCOMPARE(subSubMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
||||
mainMenu->close();
|
||||
QTRY_VERIFY(!mainMenu->isVisible());
|
||||
|
||||
// Setting QQuickPopup::Window on the root menu will force all sub-menus
|
||||
// to use QQuickPopup::Window as well, if it's supported on the platform
|
||||
// where the test runs. Otherwise it will fall back to QQuickPopup::Item.
|
||||
const QQuickPopup::PopupType windowIfSupportedElseItem =
|
||||
popupWindowsSupported ? QQuickPopup::Window : QQuickPopup::Item;
|
||||
mainMenu->setPopupType(QQuickPopup::Window);
|
||||
QCOMPARE(mainMenu->popupType(), QQuickPopup::Window);
|
||||
QCOMPARE(subMenu1->popupType(), QQuickPopup::Window);
|
||||
mainMenu->open();
|
||||
QTRY_VERIFY(mainMenu->isOpened());
|
||||
QCOMPARE(mainMenu_d->resolvedPopupType(), windowIfSupportedElseItem);
|
||||
QCOMPARE(subMenu1_d->resolvedPopupType(), windowIfSupportedElseItem);
|
||||
QCOMPARE(subSubMenu1_d->resolvedPopupType(), windowIfSupportedElseItem);
|
||||
mainMenu->close();
|
||||
QTRY_VERIFY(!mainMenu->isVisible());
|
||||
|
||||
// Setting QQuickPopup::Native on the root menu will force all sub-menus
|
||||
// to use QQuickPopup::Native as well, if it's supported on the platform
|
||||
// where the test runs. Otherwise it will fall back to either
|
||||
// QQuickPopup::Window or QQuickPopup::Item.
|
||||
mainMenu->setPopupType(QQuickPopup::Native);
|
||||
QCOMPARE(mainMenu->popupType(), QQuickPopup::Native);
|
||||
QCOMPARE(subMenu1->popupType(), QQuickPopup::Window);
|
||||
if (nativeMenuSupported) {
|
||||
// Note that we cannot actually show a native popup while testing, since
|
||||
// that will be a blocking call. Instead we just verify that we
|
||||
// intend to use a native menu.
|
||||
QVERIFY(mainMenu_d->useNativeMenu());
|
||||
QVERIFY(subMenu1_d->useNativeMenu());
|
||||
QVERIFY(subSubMenu1_d->useNativeMenu());
|
||||
} else {
|
||||
// When Native is not supported, we fall back to either Window or Item
|
||||
mainMenu->open();
|
||||
QTRY_VERIFY(mainMenu->isOpened());
|
||||
QCOMPARE(mainMenu_d->resolvedPopupType(), windowIfSupportedElseItem);
|
||||
QCOMPARE(subMenu1_d->resolvedPopupType(), windowIfSupportedElseItem);
|
||||
QCOMPARE(subSubMenu1_d->resolvedPopupType(), windowIfSupportedElseItem);
|
||||
mainMenu->close();
|
||||
QTRY_VERIFY(!mainMenu->isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QQuickMenu::scrollable_data()
|
||||
{
|
||||
QTest::addColumn<QString>("qmlFilePath");
|
||||
|
|
Loading…
Reference in New Issue