QQuickMenu: add a little delay for opening sub-menus on hover

This improves the usability on desktop. When a menu has multiple sub-
menu items, they don't all trigger a sub-menu (and close immediately)
while moving mouse over the items.

Change-Id: Ie4c9e409da8d6877e35506bffb94ed57f5985dcd
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
J-P Nurmi 2017-06-08 07:12:53 +02:00
parent 0a62e444e8
commit 0673dd4ff1
4 changed files with 44 additions and 3 deletions

View File

@ -59,6 +59,9 @@
QT_BEGIN_NAMESPACE
// copied from qfusionstyle.cpp
static const int SUBMENU_DELAY = 225;
/*!
\qmltype Menu
\inherits Popup
@ -183,6 +186,7 @@ static bool shouldCascade()
QQuickMenuPrivate::QQuickMenuPrivate()
: cascade(shouldCascade()),
hoverTimer(0),
overlap(0),
contentItem(nullptr),
contentModel(nullptr),
@ -371,7 +375,7 @@ void QQuickMenuPrivate::onItemHovered()
if (currentItem) {
QQuickMenu *subMenu = currentItem->menu();
if (subMenu && subMenu->cascade())
openSubMenu(currentItem, false);
startHoverTimer();
}
}
}
@ -468,6 +472,23 @@ void QQuickMenuPrivate::closeSubMenu(QQuickMenu *subMenu)
}
}
void QQuickMenuPrivate::startHoverTimer()
{
Q_Q(QQuickMenu);
stopHoverTimer();
hoverTimer = q->startTimer(SUBMENU_DELAY);
}
void QQuickMenuPrivate::stopHoverTimer()
{
Q_Q(QQuickMenu);
if (!hoverTimer)
return;
q->killTimer(hoverTimer);
hoverTimer = 0;
}
int QQuickMenuPrivate::currentIndex() const
{
QVariant index = contentItem->property("currentIndex");
@ -483,6 +504,7 @@ void QQuickMenuPrivate::setCurrentIndex(int index)
QQuickMenuItem *newCurrentItem = contentItem->property("currentItem").value<QQuickMenuItem *>();
if (currentItem != newCurrentItem) {
stopHoverTimer();
if (currentItem)
currentItem->setHighlighted(false);
if (newCurrentItem)
@ -1013,6 +1035,15 @@ void QQuickMenu::keyReleaseEvent(QKeyEvent *event)
item->forceActiveFocus();
}
void QQuickMenu::timerEvent(QTimerEvent *event)
{
Q_D(QQuickMenu);
if (event->timerId() == d->hoverTimer) {
d->openSubMenu(d->currentItem, false);
d->stopHoverTimer();
}
}
QFont QQuickMenu::defaultFont() const
{
return QQuickControlPrivate::themeFont(QPlatformTheme::MenuFont);

View File

@ -111,6 +111,8 @@ Q_SIGNALS:
Q_REVISION(3) void delegateChanged();
protected:
void timerEvent(QTimerEvent *event) override;
QFont defaultFont() const override;
QPalette defaultPalette() const override;

View File

@ -102,6 +102,9 @@ public:
void openSubMenu(QQuickMenuItem *item, bool activate);
void closeSubMenu(QQuickMenu *subMenu);
void startHoverTimer();
void stopHoverTimer();
int currentIndex() const;
void setCurrentIndex(int index);
@ -111,6 +114,7 @@ public:
static void contentData_clear(QQmlListProperty<QObject> *prop);
bool cascade;
int hoverTimer;
qreal overlap;
QPointer<QQuickMenu> parentMenu;
QPointer<QQuickMenuItem> currentItem;

View File

@ -636,7 +636,9 @@ void tst_menu::subMenuMouse()
QCOMPARE(mainMenu->isVisible(), cascade);
QVERIFY(subMenu1->isVisible());
QVERIFY(!subMenu2->isVisible());
QCOMPARE(subSubMenu1->isVisible(), cascade);
QVERIFY(!subSubMenu1->isVisible());
if (cascade)
QTRY_VERIFY(subSubMenu1->isVisible());
// close the sub-sub-menu with mouse hover over another parent menu item
QQuickMenuItem *subMenuItem1 = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(0));
@ -653,7 +655,9 @@ void tst_menu::subMenuMouse()
QCOMPARE(mainMenu->isVisible(), cascade);
QVERIFY(subMenu1->isVisible());
QVERIFY(!subMenu2->isVisible());
QCOMPARE(subSubMenu1->isVisible(), cascade);
QVERIFY(!subSubMenu1->isVisible());
if (cascade)
QTRY_VERIFY(subSubMenu1->isVisible());
// close sub-menu and sub-sub-menu with mouse hover in the main menu
QQuickMenuItem *mainMenuItem1 = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(0));