QComboBox: refactor item flashing code in hidePopup

Instead of using a local event loop, control the flashing through timers
and lambdas. Move the actual hiding of the popup then into a helper that
gets called when the flashing is done.

Note: this changes behavior -the popup will not be hidden when
hidePopup returns. And since events continue get processed during the
flashing effect, we might still get reentrancy (so the code added in
accc833e556ba54038d1cc7e261a936492145240 has to stay anyway).

Change-Id: I2ce20520dea16bd3be78544e9fdd059a2969a795
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Volker Hilsheimer 2022-10-28 14:13:01 +02:00
parent a9fa999f79
commit f4b028114a
2 changed files with 38 additions and 24 deletions

View File

@ -2813,41 +2813,54 @@ void QComboBox::hidePopup()
auto resetHidingPopup = qScopeGuard([d]{ auto resetHidingPopup = qScopeGuard([d]{
d->hidingPopup = false; d->hidingPopup = false;
}); });
if (d->container && d->container->isVisible()) {
if (!d->container || !d->container->isVisible())
return;
#if QT_CONFIG(effects) #if QT_CONFIG(effects)
// Flash selected/triggered item (if any).
if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) {
QItemSelectionModel *selectionModel = d->container->itemView()
? d->container->itemView()->selectionModel() : nullptr;
if (selectionModel && selectionModel->hasSelection()) {
const QItemSelection selection = selectionModel->selection();
QTimer::singleShot(0, d->container, [d, selection, selectionModel]{
QSignalBlocker modelBlocker(d->model); QSignalBlocker modelBlocker(d->model);
QSignalBlocker viewBlocker(d->container->itemView()); QSignalBlocker viewBlocker(d->container->itemView());
QSignalBlocker containerBlocker(d->container); QSignalBlocker containerBlocker(d->container);
// Flash selected/triggered item (if any).
if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) {
QItemSelectionModel *selectionModel = view() ? view()->selectionModel() : nullptr;
if (selectionModel && selectionModel->hasSelection()) {
QEventLoop eventLoop;
const QItemSelection selection = selectionModel->selection();
// Deselect item and wait 60 ms. // Deselect item and wait 60 ms.
selectionModel->select(selection, QItemSelectionModel::Toggle); selectionModel->select(selection, QItemSelectionModel::Toggle);
QTimer::singleShot(60, &eventLoop, SLOT(quit())); QTimer::singleShot(60, d->container, [d, selection, selectionModel]{
eventLoop.exec(); QSignalBlocker modelBlocker(d->model);
QSignalBlocker viewBlocker(d->container->itemView());
// Select item and wait 20 ms. QSignalBlocker containerBlocker(d->container);
selectionModel->select(selection, QItemSelectionModel::Toggle); selectionModel->select(selection, QItemSelectionModel::Toggle);
QTimer::singleShot(20, &eventLoop, SLOT(quit())); QTimer::singleShot(20, d->container, [d] {
eventLoop.exec(); d->doHidePopup();
});
});
});
} }
} } else
containerBlocker.unblock();
viewBlocker.unblock();
modelBlocker.unblock();
#endif // QT_CONFIG(effects) #endif // QT_CONFIG(effects)
d->container->hide(); {
d->doHidePopup();
} }
}
void QComboBoxPrivate::doHidePopup()
{
if (container && container->isVisible())
container->hide();
#ifdef QT_KEYPAD_NAVIGATION #ifdef QT_KEYPAD_NAVIGATION
if (QApplicationPrivate::keypadNavigationEnabled() && isEditable() && hasFocus()) if (QApplicationPrivate::keypadNavigationEnabled() && isEditable() && hasFocus())
setEditFocus(true); setEditFocus(true);
#endif #endif
d->_q_resetButton();
_q_resetButton();
} }
/*! /*!

View File

@ -352,6 +352,7 @@ public:
void updateViewContainerPaletteAndOpacity(); void updateViewContainerPaletteAndOpacity();
void updateFocusPolicy(); void updateFocusPolicy();
void showPopupFromMouseEvent(QMouseEvent *e); void showPopupFromMouseEvent(QMouseEvent *e);
void doHidePopup();
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
void cleanupNativePopup(); void cleanupNativePopup();