diff options
author | Gabriel de Dietrich <gabriel.dedietrich@digia.com> | 2014-04-03 17:52:10 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-04-07 17:33:41 +0200 |
commit | abbdb4d98d13e78cf47cca3c1d6a049770c57750 (patch) | |
tree | e0349cca28eab1f836de53861a44bdfa62caf5e7 /src/widgets | |
parent | b8d0fac5a92984c432271530e2d3560ec0f1d442 (diff) |
QComboBox: Use native popups on Mac
This remains an opt-in solution bound to the usage
of SH_ComboBox_UseNativePopup in a proxy style. The
midterm goal is to make this option on by default,
possibly in 5.4. This solution is and will remain a hint
in the sense that some exotic use cases of QComboBox
(e.g., when setting its view) are inherently incompatible
with the native popup idea.
Task-number: QTBUG-32731
Change-Id: I2a3d780795c22f9989e44325fcaf314538b1de49
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
Diffstat (limited to 'src/widgets')
-rw-r--r-- | src/widgets/widgets/qcombobox.cpp | 96 | ||||
-rw-r--r-- | src/widgets/widgets/qcombobox_p.h | 4 |
2 files changed, 94 insertions, 6 deletions
diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index e0f5ac1050..1ba08bd25f 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -44,6 +44,7 @@ #ifndef QT_NO_COMBOBOX #include <qstylepainter.h> #include <qpa/qplatformtheme.h> +#include <qpa/qplatformmenu.h> #include <qlineedit.h> #include <qapplication.h> #include <qdesktopwidget.h> @@ -205,7 +206,7 @@ void QComboBoxPrivate::updateArrow(QStyle::StateFlag state) arrowState = state; QStyleOptionComboBox opt; q->initStyleOption(&opt); - q->update(q->style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, q)); + q->update(q->rect()); } void QComboBoxPrivate::_q_modelReset() @@ -2375,6 +2376,79 @@ QSize QComboBox::sizeHint() const return d->recomputeSizeHint(d->sizeHint); } +#ifdef Q_OS_OSX +/*! + * \internal + * + * Tries to show a native popup. Returns true if it could, false otherwise. + * + */ +bool QComboBoxPrivate::showNativePopup() +{ + Q_Q(QComboBox); + + QPlatformTheme *theme = QGuiApplicationPrivate::instance()->platformTheme(); + if (QPlatformMenu *menu = theme->createPlatformMenu()) { + int itemsCount = q->count(); + + struct IndexSetter { + int index; + QComboBox *cb; + + void operator()(void) { cb->setCurrentIndex(index); } + }; + + QList<QPlatformMenuItem *> items; + items.reserve(itemsCount); + QPlatformMenuItem *currentItem = 0; + int currentIndex = q->currentIndex(); + + for (int i = 0; i < itemsCount; ++i) { + QPlatformMenuItem *item = theme->createPlatformMenuItem(); + QModelIndex rowIndex = model->index(i, modelColumn, root); + QVariant textVariant = model->data(rowIndex, Qt::EditRole); + item->setText(textVariant.toString()); + QVariant iconVariant = model->data(rowIndex, Qt::DecorationRole); + if (iconVariant.canConvert<QIcon>()) + item->setIcon(iconVariant.value<QIcon>()); + item->setCheckable(true); + item->setChecked(i == currentIndex); + if (!currentItem || i == currentIndex) + currentItem = item; + + IndexSetter setter = { i, q }; + QObject::connect(item, &QPlatformMenuItem::activated, setter); + + menu->insertMenuItem(item, 0); + menu->syncMenuItem(item); + } + + QWindow *tlw = q->window()->windowHandle(); + menu->setFont(q->font()); + menu->setMinimumWidth(q->rect().width()); + QPoint offset = QPoint(0, 7); + if (q->testAttribute(Qt::WA_MacSmallSize)) + offset = QPoint(-1, 7); + else if (q->testAttribute(Qt::WA_MacMiniSize)) + offset = QPoint(-2, 6); + menu->showPopup(tlw, tlw->mapFromGlobal(q->mapToGlobal(offset)), currentItem); + menu->deleteLater(); + Q_FOREACH (QPlatformMenuItem *item, items) + item->deleteLater(); + + // The Cocoa popup will swallow any mouse release event. + // We need to fake one here to un-press the button. + QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), Qt::LeftButton, + Qt::MouseButtons(Qt::LeftButton), Qt::KeyboardModifiers()); + qApp->sendEvent(q, &mouseReleased); + + return true; + } + + return false; +} +#endif // Q_OS_OSX + /*! Displays the list of items in the combobox. If the list is empty then the no items will be shown. @@ -2390,6 +2464,21 @@ void QComboBox::showPopup() if (count() <= 0) return; + QStyle * const style = this->style(); + QStyleOptionComboBox opt; + initStyleOption(&opt); + const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this); + +#ifdef Q_OS_OSX + if (usePopup + && (!d->container + || (view()->metaObject()->className() == QByteArray("QComboBoxListView") + && view()->itemDelegate()->metaObject()->className() == QByteArray("QComboMenuDelegate"))) + && style->styleHint(QStyle::SH_ComboBox_UseNativePopup, &opt, this) + && d->showNativePopup()) + return; +#endif // Q_OS_OSX + #ifdef QT_KEYPAD_NAVIGATION #ifndef QT_NO_COMPLETER if (QApplication::keypadNavigationEnabled() && d->completer) { @@ -2401,14 +2490,10 @@ void QComboBox::showPopup() #endif #endif - QStyle * const style = this->style(); - // set current item and select it view()->selectionModel()->setCurrentIndex(d->currentIndex, QItemSelectionModel::ClearAndSelect); QComboBoxPrivateContainer* container = d->viewContainer(); - QStyleOptionComboBox opt; - initStyleOption(&opt); QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxListBoxPopup, this)); QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(this)); @@ -2419,7 +2504,6 @@ void QComboBox::showPopup() int aboveHeight = above.y() - screen.y(); bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen); - const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this); { int listHeight = 0; int count = 0; diff --git a/src/widgets/widgets/qcombobox_p.h b/src/widgets/widgets/qcombobox_p.h index 1ad2aa455a..dceffe8d35 100644 --- a/src/widgets/widgets/qcombobox_p.h +++ b/src/widgets/widgets/qcombobox_p.h @@ -377,6 +377,10 @@ public: void modelChanged(); void updateViewContainerPaletteAndOpacity(); +#ifdef Q_OS_OSX + bool showNativePopup(); +#endif + QAbstractItemModel *model; QLineEdit *lineEdit; QComboBoxPrivateContainer *container; |