summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGabriel de Dietrich <gabriel.dedietrich@digia.com>2014-04-03 17:52:10 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-04-07 17:33:41 +0200
commitabbdb4d98d13e78cf47cca3c1d6a049770c57750 (patch)
treee0349cca28eab1f836de53861a44bdfa62caf5e7 /src
parentb8d0fac5a92984c432271530e2d3560ec0f1d442 (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')
-rw-r--r--src/widgets/widgets/qcombobox.cpp96
-rw-r--r--src/widgets/widgets/qcombobox_p.h4
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;