diff options
author | Liang Qi <liang.qi@qt.io> | 2018-03-28 08:12:21 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2018-03-28 08:12:21 +0200 |
commit | 794781e7cff9f7d44af64292796428478ed18663 (patch) | |
tree | dae1f8478885d95dac5766ce46d5dd4784ca06f4 /src/plugins | |
parent | f69d32b535d456a2441754ef5733c7fb65411d6c (diff) | |
parent | e83f1900f657a41036bd16e917527fcb7a52fd2b (diff) |
Merge remote-tracking branch 'origin/5.11' into dev
Conflicts:
src/plugins/platforms/cocoa/qnsview.mm
src/plugins/platforms/cocoa/qnsview_mouse.mm
src/testlib/testlib.pro
Change-Id: Ia0ce4243418fe6a485b0f290c67bd433b3b04ff2
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.mm | 2 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_mouse.mm | 8 | ||||
-rw-r--r-- | src/plugins/platforms/qnx/qqnxwindow.cpp | 41 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp | 64 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h | 2 | ||||
-rw-r--r-- | src/plugins/sqldrivers/psql/qsql_psql.cpp | 6 | ||||
-rw-r--r-- | src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp | 34 | ||||
-rw-r--r-- | src/plugins/styles/mac/qmacstyle_mac.mm | 824 | ||||
-rw-r--r-- | src/plugins/styles/mac/qmacstyle_mac_p_p.h | 25 | ||||
-rw-r--r-- | src/plugins/styles/windowsvista/qwindowsxpstyle.cpp | 3 |
10 files changed, 557 insertions, 452 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index f28d6477f1..35384c626e 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -493,7 +493,7 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) { const Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); const bool frameless = (flags & Qt::FramelessWindowHint) || windowIsPopupType(type); - const bool resizeable = type != Qt::Dialog; // Dialogs: remove zoom button by disabling resize + const bool resizeable = !(flags & Qt::CustomizeWindowHint); // Remove zoom button by disabling resize // Select base window type. Note that the value of NSBorderlessWindowMask is 0. NSUInteger styleMask = (frameless || !resizeable) ? NSBorderlessWindowMask : NSResizableWindowMask; diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm index 77af50119d..a101b5ab6f 100644 --- a/src/plugins/platforms/cocoa/qnsview_mouse.mm +++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm @@ -155,8 +155,12 @@ NSPoint screenPoint; if (theEvent) { NSPoint windowPoint = [theEvent locationInWindow]; - NSRect screenRect = [[theEvent window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)]; - screenPoint = screenRect.origin; + if (qIsNaN(windowPoint.x) || qIsNaN(windowPoint.y)) { + screenPoint = [NSEvent mouseLocation]; + } else { + NSRect screenRect = [[theEvent window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)]; + screenPoint = screenRect.origin; + } } else { screenPoint = [NSEvent mouseLocation]; } diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 38b61fd782..4a547aa158 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -63,6 +63,18 @@ QT_BEGIN_NAMESPACE +#define DECLARE_DEBUG_VAR(variable) \ + static bool debug_ ## variable() \ + { static bool value = qgetenv("QNX_SCREEN_DEBUG").contains(QT_STRINGIFY(variable)); return value; } +DECLARE_DEBUG_VAR(fps) +DECLARE_DEBUG_VAR(posts) +DECLARE_DEBUG_VAR(blits) +DECLARE_DEBUG_VAR(updates) +DECLARE_DEBUG_VAR(cpu_time) +DECLARE_DEBUG_VAR(gpu_time) +DECLARE_DEBUG_VAR(statistics) +#undef DECLARE_DEBUG_VAR + /*! \class QQnxWindow \brief The QQnxWindow is the base class of the various classes used as instances of @@ -211,6 +223,35 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW // it'll cause us not to join a group (the app will presumably join at some future time). if (windowGroup.isValid() && windowGroup.canConvert<QByteArray>()) joinWindowGroup(windowGroup.toByteArray()); + + int debug = 0; + if (Q_UNLIKELY(debug_fps())) { + debug |= SCREEN_DEBUG_GRAPH_FPS; + } + if (Q_UNLIKELY(debug_posts())) { + debug |= SCREEN_DEBUG_GRAPH_POSTS; + } + if (Q_UNLIKELY(debug_blits())) { + debug |= SCREEN_DEBUG_GRAPH_BLITS; + } + if (Q_UNLIKELY(debug_updates())) { + debug |= SCREEN_DEBUG_GRAPH_UPDATES; + } + if (Q_UNLIKELY(debug_cpu_time())) { + debug |= SCREEN_DEBUG_GRAPH_CPU_TIME; + } + if (Q_UNLIKELY(debug_gpu_time())) { + debug |= SCREEN_DEBUG_GRAPH_GPU_TIME; + } + if (Q_UNLIKELY(debug_statistics())) { + debug = SCREEN_DEBUG_STATISTICS; + } + + if (debug > 0) { + Q_SCREEN_CHECKERROR(screen_set_window_property_iv(nativeHandle(), SCREEN_PROPERTY_DEBUG, &debug), + "Could not set SCREEN_PROPERTY_DEBUG"); + qWindowDebug() << "window SCREEN_PROPERTY_DEBUG= " << debug; + } } QQnxWindow::~QQnxWindow() diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp index 21024385b0..86ca7afa12 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -86,6 +86,22 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXC #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 #endif +#ifndef GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB +#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#endif + +#ifndef GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB +#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#endif + +#ifndef GLX_LOSE_CONTEXT_ON_RESET_ARB +#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#endif + +#ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV +#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7 +#endif + static Window createDummyWindow(Display *dpy, XVisualInfo *visualInfo, int screenNumber, Window rootWin) { Colormap cmap = XCreateColormap(dpy, rootWin, visualInfo->visual, AllocNone); @@ -179,6 +195,8 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat , m_isPBufferCurrent(false) , m_swapInterval(-1) , m_ownsContext(nativeHandle.isNull()) + , m_getGraphicsResetStatus(0) + , m_lost(false) { if (nativeHandle.isNull()) init(screen, share); @@ -214,6 +232,8 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); const bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile"); + const bool supportsRobustness = glxExt.contains("GLX_ARB_create_context_robustness"); + const bool supportsVideoMemoryPurge = glxExt.contains("GLX_NV_robustness_video_memory_purge"); // Use glXCreateContextAttribsARB if available // Also, GL ES context creation requires GLX_EXT_create_context_es2_profile @@ -266,6 +286,9 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) int flags = 0; + if (supportsRobustness) + flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; + if (m_format.testOption(QSurfaceFormat::DebugContext)) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; @@ -279,14 +302,33 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_ES2_PROFILE_BIT_EXT; } - contextAttributes << None; + if (supportsRobustness && supportsVideoMemoryPurge) { + QVector<int> contextAttributesWithNvidiaReset = contextAttributes; + + contextAttributesWithNvidiaReset << GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB << GLX_LOSE_CONTEXT_ON_RESET_ARB; + contextAttributesWithNvidiaReset << GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV << GL_TRUE; - m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributes.data()); - if (!m_context && m_shareContext) { - // re-try without a shared glx context - m_context = glXCreateContextAttribsARB(m_display, config, 0, true, contextAttributes.data()); - if (m_context) - m_shareContext = 0; + contextAttributesWithNvidiaReset << None; + m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributesWithNvidiaReset.data()); + if (!m_context && m_shareContext) { + // re-try without a shared glx context + m_context = glXCreateContextAttribsARB(m_display, config, 0, true, contextAttributesWithNvidiaReset.data()); + if (m_context) + m_shareContext = 0; + } + } + + if (m_context) { + m_getGraphicsResetStatus = reinterpret_cast<GLenum (QOPENGLF_APIENTRYP)()>(getProcAddress("glGetGraphicsResetStatusARB")); + } else { + contextAttributes << None; + m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributes.data()); + if (!m_context && m_shareContext) { + // re-try without a shared glx context + m_context = glXCreateContextAttribsARB(m_display, config, 0, true, contextAttributes.data()); + if (m_context) + m_shareContext = 0; + } } } } @@ -494,6 +536,12 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface) QXcbWindow *window = static_cast<QXcbWindow *>(surface); glxDrawable = window->xcb_window(); success = glXMakeCurrent(m_display, glxDrawable, m_context); + m_lost = false; + if (m_getGraphicsResetStatus && m_getGraphicsResetStatus() != GL_NO_ERROR) { + m_lost = true; + // Drop the surface. Will recreate on the next makeCurrent. + window->invalidateSurface(); + } } else if (surfaceClass == QSurface::Offscreen) { m_isPBufferCurrent = true; QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface); @@ -609,7 +657,7 @@ bool QGLXContext::isSharing() const bool QGLXContext::isValid() const { - return m_context != 0; + return m_context != 0 && !m_lost; } bool QGLXContext::m_queriedDummyContext = false; diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h index f6372582db..be9d3f5dcb 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h @@ -89,6 +89,8 @@ private: bool m_isPBufferCurrent; int m_swapInterval; bool m_ownsContext; + GLenum (APIENTRY * m_getGraphicsResetStatus)(); + bool m_lost; static bool m_queriedDummyContext; static bool m_supportsThreading; }; diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp index b4eb69e6cf..5e0f7626b4 100644 --- a/src/plugins/sqldrivers/psql/qsql_psql.cpp +++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp @@ -462,8 +462,10 @@ void QPSQLResult::cleanup() d->result = nullptr; while (!d->nextResultSets.isEmpty()) PQclear(d->nextResultSets.takeFirst()); - if (d->stmtId != InvalidStatementId) - d->drv_d_func()->finishQuery(d->stmtId); + if (d->stmtId != InvalidStatementId) { + if (d->drv_d_func()) + d->drv_d_func()->finishQuery(d->stmtId); + } d->stmtId = InvalidStatementId; setAt(QSql::BeforeFirstRow); d->currentSize = -1; diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp index cb3d905f46..6375825720 100644 --- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp @@ -56,6 +56,7 @@ #include <qregularexpression.h> #endif #include <QTimeZone> +#include <QScopedValueRollback> #if defined Q_OS_WIN # include <qt_windows.h> @@ -129,6 +130,7 @@ protected: bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) override; bool reset(const QString &query) override; bool prepare(const QString &query) override; + bool execBatch(bool arrayBind) override; bool exec() override; int size() override; int numRowsAffected() override; @@ -443,6 +445,29 @@ static QString timespecToString(const QDateTime &dateTime) } } +bool QSQLiteResult::execBatch(bool arrayBind) +{ + Q_UNUSED(arrayBind); + Q_D(QSqlResult); + QScopedValueRollback<QVector<QVariant>> valuesScope(d->values); + QVector<QVariant> values = d->values; + if (values.count() == 0) + return false; + + for (int i = 0; i < values.at(0).toList().count(); ++i) { + d->values.clear(); + QScopedValueRollback<QHash<QString, QVector<int>>> indexesScope(d->indexes); + QHash<QString, QVector<int>>::const_iterator it = d->indexes.constBegin(); + while (it != d->indexes.constEnd()) { + bindValue(it.key(), values.at(it.value().first()).toList().at(i), QSql::In); + ++it; + } + if (!exec()) + return false; + } + return true; +} + bool QSQLiteResult::exec() { Q_D(QSQLiteResult); @@ -471,7 +496,7 @@ bool QSQLiteResult::exec() // can end up in a case where for virtual tables it returns 0 even though it // has parameters if (paramCount > 1 && paramCount < values.count()) { - const auto countIndexes = [](int counter, const QList<int>& indexList) { + const auto countIndexes = [](int counter, const QVector<int> &indexList) { return counter + indexList.length(); }; @@ -485,13 +510,14 @@ bool QSQLiteResult::exec() // placeholders. So we need to ensure the QVector has only one instance of // each value as SQLite will do the rest for us. QVector<QVariant> prunedValues; - QList<int> handledIndexes; + QVector<int> handledIndexes; for (int i = 0, currentIndex = 0; i < values.size(); ++i) { if (handledIndexes.contains(i)) continue; const auto placeHolder = QString::fromUtf8(sqlite3_bind_parameter_name(d->stmt, currentIndex + 1)); - handledIndexes << d->indexes[placeHolder]; - prunedValues << values.at(d->indexes[placeHolder].first()); + const auto &indexes = d->indexes.value(placeHolder); + handledIndexes << indexes; + prunedValues << values.at(indexes.first()); ++currentIndex; } values = prunedValues; diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index cdddf227ac..0f5f9395c8 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -321,6 +321,36 @@ static const qreal closeButtonCornerRadius = 2.0; static const int headerSectionArrowHeight = 6; static const int headerSectionSeparatorInset = 2; +// One for each of QStyleHelper::WidgetSizePolicy +static const QMarginsF pullDownButtonShadowMargins[3] = { + { 0.5, -1, 0.5, 2 }, + { 0.5, -1.5, 0.5, 2.5 }, + { 0.5, 0, 0.5, 1 } +}; + +static const QMarginsF pushButtonShadowMargins[3] = { + { 1.5, -1.5, 1.5, 4.5 }, + { 1.5, -1, 1.5, 4 }, + { 1.5, 0.5, 1.5, 2.5 } +}; + +// These are frame heights as reported by Xcode 9's Interface Builder. +// Alignemnet rectangle's heights match for push and popup buttons +// with respective values 21, 18 and 15. + +static const qreal pushButtonDefaultHeight[3] = { + 32, 28, 16 +}; + +static const qreal popupButtonDefaultHeight[3] = { + 26, 22, 15 +}; + +static const int toolButtonArrowSize = 7; +static const int toolButtonArrowMargin = 2; + +static const qreal focusRingWidth = 3.5; + #if QT_CONFIG(tabbar) static bool isVerticalTabs(const QTabBar::Shape shape) { return (shape == QTabBar::RoundedEast @@ -848,7 +878,11 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg // However, this doesn't work for German, therefore only do it for English, // I suppose it would be better to do some sort of lookups for languages // that like to have really long words. - ret.setWidth(77 - 8); + // FIXME This is not exactly true. Out of context, OK buttons have their + // implicit size calculated the same way as any other button. Inside a + // QDialogButtonBox, their size should be calculated such that the action + // or accept button (i.e., rightmost) and cancel button have the same width. + ret.setWidth(69); } } else { // The only sensible thing to do is to return whatever the style suggests... @@ -1193,20 +1227,18 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int h QRect(focusRingPixmap.width() - shCornerSize, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize)); } -void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const +void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const { - static const auto focusRingWidth = 3.5; - QPainterPath focusRingPath; qreal hOffset = 0.0; qreal vOffset = 0.0; - switch (cw.first) { + switch (cw.type) { case Button_CheckBox: { - const auto cbInnerRadius = (cw.second == QStyleHelper::SizeMini ? 2.0 : 3.0); - const auto cbSize = cw.second == QStyleHelper::SizeLarge ? 13 : - cw.second == QStyleHelper::SizeSmall ? 11 : 9; // As measured - hOffset = hMargin + (cw.second == QStyleHelper::SizeLarge ? 2.5 : - cw.second == QStyleHelper::SizeSmall ? 2.0 : 1.0); // As measured + const auto cbInnerRadius = (cw.size == QStyleHelper::SizeMini ? 2.0 : 3.0); + const auto cbSize = cw.size == QStyleHelper::SizeLarge ? 13 : + cw.size == QStyleHelper::SizeSmall ? 11 : 9; // As measured + hOffset = hMargin + (cw.size == QStyleHelper::SizeLarge ? 2.5 : + cw.size == QStyleHelper::SizeSmall ? 2.0 : 1.0); // As measured vOffset = 0.5 * qreal(targetRect.height() - cbSize); const auto cbInnerRect = QRectF(0, 0, cbSize, cbSize); const auto cbOutterRadius = cbInnerRadius + focusRingWidth; @@ -1217,10 +1249,10 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int h break; } case Button_RadioButton: { - const auto rbSize = cw.second == QStyleHelper::SizeLarge ? 15 : - cw.second == QStyleHelper::SizeSmall ? 13 : 9; // As measured - hOffset = hMargin + (cw.second == QStyleHelper::SizeLarge ? 1.5 : - cw.second == QStyleHelper::SizeSmall ? 1.0 : 1.0); // As measured + const auto rbSize = cw.size == QStyleHelper::SizeLarge ? 15 : + cw.size == QStyleHelper::SizeSmall ? 13 : 9; // As measured + hOffset = hMargin + (cw.size == QStyleHelper::SizeLarge ? 1.5 : + cw.size == QStyleHelper::SizeSmall ? 1.0 : 1.0); // As measured vOffset = 0.5 * qreal(targetRect.height() - rbSize); const auto rbInnerRect = QRectF(0, 0, rbSize, rbSize); const auto rbOutterRect = rbInnerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth); @@ -1229,8 +1261,21 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int h focusRingPath.addEllipse(rbOutterRect); break; } + case Button_PopupButton: + case Button_PullDown: + case Button_PushButton: { + const qreal innerRadius = cw.type == Button_PullDown ? 4 : 3; + const qreal outterRadius = innerRadius + focusRingWidth; + hOffset = targetRect.left(); + vOffset = targetRect.top(); + const auto innerRect = targetRect.translated(-targetRect.topLeft()); + const auto outterRect = innerRect.adjusted(-hMargin, -vMargin, hMargin, vMargin); + focusRingPath.setFillRule(Qt::OddEvenFill); + focusRingPath.addRoundedRect(innerRect, innerRadius, innerRadius); + focusRingPath.addRoundedRect(outterRect, outterRadius, outterRadius); + break; + } default: - Q_UNUSED(vMargin); Q_UNREACHABLE(); } @@ -1450,128 +1495,149 @@ QStyleHelper::WidgetSizePolicy QMacStylePrivate::aquaSizeConstrain(const QStyleO #endif } -/** - Returns the free space awailable for contents inside the - button (and not the size of the contents itself) -*/ -CGRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn, - const HIThemeButtonDrawInfo *bdi) const +uint qHash(const QMacStylePrivate::CocoaControl &cw, uint seed = 0) { - CGRect outerBounds = btn->rect.toCGRect(); - // Adjust the bounds to correct for - // carbon not calculating the content bounds fully correct - if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){ - outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset; - outerBounds.size.height -= QMacStylePrivate::PushButtonBottomOffset; - } else if (bdi->kind == kThemePushButtonMini) { - outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset; - } + return ((cw.type << 2) | cw.size) ^ seed; +} - CGRect contentBounds; - HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds); - return contentBounds; +QMacStylePrivate::CocoaControl::CocoaControl() + : type(NoControl), size(QStyleHelper::SizeDefault) +{ } -/** - Calculates the size of the button contents. - This includes both the text and the icon. -*/ -QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const +QMacStylePrivate::CocoaControl::CocoaControl(CocoaControlType t, QStyleHelper::WidgetSizePolicy s) + : type(t), size(s) { - Q_Q(const QMacStyle); - QSize csz(0, 0); - QSize iconSize = btn->icon.isNull() ? QSize(0, 0) - : (btn->iconSize + QSize(QMacStylePrivate::PushButtonContentPadding, 0)); - QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1) - : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text); - csz.setWidth(iconSize.width() + textRect.width() - + ((btn->features & QStyleOptionButton::HasMenu) - ? q->proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0)); - csz.setHeight(qMax(iconSize.height(), textRect.height())); - return csz; } -/** - Checks if the actual contents of btn fits inside the free content bounds of - 'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton' - for determining which button kind to use for drawing. -*/ -bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn, - HIThemeButtonDrawInfo *bdi, - ThemeButtonKind buttonKindToCheck) const +bool QMacStylePrivate::CocoaControl::operator==(const CocoaControl &other) const { - ThemeButtonKind tmp = bdi->kind; - bdi->kind = buttonKindToCheck; - QSize contentSize = pushButtonSizeFromContents(btn); - QRect freeContentRect = QRectF::fromCGRect(pushButtonContentBounds(btn, bdi)).toRect(); - bdi->kind = tmp; - return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(), - contentSize.width(), contentSize.height())); + return other.type == type && other.size == size; } -/** - Creates a HIThemeButtonDrawInfo structure that specifies the correct button - kind and other details to use for drawing the given push button. Which - button kind depends on the size of the button, the size of the contents, - explicit user style settings, etc. -*/ -void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn, - const QWidget *widget, - const ThemeDrawState tds, - HIThemeButtonDrawInfo *bdi) const +QSizeF QMacStylePrivate::CocoaControl::defaultFrameSize() const { - ThemeDrawState tdsModified = tds; - if (btn->state & QStyle::State_On) - tdsModified = kThemeStatePressed; - bdi->version = qt_mac_hitheme_version; - bdi->state = tdsModified; - bdi->value = kThemeButtonOff; + // We need this because things like NSView.alignmentRectInsets + // or -[NSCell titleRectForBounds:] won't work unless the control + // has a reasonable frame set. IOW, it's a chicken and egg problem. + // These values are as observed in Xcode 9's Interface Builder. - if (tds == kThemeStateInactive) - bdi->state = kThemeStateActive; - if (btn->state & QStyle::State_HasFocus) - bdi->adornment = kThemeAdornmentFocus; - else - bdi->adornment = kThemeAdornmentNone; + if (type == Button_PushButton) + return QSizeF(-1, pushButtonDefaultHeight[size]); + if (type == Button_PopupButton + || type == Button_PullDown) + return QSizeF(-1, popupButtonDefaultHeight[size]); + + return QSizeF(); +} - if (btn->features & (QStyleOptionButton::Flat)) { - bdi->kind = kThemeBevelButton; +QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect) const +{ + QRectF frameRect; + if (type == QMacStylePrivate::Button_SquareButton) { + frameRect = rect.adjusted(3, 1, -3, -5) + .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth); } else { - switch (aquaSizeConstrain(btn, widget)) { - case QStyleHelper::SizeSmall: - bdi->kind = kThemePushButtonSmall; - break; - case QStyleHelper::SizeMini: - bdi->kind = kThemePushButtonMini; - break; - case QStyleHelper::SizeLarge: - // ... We should honor if the user is explicit about using the - // large button. But right now Qt will specify the large button - // as default rather than QStyleHelper::SizeDefault. - // So we treat it like QStyleHelper::SizeDefault - // to get the dynamic choosing of button kind. - case QStyleHelper::SizeDefault: - // Choose the button kind that closest match the button rect, but at the - // same time displays the button contents without clipping. - bdi->kind = kThemeBevelButton; - if (btn->rect.width() >= QMacStylePrivate::BevelButtonW && btn->rect.height() >= QMacStylePrivate::BevelButtonH){ - if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) { - if (btn->rect.height() <= QMacStylePrivate::MiniButtonH){ - if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini)) - bdi->kind = kThemePushButtonMini; - } else if (btn->rect.height() <= QMacStylePrivate::SmallButtonH){ - if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall)) - bdi->kind = kThemePushButtonSmall; - } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) { - bdi->kind = kThemePushButton; - } - } else { - bdi->kind = kThemePushButton; - } - } + const auto frameSize = defaultFrameSize(); + if (type == QMacStylePrivate::Button_PullDown) { + // Center in the style option's rect. + frameRect = QRectF(QPointF(0, (rect.height() - frameSize.height()) / 2.0), + QSizeF(rect.width(), frameSize.height())); + if (size == QStyleHelper::SizeLarge) + frameRect = frameRect.adjusted(0, 0, -6, 0).translated(3, -1); + else if (size == QStyleHelper::SizeSmall) + frameRect = frameRect.adjusted(0, 0, -4, 0).translated(2, 1); + else if (size == QStyleHelper::SizeMini) + frameRect = frameRect.adjusted(0, 0, -9, 0).translated(5, 0); + } else if (type == QMacStylePrivate::Button_PushButton) { + // Start from the style option's top-left corner. + frameRect = QRectF(rect.topLeft(), + QSizeF(rect.width(), frameSize.height())); + if (size == QStyleHelper::SizeSmall) + frameRect = frameRect.translated(0, 1.5); + else if (size == QStyleHelper::SizeMini) + frameRect = frameRect.adjusted(0, 0, -8, 0).translated(4, 4); } } + + return frameRect; +} + +QMarginsF QMacStylePrivate::CocoaControl::titleMargins() const +{ + if (type == QMacStylePrivate::Button_PushButton) { + if (size == QStyleHelper::SizeLarge) + return QMarginsF(12, 5, 12, 9); + if (size == QStyleHelper::SizeSmall) + return QMarginsF(12, 4, 12, 9); + if (size == QStyleHelper::SizeMini) + return QMarginsF(10, 1, 10, 2); + } + + if (type == QMacStylePrivate::Button_PullDown) { + if (size == QStyleHelper::SizeLarge) + return QMarginsF(7.5, 2.5, 22.5, 5.5); + if (size == QStyleHelper::SizeSmall) + return QMarginsF(7.5, 2, 20.5, 4); + if (size == QStyleHelper::SizeMini) + return QMarginsF(4.5, 1, 16.5, 2); + } + + if (type == QMacStylePrivate::Button_SquareButton) + return QMarginsF(6, 1, 6, 2); + + return QMarginsF(); +} + + +bool QMacStylePrivate::CocoaControl::getCocoaButtonTypeAndBezelStyle(NSButtonType *buttonType, NSBezelStyle *bezelStyle) const +{ + switch (type) { + case Button_CheckBox: + *buttonType = NSSwitchButton; + *bezelStyle = NSRegularSquareBezelStyle; + break; + case Button_Disclosure: + *buttonType = NSOnOffButton; + *bezelStyle = NSDisclosureBezelStyle; + break; + case Button_RadioButton: + *buttonType = NSRadioButton; + *bezelStyle = NSRegularSquareBezelStyle; + break; + case Button_SquareButton: + *buttonType = NSPushOnPushOffButton; + *bezelStyle = NSShadowlessSquareBezelStyle; + break; + case Button_PushButton: + *buttonType = NSPushOnPushOffButton; + *bezelStyle = NSRoundedBezelStyle; + break; + default: + return false; + } + + return true; +} + +QMacStylePrivate::CocoaControlType cocoaControlType(const QStyleOption *opt, const QWidget *w) +{ + if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + const bool hasMenu = btn->features & QStyleOptionButton::HasMenu; + // When the contents won't fit in a large sized button, + // and WA_MacNormalSize is not set, make the button square. + // Threshold used to be at 34, not 32. + const auto maxNonSquareHeight = pushButtonDefaultHeight[QStyleHelper::SizeLarge]; + const bool isSquare = (btn->features & QStyleOptionButton::Flat) + || (btn->rect.height() > maxNonSquareHeight + && !(w && w->testAttribute(Qt::WA_MacNormalSize))); + return (isSquare? QMacStylePrivate::Button_SquareButton : + hasMenu ? QMacStylePrivate::Button_PullDown : + QMacStylePrivate::Button_PushButton); + } + + return QMacStylePrivate::NoControl; } /** @@ -1595,8 +1661,8 @@ void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThem bdi->state = tds; QStyleHelper::WidgetSizePolicy aSize = aquaSizeConstrain(combo, widget); - cw->first = combo->editable ? ComboBox : Button_PopupButton; - cw->second = aSize; + cw->type = combo->editable ? ComboBox : Button_PopupButton; + cw->size = aSize; switch (aSize) { case QStyleHelper::SizeMini: bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini) @@ -1628,26 +1694,26 @@ void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThem // or return any subcontrol size of the poor thing. if (h < 9) { bdi->kind = kThemeComboBoxMini; - cw->second = QStyleHelper::SizeMini; + cw->size = QStyleHelper::SizeMini; } else if (h < 22) { bdi->kind = kThemeComboBoxSmall; - cw->second = QStyleHelper::SizeSmall; + cw->size = QStyleHelper::SizeSmall; } else { bdi->kind = kThemeComboBox; - cw->second = QStyleHelper::SizeLarge; + cw->size = QStyleHelper::SizeLarge; } } else #endif { if (h < 21) { bdi->kind = kThemeComboBoxMini; - cw->second = QStyleHelper::SizeMini; + cw->size = QStyleHelper::SizeMini; } else if (h < 26) { bdi->kind = kThemeComboBoxSmall; - cw->second = QStyleHelper::SizeSmall; + cw->size = QStyleHelper::SizeSmall; } else { bdi->kind = kThemeComboBox; - cw->second = QStyleHelper::SizeLarge; + cw->size = QStyleHelper::SizeLarge; } } } else { @@ -1658,13 +1724,13 @@ void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThem // the use of LargeButtons explicit. if (h < 21) { bdi->kind = kThemePopupButtonMini; - cw->second = QStyleHelper::SizeMini; + cw->size = QStyleHelper::SizeMini; } else if (h < 26) { bdi->kind = kThemePopupButtonSmall; - cw->second = QStyleHelper::SizeSmall; + cw->size = QStyleHelper::SizeSmall; } else { bdi->kind = kThemePopupButton; - cw->second = QStyleHelper::SizeLarge; + cw->size = QStyleHelper::SizeLarge; } } break; @@ -1681,8 +1747,8 @@ CGRect QMacStylePrivate::comboboxInnerBounds(const CGRect &outerBounds, const Co // Carbon draw parts of the view outside the rect. // So make the rect a bit smaller to compensate // (I wish HIThemeGetButtonBackgroundBounds worked) - if (cocoaWidget.first == Button_PopupButton) { - switch (cocoaWidget.second) { + if (cocoaWidget.type == Button_PopupButton) { + switch (cocoaWidget.size) { case QStyleHelper::SizeSmall: innerBounds.origin.x += 3; innerBounds.origin.y += 3; @@ -1702,8 +1768,8 @@ CGRect QMacStylePrivate::comboboxInnerBounds(const CGRect &outerBounds, const Co innerBounds.size.width -= 5; innerBounds.size.height -= 6; } - } else if (cocoaWidget.first == ComboBox) { - switch (cocoaWidget.second) { + } else if (cocoaWidget.type == ComboBox) { + switch (cocoaWidget.size) { case QStyleHelper::SizeSmall: innerBounds.origin.x += 3; innerBounds.origin.y += 3; @@ -1856,28 +1922,28 @@ ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags) case kThemePopupButton: case kThemePopupButtonSmall: case kThemePopupButtonMini: - w.first = Button_PopupButton; + w.type = Button_PopupButton; break; case kThemeComboBox: - w.first = ComboBox; + w.type = ComboBox; break; case kThemeArrowButton: - w.first = Button_Disclosure; + w.type = Button_Disclosure; break; case kThemeCheckBox: case kThemeCheckBoxSmall: case kThemeCheckBoxMini: - w.first = Button_CheckBox; + w.type = Button_CheckBox; break; case kThemeRadioButton: case kThemeRadioButtonSmall: case kThemeRadioButtonMini: - w.first = Button_RadioButton; + w.type = Button_RadioButton; break; case kThemePushButton: case kThemePushButtonSmall: case kThemePushButtonMini: - w.first = Button_PushButton; + w.type = Button_PushButton; break; default: break; @@ -1888,37 +1954,27 @@ ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags) case kThemePopupButtonSmall: case kThemeCheckBoxSmall: case kThemeRadioButtonSmall: - w.second = QStyleHelper::SizeSmall; + w.size = QStyleHelper::SizeSmall; break; case kThemePushButtonMini: case kThemePopupButtonMini: case kThemeCheckBoxMini: case kThemeRadioButtonMini: - w.second = QStyleHelper::SizeMini; + w.size = QStyleHelper::SizeMini; break; default: - w.second = QStyleHelper::SizeLarge; + w.size = QStyleHelper::SizeLarge; break; } return w; } -static NSButton *makeButton(NSButtonType type, NSBezelStyle style) -{ - NSButton *b = [[NSButton alloc] init]; - b.title = @""; - b.buttonType = type; - b.bezelStyle = style; - return b; -} - NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const { NSView *bv = cocoaControls.value(widget, nil); - if (!bv) { - switch (widget.first) { + switch (widget.type) { case Box: { NSBox *bc = [[NSBox alloc] init]; bc.title = @""; @@ -1929,31 +1985,30 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const break; } case Button_CheckBox: - bv = makeButton(NSSwitchButton, NSRegularSquareBezelStyle); - break; case Button_Disclosure: - bv = makeButton(NSOnOffButton, NSDisclosureBezelStyle); + case Button_PushButton: + case Button_RadioButton: + case Button_SquareButton: { + NSButton *bc = [[NSButton alloc] init]; + bc.title = @""; + // See below for style and bezel setting. + bv = bc; break; + } case Button_PopupButton: case Button_PullDown: { NSPopUpButton *bc = [[NSPopUpButton alloc] init]; bc.title = @""; - if (widget.first == Button_PullDown) + if (widget.type == Button_PullDown) bc.pullsDown = YES; bv = bc; break; } - case Button_PushButton: - bv = makeButton(NSMomentaryLightButton, NSRoundedBezelStyle); - break; - case Button_RadioButton: - bv = makeButton(NSRadioButton, NSRegularSquareBezelStyle); - break; case Button_WindowClose: case Button_WindowMiniaturize: case Button_WindowZoom: { const NSWindowButton button = [=] { - switch (widget.first) { + switch (widget.type) { case Button_WindowClose: return NSWindowCloseButton; case Button_WindowMiniaturize: @@ -2020,7 +2075,7 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const if ([bv isKindOfClass:[NSControl class]]) { auto *ctrl = static_cast<NSControl *>(bv); - switch (widget.second) { + switch (widget.size) { case QStyleHelper::SizeSmall: ctrl.controlSize = NSSmallControlSize; break; @@ -2030,11 +2085,11 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const default: break; } - } else if (widget.first == ProgressIndicator_Determinate || - widget.first == ProgressIndicator_Indeterminate) { + } else if (widget.type == ProgressIndicator_Determinate || + widget.type == ProgressIndicator_Indeterminate) { auto *pi = static_cast<NSProgressIndicator *>(bv); - pi.indeterminate = (widget.first == ProgressIndicator_Indeterminate); - switch (widget.second) { + pi.indeterminate = (widget.type == ProgressIndicator_Indeterminate); + switch (widget.size) { case QStyleHelper::SizeSmall: pi.controlSize = NSSmallControlSize; break; @@ -2049,6 +2104,16 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const cocoaControls.insert(widget, bv); } + NSButtonType buttonType; + NSBezelStyle bezelStyle; + if (widget.getCocoaButtonTypeAndBezelStyle(&buttonType, &bezelStyle)) { + // FIXME We need to reset the button's type and + // bezel style properties, even when cached. + auto *button = static_cast<NSButton *>(bv); + button.buttonType = buttonType; + button.bezelStyle = bezelStyle; + } + return bv; } @@ -2056,7 +2121,7 @@ NSCell *QMacStylePrivate::cocoaCell(CocoaControl widget) const { NSCell *cell = cocoaCells[widget]; if (!cell) { - switch (widget.first) { + switch (widget.type) { case Stepper: cell = [[NSStepperCell alloc] init]; break; @@ -2071,7 +2136,7 @@ NSCell *QMacStylePrivate::cocoaCell(CocoaControl widget) const break; } - switch (widget.second) { + switch (widget.size) { case QStyleHelper::SizeSmall: cell.controlSize = NSSmallControlSize; break; @@ -2088,19 +2153,14 @@ NSCell *QMacStylePrivate::cocoaCell(CocoaControl widget) const return cell; } -void QMacStylePrivate::drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &qtRect, QPainter *p, bool isQWidget, __attribute__((noescape)) DrawRectBlock drawRectBlock) const +void QMacStylePrivate::drawNSViewInRect(CocoaControl widget, NSView *view, const QRectF &qtRect, QPainter *p, bool isQWidget, __attribute__((noescape)) DrawRectBlock drawRectBlock) const { + Q_UNUSED(isQWidget); QPoint offset; if (widget == CocoaControl(Button_PopupButton, QStyleHelper::SizeSmall)) offset.setY(1); else if (widget == CocoaControl(Button_PopupButton, QStyleHelper::SizeMini)) offset = QPoint(2, -1); - else if (widget == CocoaControl(Button_PullDown, QStyleHelper::SizeLarge)) - offset = isQWidget ? QPoint(3, -1) : QPoint(-1, -3); - else if (widget == CocoaControl(Button_PullDown, QStyleHelper::SizeSmall)) - offset = QPoint(2, 1); - else if (widget == CocoaControl(Button_PullDown, QStyleHelper::SizeMini)) - offset = QPoint(5, 0); else if (widget == CocoaControl(ComboBox, QStyleHelper::SizeLarge)) offset = QPoint(3, 0); @@ -2448,6 +2508,10 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW ret = 0; break; + case PM_MenuButtonIndicator: + ret = toolButtonArrowSize; + break; + case QStyle::PM_MenuDesktopFrameWidth: ret = 5; break; @@ -3613,7 +3677,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const QWidget *w) const { Q_D(const QMacStyle); - ThemeDrawState tds = d->getDrawState(opt->state); QMacCGContext cg(p); QWindow *window = w && w->window() ? w->window()->windowHandle() : QStyleHelper::styleObjectWindow(opt->styleObject); @@ -3785,120 +3848,80 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter break; } + const bool hasFocus = btn->state & State_HasFocus; + const bool isActive = btn->state & State_Active; + // a focused auto-default button within an active window // takes precedence over a normal default button if ((btn->features & QStyleOptionButton::AutoDefaultButton) - && (opt->state & State_Active) - && (opt->state & State_HasFocus)) - d->autoDefaultButton = opt->styleObject; - else if (d->autoDefaultButton == opt->styleObject) + && isActive && hasFocus) + d->autoDefaultButton = btn->styleObject; + else if (d->autoDefaultButton == btn->styleObject) d->autoDefaultButton = nullptr; - bool hasMenu = btn->features & QStyleOptionButton::HasMenu; - HIThemeButtonDrawInfo bdi; - d->initHIThemePushButton(btn, w, tds, &bdi); - - if (!hasMenu) { - // HITheme is not drawing a nice focus frame around buttons. - // We'll do it ourselves further down. - bdi.adornment &= ~kThemeAdornmentFocus; - - // We can't rely on an animation existing to test for the default look. That means a bit - // more logic (notice that the logic is slightly different for the bevel and the label). - if (tds == kThemeStateActive - && (btn->features & QStyleOptionButton::DefaultButton - || (btn->features & QStyleOptionButton::AutoDefaultButton - && d->autoDefaultButton == btn->styleObject))) - bdi.adornment |= kThemeAdornmentDefault; - } + const bool isEnabled = btn->state & State_Enabled; + const bool isPressed = btn->state & State_Sunken; + const bool isHighlighted = isActive && + ((btn->state & State_On) + || (btn->features & QStyleOptionButton::DefaultButton) + || (btn->features & QStyleOptionButton::AutoDefaultButton + && d->autoDefaultButton == btn->styleObject)); + const bool hasMenu = btn->features & QStyleOptionButton::HasMenu; + const auto ct = cocoaControlType(btn, w); + const auto cs = d->effectiveAquaSizeConstrain(btn, w); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); + // Ensure same size and location as we used to have with HITheme. + // This is more convoluted than we initialy thought. See for example + // differences between plain and menu button frames. + const QRectF frameRect = cw.adjustedControlFrame(btn->rect); + pb.frame = frameRect.toCGRect(); + + pb.enabled = isEnabled; + [pb highlight:isPressed]; + pb.state = isHighlighted && !isPressed ? NSOnState : NSOffState; + d->drawNSViewInRect(cw, pb, frameRect, p, true, ^(CGContextRef __unused ctx, const CGRect &r) { + [pb.cell drawBezelWithFrame:r inView:pb.superview]; + }); - // Unlike Carbon, we want the button to always be drawn inside its bounds. - // Therefore, make the button a bit smaller, so that even if it got focus, - // the focus 'shadow' will be inside. - CGRect newRect = btn->rect.toCGRect(); - if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall) { - newRect.origin.x += QMacStylePrivate::PushButtonLeftOffset; - newRect.origin.y += QMacStylePrivate::PushButtonTopOffset; - newRect.size.width -= QMacStylePrivate::PushButtonRightOffset; - newRect.size.height -= QMacStylePrivate::PushButtonBottomOffset; - } else if (bdi.kind == kThemePushButtonMini) { - newRect.origin.x += QMacStylePrivate::PushButtonLeftOffset - 2; - newRect.origin.y += QMacStylePrivate::PushButtonTopOffset; - newRect.size.width -= QMacStylePrivate::PushButtonRightOffset - 4; + if (hasMenu && cw.type == QMacStylePrivate::Button_SquareButton) { + // Using -[NSPopuButtonCell drawWithFrame:inView:] above won't do + // it right because we don't set the text in the native button. + const int mbi = proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, w); + const auto ir = frameRect.toRect(); + int arrowYOffset = 0; +#if 0 + // FIXME What's this for again? + if (!w) { + // adjustment for Qt Quick Controls + arrowYOffset -= ir.top(); + if (cw.second == QStyleHelper::SizeSmall) + arrowYOffset += 1; + } +#endif + const auto ar = visualRect(btn->direction, ir, QRect(ir.right() - mbi - 6, ir.height() / 2 - arrowYOffset, mbi, mbi)); + + QStyleOption arrowOpt = *opt; + arrowOpt.rect = ar; + proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, w); } - QMacStylePrivate::CocoaControl cw = QMacStylePrivate::cocoaControlFromHIThemeButtonKind(bdi.kind); - if (hasMenu) - cw.first = QMacStylePrivate::Button_PullDown; - if (hasMenu && bdi.kind != kThemeBevelButton) { - NSPopUpButton *pdb = (NSPopUpButton *)d->cocoaControl(cw); - [pdb highlight:(bdi.state == kThemeStatePressed)]; - pdb.enabled = bdi.state != kThemeStateUnavailable && bdi.state != kThemeStateUnavailableInactive; - QRect rect = opt->rect; - rect.adjust(0, 0, cw.second == QStyleHelper::SizeSmall ? -4 : cw.second == QStyleHelper::SizeMini ? -9 : -6, 0); - d->drawNSViewInRect(cw, pdb, rect, p, w != 0); - } else if (hasMenu && bdi.state == kThemeStatePressed) - d->drawColorlessButton(newRect, &bdi, cw, p, opt); - else - HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0); if (btn->state & State_HasFocus) { - CGRect focusRect = newRect; - if (bdi.kind == kThemePushButton) - focusRect.size.height += 1; // Another thing HITheme and Cocoa seem to disagree about. - else if (bdi.kind == kThemePushButtonMini) - focusRect.size.height = 15; // Our QPushButton sizes are really weird - - if (bdi.adornment & kThemeAdornmentDefault || bdi.state == kThemeStatePressed) { - if (bdi.kind == kThemePushButtonSmall) { - focusRect = CGRectInset(focusRect, -1, 0); - } else if (bdi.kind == kThemePushButtonMini) { - focusRect = CGRectInset(focusRect, 1, 0); - } - } else { - if (bdi.kind == kThemePushButton) { - focusRect = CGRectInset(focusRect, 1, 1); - } else if (bdi.kind == kThemePushButtonSmall) { - focusRect = CGRectInset(focusRect, 0, 2); - } else if (bdi.kind == kThemePushButtonMini) { - focusRect = CGRectInset(focusRect, 2, 1); - } - } - - const qreal radius = bdi.kind == kThemeBevelButton ? 0 : 3; + // TODO Remove and use QFocusFrame instead. const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, btn, w); const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, btn, w); - const QRect focusTargetRect(focusRect.origin.x, focusRect.origin.y, focusRect.size.width, focusRect.size.height); - d->drawFocusRing(p, focusTargetRect.adjusted(-hMargin, -vMargin, hMargin, vMargin), hMargin, vMargin, radius); - } - - if (hasMenu && bdi.kind == kThemeBevelButton) { - int mbi = proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, w); - QRect ir = btn->rect; - int arrowXOffset = bdi.kind == kThemePushButton ? 6 : - bdi.kind == kThemePushButtonSmall ? 7 : 8; - int arrowYOffset = bdi.kind == kThemePushButton ? 3 : - bdi.kind == kThemePushButtonSmall ? 1 : 2; - if (!w) { - // adjustment for Qt Quick Controls - arrowYOffset -= ir.top(); - if (bdi.kind == kThemePushButtonSmall) - arrowYOffset += 1; + if (cw.type == QMacStylePrivate::Button_SquareButton) { + const auto focusRect = frameRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth); + d->drawFocusRing(p, focusRect.toAlignedRect(), hMargin, vMargin, 0); + } else { + auto focusRect = QRectF::fromCGRect([pb alignmentRectForFrame:pb.frame]); + if (cw.type == QMacStylePrivate::Button_PushButton) + focusRect -= pushButtonShadowMargins[cw.size]; + else if (cw.type == QMacStylePrivate::Button_PullDown) + focusRect -= pullDownButtonShadowMargins[cw.size]; + d->drawFocusRing(p, focusRect, hMargin, vMargin, cw); } - QRect ar = QRect(ir.right() - mbi - QMacStylePrivate::PushButtonRightOffset, - ir.height() / 2 - arrowYOffset, mbi, ir.height() / 2); - ar = visualRect(btn->direction, ir, ar); - CGRect arrowRect = CGRectMake(ar.x() + arrowXOffset, ar.y(), ar.width(), ar.height()); - - HIThemePopupArrowDrawInfo pdi; - pdi.version = qt_mac_hitheme_version; - pdi.state = tds == kThemeStateInactive ? kThemeStateActive : tds; - pdi.orientation = kThemeArrowDown; - if (bdi.kind == kThemePushButtonMini) - pdi.size = kThemeArrow5pt; - else if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall) - pdi.size = kThemeArrow7pt; - HIThemeDrawPopupArrow(&arrowRect, &pdi, cg, kHIThemeOrientationNormal); } } break; @@ -3909,13 +3932,18 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // windows style if it has an icon and text, then it should be more like a // tab. So, cheat a little here. However, if it *is* only an icon // the windows style works great, so just use that implementation. + const bool isEnabled = btn.state & State_Enabled; const bool hasMenu = btn.features & QStyleOptionButton::HasMenu; const bool hasIcon = !btn.icon.isNull(); const bool hasText = !btn.text.isEmpty(); + const bool isActive = btn.state & State_Active; + const bool isPressed = btn.state & State_Sunken; + + const auto ct = cocoaControlType(&btn, w); - if (!hasMenu) { - if (tds == kThemeStatePressed - || (tds == kThemeStateActive + if (!hasMenu && ct != QMacStylePrivate::Button_SquareButton) { + if (isPressed + || (isActive && isEnabled && ((btn.features & QStyleOptionButton::DefaultButton && !d->autoDefaultButton) || d->autoDefaultButton == btn.styleObject))) btn.palette.setColor(QPalette::ButtonText, Qt::white); @@ -3926,7 +3954,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } else { QRect freeContentRect = btn.rect; QRect textRect = itemTextRect( - btn.fontMetrics, freeContentRect, Qt::AlignCenter, btn.state & State_Enabled, btn.text); + btn.fontMetrics, freeContentRect, Qt::AlignCenter, isEnabled, btn.text); if (hasMenu) { textRect.moveTo(w ? 15 : 11, textRect.top()); // Supports Qt Quick Controls } @@ -3935,7 +3963,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter int contentW = textRect.width(); if (hasMenu) contentW += proxy()->pixelMetric(PM_MenuButtonIndicator) + 4; - QIcon::Mode mode = btn.state & State_Enabled ? QIcon::Normal : QIcon::Disabled; + QIcon::Mode mode = isEnabled ? QIcon::Normal : QIcon::Disabled; if (mode == QIcon::Normal && btn.state & State_HasFocus) mode = QIcon::Active; // Decide if the icon is should be on or off: @@ -3959,7 +3987,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (hasText) { textRect = visualRect(btn.direction, freeContentRect, textRect); proxy()->drawItemText(p, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, btn.palette, - (btn.state & State_Enabled), btn.text, QPalette::ButtonText); + isEnabled, btn.text, QPalette::ButtonText); } } } @@ -4644,13 +4672,20 @@ QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt, break; case SE_PushButtonContents: if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { - // Unlike Carbon, we want the button to always be drawn inside its bounds. - // Therefore, the button is a bit smaller, so that even if it got focus, - // the focus 'shadow' will be inside. Adjust the content rect likewise. - HIThemeButtonDrawInfo bdi; - d->initHIThemePushButton(btn, widget, d->getDrawState(opt->state), &bdi); - CGRect contentRect = d->pushButtonContentBounds(btn, &bdi); - rect = QRectF::fromCGRect(contentRect).toRect(); + // Comment from the old HITheme days: + // "Unlike Carbon, we want the button to always be drawn inside its bounds. + // Therefore, the button is a bit smaller, so that even if it got focus, + // the focus 'shadow' will be inside. Adjust the content rect likewise." + // In the future, we should consider using -[NSCell titleRectForBounds:]. + // Since it requires configuring the NSButton fully, i.e. frame, image, + // title and font, we keep things more manual until we are more familiar + // with side effects when changing NSButton state. + const auto ct = cocoaControlType(btn, widget); + const auto cs = d->effectiveAquaSizeConstrain(btn, widget); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + const auto frameRect = cw.adjustedControlFrame(btn->rect); + const auto titleMargins = cw.titleMargins(); + rect = (frameRect - titleMargins).toRect(); } break; case SE_HeaderLabel: { @@ -5111,16 +5146,15 @@ QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt, return rect; } -static inline void drawToolbarButtonArrow(const QRect &toolButtonRect, ThemeDrawState tds, CGContextRef cg) +void QMacStylePrivate::drawToolbarButtonArrow(const QStyleOption *opt, QPainter *p) const { - QRect arrowRect = QRect(toolButtonRect.right() - 9, toolButtonRect.bottom() - 9, 7, 5); - HIThemePopupArrowDrawInfo padi; - padi.version = qt_mac_hitheme_version; - padi.state = tds; - padi.orientation = kThemeArrowDown; - padi.size = kThemeArrow7pt; - CGRect cgRect = arrowRect.toCGRect(); - HIThemeDrawPopupArrow(&cgRect, &padi, cg, kHIThemeOrientationNormal); + Q_Q(const QMacStyle); + QStyleOption arrowOpt = *opt; + arrowOpt.rect = QRect(opt->rect.right() - (toolButtonArrowSize + toolButtonArrowMargin), + opt->rect.bottom() - (toolButtonArrowSize + toolButtonArrowMargin), + toolButtonArrowSize, + toolButtonArrowSize); + q->proxy()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &arrowOpt, p); } void QMacStylePrivate::setupNSGraphicsContext(CGContextRef cg, bool flipped) const @@ -5629,7 +5663,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget); } else if ((tb->features & QStyleOptionToolButton::HasMenu) && (tb->toolButtonStyle != Qt::ToolButtonTextOnly && !tb->icon.isNull())) { - drawToolbarButtonArrow(tb->rect, tds, cg); + d->drawToolbarButtonArrow(tb, p); } if (tb->state & State_On) { QWindow *window = 0; @@ -5654,81 +5688,45 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } else #endif // QT_NO_ACCESSIBILITY { - ThemeButtonKind bkind = kThemeBevelButton; - switch (d->aquaSizeConstrain(opt, widget)) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - bkind = kThemeBevelButton; - break; - case QStyleHelper::SizeMini: - case QStyleHelper::SizeSmall: - bkind = kThemeSmallBevelButton; - break; - } - - QRect button, menuarea; - button = proxy()->subControlRect(cc, tb, SC_ToolButton, widget); - menuarea = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget); - State bflags = tb->state, - mflags = tb->state; + auto bflags = tb->state; if (tb->subControls & SC_ToolButton) bflags |= State_Sunken; + auto mflags = tb->state; if (tb->subControls & SC_ToolButtonMenu) mflags |= State_Sunken; if (tb->subControls & SC_ToolButton) { if (bflags & (State_Sunken | State_On | State_Raised)) { - HIThemeButtonDrawInfo bdi; - bdi.version = qt_mac_hitheme_version; - bdi.state = tds; - bdi.adornment = kThemeAdornmentNone; - bdi.kind = bkind; - bdi.value = kThemeButtonOff; - if (tb->state & State_HasFocus) - bdi.adornment = kThemeAdornmentFocus; - if (tb->state & State_Sunken) - bdi.state = kThemeStatePressed; - if (tb->state & State_On) - bdi.value = kThemeButtonOn; - - CGRect myRect, macRect; - myRect = CGRectMake(tb->rect.x(), tb->rect.y(), - tb->rect.width(), tb->rect.height()); - HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect); - - const auto offMargins = QMargins(int(myRect.origin.x - macRect.origin.x), - int(myRect.origin.y - macRect.origin.y), - int(macRect.size.width - myRect.size.width), - int(macRect.size.height - myRect.size.height)); - myRect = button.marginsRemoved(offMargins).toCGRect(); - HIThemeDrawButton(&myRect, &bdi, cg, kHIThemeOrientationNormal, 0); + const bool isEnabled = tb->state & State_Enabled; + const bool isPressed = tb->state & State_Sunken; + const bool isHighlighted = (tb->state & State_Active) && (tb->state & State_On); + const auto ct = QMacStylePrivate::Button_PushButton; + const auto cs = d->effectiveAquaSizeConstrain(opt, widget); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); + pb.bezelStyle = NSShadowlessSquareBezelStyle; // TODO Use NSTexturedRoundedBezelStyle in the future. + pb.frame = opt->rect.toCGRect(); + pb.buttonType = NSPushOnPushOffButton; + pb.enabled = isEnabled; + [pb highlight:isPressed]; + pb.state = isHighlighted && !isPressed ? NSOnState : NSOffState; + const auto buttonRect = proxy()->subControlRect(cc, tb, SC_ToolButton, widget); + d->drawNSViewInRect(cw, pb, buttonRect, p, ^(CGContextRef __unused ctx, const CGRect &rect) { + [pb.cell drawBezelWithFrame:rect inView:pb]; + }); } } if (tb->subControls & SC_ToolButtonMenu) { - HIThemeButtonDrawInfo bdi; - bdi.version = qt_mac_hitheme_version; - bdi.state = tds; - bdi.value = kThemeButtonOff; - bdi.adornment = kThemeAdornmentNone; - bdi.kind = bkind; - if (tb->state & State_HasFocus) - bdi.adornment = kThemeAdornmentFocus; - if (tb->state & (State_On | State_Sunken) - || (tb->activeSubControls & SC_ToolButtonMenu)) - bdi.state = kThemeStatePressed; - CGRect cgRect = menuarea.toCGRect(); - HIThemeDrawButton(&cgRect, &bdi, cg, kHIThemeOrientationNormal, 0); - QRect r(menuarea.x() + ((menuarea.width() / 2) - 3), menuarea.height() - 8, 8, 8); - HIThemePopupArrowDrawInfo padi; - padi.version = qt_mac_hitheme_version; - padi.state = tds; - padi.orientation = kThemeArrowDown; - padi.size = kThemeArrow7pt; - cgRect = r.toCGRect(); - HIThemeDrawPopupArrow(&cgRect, &padi, cg, kHIThemeOrientationNormal); + const auto menuRect = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget); + QStyleOption arrowOpt = *tb; + arrowOpt.rect = QRect(menuRect.x() + ((menuRect.width() - toolButtonArrowSize) / 2), + menuRect.height() - (toolButtonArrowSize + toolButtonArrowMargin), + toolButtonArrowSize, + toolButtonArrowSize); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget); } else if (tb->features & QStyleOptionToolButton::HasMenu) { - drawToolbarButtonArrow(tb->rect, tds, cg); + d->drawToolbarButtonArrow(tb, p); } QRect buttonRect = proxy()->subControlRect(CC_ToolButton, tb, SC_ToolButton, widget); int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget); @@ -6201,7 +6199,7 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, case CT_SpinBox: if (qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { const int buttonWidth = 20; // FIXME Use subControlRect() - sz += QSize(buttonWidth, -3); + sz += QSize(buttonWidth, 0); } break; #endif @@ -6326,16 +6324,33 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, } break; #endif - case QStyle::CT_PushButton: + case QStyle::CT_PushButton: { + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) + if (btn->features & QStyleOptionButton::CommandLinkButton) + return QCommonStyle::sizeFromContents(ct, opt, sz, widget); + // By default, we fit the contents inside a normal rounded push button. // Do this by add enough space around the contents so that rounded // borders (including highlighting when active) will show. - sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 12; - if (opt->state & QStyle::State_Small) - sz.rheight() += 14; + // TODO Use QFocusFrame and get rid of these horrors. + QSize macsz; + const auto controlSize = d->effectiveAquaSizeConstrain(opt, widget, CT_PushButton, sz, &macsz); + // FIXME See comment in CT_PushButton case in qt_aqua_get_known_size(). + if (macsz.width() != -1) + sz.setWidth(macsz.width()); + else + sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 12; + // All values as measured from HIThemeGetButtonBackgroundBounds() + if (controlSize != QStyleHelper::SizeMini) + sz.rwidth() += 12; // We like 12 over here. + if (controlSize == QStyleHelper::SizeLarge && sz.height() > 16) + sz.rheight() += pushButtonDefaultHeight[QStyleHelper::SizeLarge] - 16; + else if (controlSize == QStyleHelper::SizeMini) + sz.setHeight(24); // FIXME Our previous HITheme-based logic returned this. else - sz.rheight() += 4; + sz.setHeight(pushButtonDefaultHeight[controlSize]); break; + } case QStyle::CT_MenuItem: if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { int maxpmw = mi->maxIconWidth; @@ -6442,7 +6457,8 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget); } - if (useAquaGuideline){ + if (useAquaGuideline && ct != CT_PushButton) { + // TODO Probably going away at some point QSize macsz; if (d->aquaSizeConstrain(opt, widget, ct, sz, &macsz) != QStyleHelper::SizeDefault) { if (macsz.width() != -1) @@ -6457,65 +6473,11 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){ const auto widgetSize = d->aquaSizeConstrain(opt, widget); QMacStylePrivate::CocoaControl cw; - cw.first = combo->editable ? QMacStylePrivate::ComboBox : QMacStylePrivate::Button_PopupButton; - cw.second = widgetSize; + cw.type = combo->editable ? QMacStylePrivate::ComboBox : QMacStylePrivate::Button_PopupButton; + cw.size = widgetSize; const CGRect diffRect = QMacStylePrivate::comboboxInnerBounds(CGRectZero, cw); sz.rwidth() -= qRound(diffRect.size.width); sz.rheight() -= qRound(diffRect.size.height); - } else if (ct == CT_PushButton || ct == CT_ToolButton){ - ThemeButtonKind bkind; - QStyleHelper::WidgetSizePolicy widgetSize = d->aquaSizeConstrain(opt, widget); - switch (ct) { - default: - case CT_PushButton: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { - if (btn->features & QStyleOptionButton::CommandLinkButton) { - return QCommonStyle::sizeFromContents(ct, opt, sz, widget); - } - } - - switch (widgetSize) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - bkind = kThemePushButton; - break; - case QStyleHelper::SizeSmall: - bkind = kThemePushButtonSmall; - break; - case QStyleHelper::SizeMini: - bkind = kThemePushButtonMini; - break; - } - break; - case CT_ToolButton: - switch (widgetSize) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - bkind = kThemeLargeBevelButton; - break; - case QStyleHelper::SizeMini: - case QStyleHelper::SizeSmall: - bkind = kThemeSmallBevelButton; - } - break; - } - - HIThemeButtonDrawInfo bdi; - bdi.version = qt_mac_hitheme_version; - bdi.state = kThemeStateActive; - bdi.kind = bkind; - bdi.value = kThemeButtonOff; - bdi.adornment = kThemeAdornmentNone; - CGRect macRect, myRect; - myRect = CGRectMake(0, 0, sz.width(), sz.height()); - HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect); - // Mini buttons only return their actual size in HIThemeGetButtonBackgroundBounds, so help them out a bit (guess), - if (bkind == kThemePushButtonMini) - macRect.size.height += 8.; - else if (bkind == kThemePushButtonSmall) - macRect.size.height -= 10; - sz.setWidth(sz.width() + int(macRect.size.width - myRect.size.width)); - sz.setHeight(sz.height() + int(macRect.size.height - myRect.size.height)); } return sz; } diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h index 00dc8a9b53..e43b16d0f1 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h @@ -190,6 +190,7 @@ public: Button_PullDown, // QPushButton with menu Button_PushButton, Button_RadioButton, + Button_SquareButton, // Oversized QPushButton Button_WindowClose, Button_WindowMiniaturize, Button_WindowZoom, @@ -206,7 +207,23 @@ public: TextField }; - typedef QPair<CocoaControlType, QStyleHelper::WidgetSizePolicy> CocoaControl; + + struct CocoaControl { + CocoaControl(); + CocoaControl(CocoaControlType t, QStyleHelper::WidgetSizePolicy s); + + CocoaControlType type; + QStyleHelper::WidgetSizePolicy size; + + bool operator==(const CocoaControl &other) const; + + QSizeF defaultFrameSize() const; + QRectF adjustedControlFrame(const QRectF &rect) const; + QMarginsF titleMargins() const; + + bool getCocoaButtonTypeAndBezelStyle(NSButtonType *buttonType, NSBezelStyle *bezelStyle) const; + }; + typedef void (^DrawRectBlock)(CGContextRef, const CGRect &); @@ -273,11 +290,13 @@ public: void setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const; - void drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, __attribute__((noescape)) DrawRectBlock drawRectBlock = nil) const; + void drawNSViewInRect(CocoaControl widget, NSView *view, const QRectF &rect, QPainter *p, bool isQWidget = true, __attribute__((noescape)) DrawRectBlock drawRectBlock = nil) const; void resolveCurrentNSView(QWindow *window) const; void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const; - void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const; + void drawFocusRing(QPainter *p, const QRectF &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const; + + void drawToolbarButtonArrow(const QStyleOption *opt, QPainter *p) const; QPainterPath windowPanelPath(const QRectF &r) const; diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp index ff27cab98a..733b3a9e9c 100644 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp +++ b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp @@ -122,7 +122,8 @@ static inline HDC hdcForWidgetBackingStore(const QWidget *widget) { if (QBackingStore *backingStore = backingStoreForWidget(widget)) { QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); - return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore)); + if (nativeInterface) + return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore)); } return 0; } |