diff options
Diffstat (limited to 'src/plugins/styles/mac/qmacstyle_mac.mm')
-rw-r--r-- | src/plugins/styles/mac/qmacstyle_mac.mm | 753 |
1 files changed, 366 insertions, 387 deletions
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 6563e95ceb..3f57f284e6 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* Note: The qdoc comments for QMacStyle are contained in @@ -80,11 +44,6 @@ QT_USE_NAMESPACE -static QWindow *qt_getWindow(const QWidget *widget) -{ - return widget ? widget->window()->windowHandle() : 0; -} - @interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator @property (readonly, nonatomic) NSInteger animators; @@ -195,46 +154,13 @@ const int QMacStylePrivate::PushButtonLeftOffset = 6; const int QMacStylePrivate::PushButtonRightOffset = 12; const int QMacStylePrivate::PushButtonContentPadding = 6; -QVector<QPointer<QObject> > QMacStylePrivate::scrollBars; +const int pushButtonBevelRectOffsets[3] = { + QMacStylePrivate::PushButtonLeftOffset, 5, 5 +}; -// Title bar gradient colors for Lion were determined by inspecting PSDs exported -// using CoreUI's CoreThemeDocument; there is no public API to retrieve them +QVector<QPointer<QObject> > QMacStylePrivate::scrollBars; -static QLinearGradient titlebarGradientActive() -{ - static QLinearGradient darkGradient = [](){ - QLinearGradient gradient; - // FIXME: colors are chosen somewhat arbitrarily and could be fine-tuned, - // or ideally determined by calling a native API. - gradient.setColorAt(0, QColor(47, 47, 47)); - return gradient; - }(); - static QLinearGradient lightGradient = [](){ - QLinearGradient gradient; - gradient.setColorAt(0, QColor(235, 235, 235)); - gradient.setColorAt(0.5, QColor(210, 210, 210)); - gradient.setColorAt(0.75, QColor(195, 195, 195)); - gradient.setColorAt(1, QColor(180, 180, 180)); - return gradient; - }(); - return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient; -} - -static QLinearGradient titlebarGradientInactive() -{ - static QLinearGradient darkGradient = [](){ - QLinearGradient gradient; - gradient.setColorAt(1, QColor(42, 42, 42)); - return gradient; - }(); - static QLinearGradient lightGradient = [](){ - QLinearGradient gradient; - gradient.setColorAt(0, QColor(250, 250, 250)); - gradient.setColorAt(1, QColor(225, 225, 225)); - return gradient; - }(); - return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient; -} +bool isDarkMode() { return QGuiApplicationPrivate::platformTheme()->colorScheme() == Qt::ColorScheme::Dark; } #if QT_CONFIG(tabwidget) /* @@ -255,7 +181,7 @@ static void clipTabBarFrame(const QStyleOption *option, const QMacStyle *style, Q_ASSERT(style); Q_ASSERT(ctx); - if (qt_mac_applicationIsInDarkMode()) { + if (isDarkMode()) { QTabWidget *tabWidget = qobject_cast<QTabWidget *>(option->styleObject); Q_ASSERT(tabWidget); @@ -287,7 +213,7 @@ static const QColor titlebarSeparatorLineInactive(131, 131, 131); static const QColor darkModeSeparatorLine(88, 88, 88); // Gradient colors used for the dock widget title bar and -// non-unifed tool bar bacground. +// non-unifed tool bar background. static const QColor lightMainWindowGradientBegin(240, 240, 240); static const QColor lightMainWindowGradientEnd(200, 200, 200); static const QColor darkMainWindowGradientBegin(47, 47, 47); @@ -303,7 +229,6 @@ static const qreal titleBarButtonSpacing = 8; // active: window is active // selected: tab is selected // hovered: tab is hovered -bool isDarkMode() { return qt_mac_applicationIsInDarkMode(); } #if QT_CONFIG(tabbar) static const QColor lightTabBarTabBackgroundActive(190, 190, 190); @@ -353,7 +278,7 @@ static const int closeButtonSize = 14; static const qreal closeButtonCornerRadius = 2.0; #endif // QT_CONFIG(tabbar) -#ifndef QT_NO_ACCESSIBILITY // This ifdef to avoid "unused function" warning. +#if QT_CONFIG(accessibility) // This ifdef to avoid "unused function" warning. QBrush brushForToolButton(bool isOnKeyWindow) { // When a toolbutton in a toolbar is in the 'ON' state, we draw a @@ -364,7 +289,7 @@ QBrush brushForToolButton(bool isOnKeyWindow) return isOnKeyWindow ? QColor(0, 0, 0, 28) : QColor(0, 0, 0, 21); } -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) static const int headerSectionArrowHeight = 6; @@ -389,16 +314,15 @@ static const QMarginsF pushButtonShadowMargins[3] = { { 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. +// These are frame heights as reported by Xcode 9's Interface Builder +// and determined experimentally. static const qreal comboBoxDefaultHeight[3] = { 26, 22, 19 }; static const qreal pushButtonDefaultHeight[3] = { - 32, 28, 16 + 32, 28, 24 }; static const qreal popupButtonDefaultHeight[3] = { @@ -422,16 +346,14 @@ class AppearanceSync { public: AppearanceSync() { -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave - && !qt_mac_applicationIsInDarkMode()) { + && !isDarkMode()) { auto requiredAppearanceName = NSApplication.sharedApplication.effectiveAppearance.name; if (![NSAppearance.currentAppearance.name isEqualToString:requiredAppearanceName]) { previous = NSAppearance.currentAppearance; NSAppearance.currentAppearance = [NSAppearance appearanceNamed:requiredAppearanceName]; } } -#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) } ~AppearanceSync() @@ -469,6 +391,7 @@ static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl) return false; slider.frame = sl->rect.toCGRect(); + slider.minValue = sl->minimum; slider.maxValue = sl->maximum; slider.intValue = sl->sliderPosition; @@ -494,38 +417,11 @@ static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl) slider.numberOfTickMarks = 0; } - return true; -} + // Ensure the values set above are reflected when asking + // the cell for its metrics and to draw itself. + [slider layoutSubtreeIfNeeded]; -static void fixStaleGeometry(NSSlider *slider) -{ - // If it's later fixed in AppKit, this function is not needed. - // On macOS Mojave we suddenly have NSSliderCell with a cached - // (and stale) geometry, thus its -drawKnob, -drawBarInside:flipped:, - // -drawTickMarks fail to render the slider properly. Setting the number - // of tickmarks triggers an update in geometry. - - Q_ASSERT(slider); - - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave) - return; - - NSSliderCell *cell = slider.cell; - const NSRect barRect = [cell barRectFlipped:NO]; - const NSSize sliderSize = slider.frame.size; - CGFloat difference = 0.; - if (slider.vertical) - difference = std::abs(sliderSize.height - barRect.size.height); - else - difference = std::abs(sliderSize.width - barRect.size.width); - - if (difference > 6.) { - // Stale ... - const auto nOfTicks = slider.numberOfTickMarks; - // Non-zero, different from nOfTicks to force update - slider.numberOfTickMarks = nOfTicks + 10; - slider.numberOfTickMarks = nOfTicks; - } + return true; } static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY) @@ -708,7 +604,7 @@ static inline bool isTreeView(const QWidget *widget) static QString qt_mac_removeMnemonics(const QString &original) { - QString returnText(original.size(), 0); + QString returnText(original.size(), QChar(0)); int finalDest = 0; int currPos = 0; int l = original.length(); @@ -872,6 +768,8 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption return ret; } + const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; + if (ct == QStyle::CT_CustomBase && widg) { #if QT_CONFIG(pushbutton) if (qobject_cast<const QPushButton *>(widg)) @@ -1008,7 +906,7 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption #if QT_CONFIG(toolbutton) const QStyleOptionToolButton *bt = qstyleoption_cast<const QStyleOptionToolButton *>(opt); // If this conversion fails then the widget was not what it claimed to be. - if(bt) { + if (bt) { if (!bt->icon.isNull()) { QSize iconSize = bt->iconSize; QSize pmSize = bt->icon.actualSize(QSize(32, 32), QIcon::Normal); @@ -1046,12 +944,14 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption int w = -1; const QStyleOptionSlider *sld = qstyleoption_cast<const QStyleOptionSlider *>(opt); // If this conversion fails then the widget was not what it claimed to be. - if(sld) { + if (sld) { if (sz == QStyleHelper::SizeLarge) { if (sld->orientation == Qt::Horizontal) { w = qt_mac_aqua_get_metric(HSliderHeight); if (sld->tickPosition != QSlider::NoTicks) w += qt_mac_aqua_get_metric(HSliderTickHeight); + else if (isBigSurOrAbove) + w += 3; } else { w = qt_mac_aqua_get_metric(VSliderWidth); if (sld->tickPosition != QSlider::NoTicks) @@ -1115,7 +1015,7 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption #if QT_CONFIG(combobox) case QStyle::CT_LineEdit: if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) { - //should I take into account the font dimentions of the lineedit? -Sam + //should I take into account the font dimensions of the lineedit? -Sam if (sz == QStyleHelper::SizeLarge) ret = QSize(-1, 21); else @@ -1177,6 +1077,8 @@ static QStyleHelper::WidgetSizePolicy qt_aqua_guess_size(const QWidget *widg, QS void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const { + const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; + QPainterPath focusRingPath; focusRingPath.setFillRule(Qt::OddEvenFill); @@ -1188,6 +1090,8 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int case SegmentedControl_Middle: case TextField: { auto innerRect = targetRect; + if (cw.type == Button_SquareButton) + innerRect = cw.adjustedControlFrame(targetRect.adjusted(hMargin, vMargin, -hMargin, -vMargin)); if (cw.type == TextField) innerRect = innerRect.adjusted(hMargin, vMargin, -hMargin, -vMargin).adjusted(0.5, 0.5, -0.5, -0.5); const auto outerRect = innerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth); @@ -1222,15 +1126,49 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int focusRingPath.addEllipse(rbOuterRect); break; } - case Button_PopupButton: case Button_PullDown: - case Button_PushButton: + case Button_PushButton: { + QRectF focusRect; + auto *pb = static_cast<NSButton *>(cocoaControl(cw)); + const QRectF frameRect = cw.adjustedControlFrame(targetRect.adjusted(hMargin, vMargin, -hMargin, -vMargin)); + pb.frame = frameRect.toCGRect(); + focusRect = QRectF::fromCGRect([pb alignmentRectForFrame:pb.frame]); + if (cw.type == QMacStylePrivate::Button_PushButton) { + focusRect -= pushButtonShadowMargins[cw.size]; + if (cw.size == QStyleHelper::SizeMini) + focusRect.adjust(0, 3, 0, -3); + } else if (cw.type == QMacStylePrivate::Button_PullDown) { + focusRect -= pullDownButtonShadowMargins[cw.size]; + //fix focus ring drawn slightly off for pull downs + if (cw.size == QStyleHelper::SizeLarge) + focusRect = focusRect.adjusted(0, 0, 0, -1); + else if (cw.size == QStyleHelper::SizeMini) + focusRect = focusRect.adjusted(0, -1, 0, 0); + } + if (isBigSurOrAbove) + focusRect = focusRect.translated(0, 1); + const qreal innerRadius = cw.type == Button_PushButton ? 3 : 4; + const qreal outerRadius = innerRadius + focusRingWidth; + hOffset = focusRect.left(); + vOffset = focusRect.top(); + const auto innerRect = focusRect.translated(-focusRect.topLeft()); + const auto outerRect = innerRect.adjusted(-hMargin, -vMargin, hMargin, vMargin); + focusRingPath.addRoundedRect(innerRect, innerRadius, innerRadius); + focusRingPath.addRoundedRect(outerRect, outerRadius, outerRadius); + break; + } + case Button_PopupButton: case SegmentedControl_Single: { + QRectF focusRect = targetRect; + if (isBigSurOrAbove) + focusRect.translate(0, -1.5); + else if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSCatalina) + focusRect.adjust(0, 0, 0, -1); const qreal innerRadius = cw.type == Button_PushButton ? 3 : 4; const qreal outerRadius = innerRadius + focusRingWidth; - hOffset = targetRect.left(); - vOffset = targetRect.top(); - const auto innerRect = targetRect.translated(-targetRect.topLeft()); + hOffset = focusRect.left(); + vOffset = focusRect.top(); + const auto innerRect = focusRect.translated(-focusRect.topLeft()); const auto outerRect = innerRect.adjusted(-hMargin, -vMargin, hMargin, vMargin); focusRingPath.addRoundedRect(innerRect, innerRadius, innerRadius); focusRingPath.addRoundedRect(outerRect, outerRadius, outerRadius); @@ -1279,7 +1217,7 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int } auto focusRingColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor); - if (!qt_mac_applicationIsInDarkMode()) { + if (!isDarkMode()) { // This color already has alpha ~ 0.25, this value is too small - the ring is // very pale and nothing like the native one. 0.39 makes it better (not ideal // anyway). The color seems to be correct in dark more without any modification. @@ -1501,7 +1439,7 @@ QStyleHelper::WidgetSizePolicy QMacStylePrivate::aquaSizeConstrain(const QStyleO if (guess_size) ret = qt_aqua_guess_size(widg, large, small, mini); - QSize *sz = 0; + QSize *sz = nullptr; if (ret == QStyleHelper::SizeSmall) sz = &small; else if (ret == QStyleHelper::SizeLarge) @@ -1582,15 +1520,18 @@ QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect) const auto frameSize = defaultFrameSize(); if (type == QMacStylePrivate::Button_SquareButton) { frameRect = rect.adjusted(3, 1, -3, -1) - .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth); + .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth) + .adjusted(-6.5, 0, 6.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); + frameRect.translate(0, 0.5); else if (size == QStyleHelper::SizeMini) - frameRect = frameRect.adjusted(0, 0, -8, 0).translated(4, 4); + frameRect = frameRect.adjusted(0, 0, -8, 0).translated(4, -0.5); + frameRect = frameRect.adjusted(-pushButtonBevelRectOffsets[size], 0, + pushButtonBevelRectOffsets[size], 0); } else { // Center in the style option's rect. frameRect = QRectF(QPointF(0, (rect.height() - frameSize.height()) / 2.0), @@ -1603,6 +1544,9 @@ QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect) 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); + if (type == QMacStylePrivate::Button_PullDown) + frameRect = frameRect.adjusted(-pushButtonBevelRectOffsets[size], 0, + pushButtonBevelRectOffsets[size], 0); } else if (type == QMacStylePrivate::ComboBox) { frameRect = frameRect.adjusted(0, 0, -6, 0).translated(4, 0); } @@ -1657,7 +1601,7 @@ bool QMacStylePrivate::CocoaControl::getCocoaButtonTypeAndBezelStyle(NSButtonTyp *bezelStyle = NSBezelStyleShadowlessSquare; break; case Button_PushButton: - *buttonType = NSButtonTypePushOnPushOff; + *buttonType = NSButtonTypeMomentaryPushIn; *bezelStyle = NSBezelStyleRounded; break; default: @@ -1671,13 +1615,15 @@ QMacStylePrivate::CocoaControlType cocoaControlType(const QStyleOption *opt, con { 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. + // Buttons that don't fit (or don't get filled by) the default bevel + // will be made of square type, unless WA_MacNormalSize is not set. // Threshold used to be at 34, not 32. - const auto maxNonSquareHeight = pushButtonDefaultHeight[QStyleHelper::SizeLarge]; + // Needs to be in sync with similar logic in CE_FocusFrame handling + QStyleHelper::WidgetSizePolicy sizePolicy = QStyleHelper::widgetSizePolicy(w, opt); + if (sizePolicy == QStyleHelper::SizeDefault) + sizePolicy = QStyleHelper::SizeLarge; const bool isSquare = (btn->features & QStyleOptionButton::Flat) - || (btn->rect.height() > maxNonSquareHeight - && !(w && w->testAttribute(Qt::WA_MacNormalSize))); + || (btn->rect.height() != pushButtonDefaultHeight[sizePolicy]); return (isSquare? QMacStylePrivate::Button_SquareButton : hasMenu ? QMacStylePrivate::Button_PullDown : QMacStylePrivate::Button_PushButton); @@ -1796,10 +1742,6 @@ QRectF QMacStylePrivate::comboboxEditBounds(const QRectF &outerBounds, const Coc QMacStylePrivate::QMacStylePrivate() : backingStoreNSView(nil) { - if (auto *ssf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::SmallFont)) - smallSystemFont = *ssf; - if (auto *msf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::MiniFont)) - miniSystemFont = *msf; } QMacStylePrivate::~QMacStylePrivate() @@ -1817,13 +1759,9 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const || widget.size == QStyleHelper::SizeDefault) return nil; - if (widget.type == Box) { - if (__builtin_available(macOS 10.14, *)) { - if (qt_mac_applicationIsInDarkMode()) { - // See render code in drawPrimitive(PE_FrameTabWidget) - widget.type = Box_Dark; - } - } + if (widget.type == Box && isDarkMode()) { + // See render code in drawPrimitive(PE_FrameTabWidget) + widget.type = Box_Dark; } NSView *bv = cocoaControls.value(widget, nil); @@ -2068,7 +2006,6 @@ QMacStyle::QMacStyle() QCoreApplication::sendEvent(o, &event); }); -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) Q_D(QMacStyle); // FIXME: Tie this logic into theme change, or even polish/unpolish if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { @@ -2079,7 +2016,6 @@ QMacStyle::QMacStyle() d->cocoaControls.clear(); }); } -#endif } QMacStyle::~QMacStyle() @@ -2100,6 +2036,14 @@ void QMacStyle::unpolish(QApplication *) void QMacStyle::polish(QWidget* w) { + Q_D(QMacStyle); + if (!d->smallSystemFont && QGuiApplicationPrivate::platformTheme()) { + if (auto *ssf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::SmallFont)) + d->smallSystemFont = *ssf; + else + d->smallSystemFont = QFont(); + } + if (false #if QT_CONFIG(menu) || qobject_cast<QMenu*>(w) @@ -2212,25 +2156,6 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW case PM_FocusFrameHMargin: ret = qt_mac_aqua_get_metric(FocusRectOutset); break; - case PM_DialogButtonsSeparator: - ret = -5; - break; - case PM_DialogButtonsButtonHeight: { - QSize sz; - ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz); - if (sz == QSize(-1, -1)) - ret = 32; - else - ret = sz.height(); - break; } - case PM_DialogButtonsButtonWidth: { - QSize sz; - ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz); - if (sz == QSize(-1, -1)) - ret = 70; - else - ret = sz.width(); - break; } case PM_MenuBarHMargin: ret = 8; @@ -2557,10 +2482,13 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW case PM_ToolBarFrameWidth: ret = 1; break; - case PM_ScrollView_ScrollBarOverlap: - ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay ? - pixelMetric(PM_ScrollBarExtent, opt, widget) : 0; + case PM_ScrollView_ScrollBarOverlap: { + const QStyle *realStyle = widget ? widget->style() : proxy(); + ret = realStyle->styleHint(SH_ScrollBar_Transient, opt, widget) + ? realStyle->pixelMetric(PM_ScrollBarExtent, opt, widget) + : 0; break; + } default: ret = QCommonStyle::pixelMetric(metric, opt, widget); break; @@ -2572,7 +2500,7 @@ QPalette QMacStyle::standardPalette() const { auto platformTheme = QGuiApplicationPrivate::platformTheme(); auto styleNames = platformTheme->themeHint(QPlatformTheme::StyleNames); - if (styleNames.toStringList().contains("macintosh")) + if (styleNames.toStringList().contains("macOS")) return QPalette(); // Inherit everything from theme else return QStyle::standardPalette(); @@ -2611,7 +2539,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w ret = true; break; case SH_Slider_AbsoluteSetButtons: - ret = Qt::LeftButton|Qt::MidButton; + ret = Qt::LeftButton|Qt::MiddleButton; break; case SH_Slider_PageSetButtons: ret = 0; @@ -2740,7 +2668,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w break; case SH_FocusFrame_Mask: { ret = true; - if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) { + if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) { const uchar fillR = 192, fillG = 191, fillB = 190; QImage img; @@ -2828,9 +2756,6 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w case SH_MessageBox_TextInteractionFlags: ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard; break; - case SH_SpellCheckUnderlineStyle: - ret = QTextCharFormat::DashUnderline; - break; case SH_MessageBox_CenterButtons: ret = false; break; @@ -2871,7 +2796,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w case SH_ScrollBar_Transient: if ((qobject_cast<const QScrollBar *>(w) && w->parent() && qobject_cast<QAbstractScrollArea*>(w->parent()->parent())) -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) || (opt && QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ScrollBar)) #endif ) { @@ -2896,6 +2821,9 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w case SH_Table_GridLineColor: ret = int(qt_mac_toQColor(NSColor.gridColor).rgba()); break; + case SH_TabBar_AllowWheelScrolling: + ret = false; + break; default: ret = QCommonStyle::styleHint(sh, opt, w, hret); break; @@ -2956,7 +2884,8 @@ QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOpt size = 64; break; } - return icon.pixmap(qt_getWindow(widget), QSize(size, size)); + qreal dpr = widget ? widget->devicePixelRatio() : qApp->devicePixelRatio(); + return icon.pixmap(QSize(size, size), dpr); } void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, @@ -3026,7 +2955,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } #if QT_CONFIG(tabwidget) QRegion region(tbb->rect); - region -= tbb->tabBarRect; + region -= tbb->tabBarRect.adjusted(3, 0, -3, 0); p->save(); p->setClipRegion(region); QStyleOptionTabWidgetFrame twf; @@ -3084,7 +3013,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai auto adjustedRect = opt->rect; bool needTranslation = false; if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave - && !qt_mac_applicationIsInDarkMode()) { + && !isDarkMode()) { // In Aqua theme we have to use the 'default' NSBox (as opposite // to the 'custom' QDarkNSBox we use in dark theme). Since -drawRect: // does nothing in default NSBox, we call -displayRectIgnoringOpaticty:. @@ -3093,7 +3022,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai // vertically and even worse, if QTabWidget has autoFillBackground // set, this background overpaints NSBox making it to disappear. // We trick our NSBox to render in a larger rectangle, so that - // the actuall result (which is again smaller than requested), + // the actual result (which is again smaller than requested), // more or less is what we really want. We'll have to adjust CTM // and translate accordingly. adjustedRect.adjust(0, 0, 6, 6); @@ -3132,14 +3061,14 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai theStroker.setCapStyle(Qt::FlatCap); theStroker.setDashPattern(QVector<qreal>() << 1 << 2); path = theStroker.createStroke(path); - const auto dark = qt_mac_applicationIsInDarkMode() ? opt->palette.dark().color().darker() + const auto dark = isDarkMode() ? opt->palette.dark().color().darker() : QColor(0, 0, 0, 119); p->fillPath(path, dark); } break; case PE_FrameWindow: if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { - if (w && w->inherits("QMdiSubWindow")) { + if (qobject_cast<const QMdiSubWindow*>(w)) { p->save(); p->setPen(QPen(frame->palette.dark().color(), frame->lineWidth)); p->setBrush(frame->palette.window()); @@ -3287,7 +3216,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai NSButtonCell *triangleCell = static_cast<NSButtonCell *>(d->cocoaCell(cw)); [triangleCell setState:(opt->state & State_Open) ? NSControlStateValueOn : NSControlStateValueOff]; bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus); - [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight]; + [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleEmphasized : NSBackgroundStyleNormal]; d->setupNSGraphicsContext(cg, NO); @@ -3326,7 +3255,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai static_cast<NSTextFieldCell *>(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel; tf.frame = opt->rect.toCGRect(); d->drawNSViewInRect(tf, opt->rect, p, ^(CGContextRef, const CGRect &rect) { - if (!qt_mac_applicationIsInDarkMode()) { + if (!isDarkMode()) { // In 'Dark' mode controls are transparent, so we do not // over-paint the (potentially custom) color in the background. // In 'Light' mode we have to care about the correct @@ -3359,7 +3288,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai case PE_PanelLineEdit: { const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt); - if (qt_mac_applicationIsInDarkMode() || (panel && panel->lineWidth <= 0)) { + if (isDarkMode() || (panel && panel->lineWidth <= 0)) { // QCommonStyle::drawPrimitive(PE_PanelLineEdit) fill the background with // a proper color, defined in opt->palette and then, if lineWidth > 0, it // calls QMacStyle::drawPrimitive(PE_FrameLineEdit). We use NSTextFieldCell @@ -3424,17 +3353,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } break; #endif // QT_CONFIG(tabbar) case PE_PanelStatusBar: { - // Fill the status bar with the titlebar gradient. - QLinearGradient linearGrad; - if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active)) { - linearGrad = titlebarGradientActive(); - } else { - linearGrad = titlebarGradientInactive(); - } - - linearGrad.setStart(0, opt->rect.top()); - linearGrad.setFinalStop(0, opt->rect.bottom()); - p->fillRect(opt->rect, linearGrad); + p->fillRect(opt->rect, opt->palette.window()); // Draw the black separator line at the top of the status bar. if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active)) @@ -3538,33 +3457,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } break; - case CE_HeaderLabel: - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { - p->save(); - QRect textr = header->rect; - if (!header->icon.isNull()) { - QIcon::Mode mode = QIcon::Disabled; - if (opt->state & State_Enabled) - mode = QIcon::Normal; - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); - QPixmap pixmap = header->icon.pixmap(window, QSize(iconExtent, iconExtent), mode); - - QRect pixr = header->rect; - pixr.setY(header->rect.center().y() - (pixmap.height() / pixmap.devicePixelRatio() - 1) / 2); - proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap); - textr.translate(pixmap.width() / pixmap.devicePixelRatio() + 2, 0); - } - - proxy()->drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette, - header->state & State_Enabled, header->text, QPalette::ButtonText); - p->restore(); - } - break; case CE_ToolButtonLabel: if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { QStyleOptionToolButton myTb = *tb; myTb.state &= ~State_AutoRaise; -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { QRect cr = tb->rect; int shiftX = 0; @@ -3598,19 +3495,19 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter : QIcon::Disabled; QIcon::State iconState = (tb->state & State_On) ? QIcon::On : QIcon::Off; - QPixmap pixmap = tb->icon.pixmap(window, - tb->rect.size().boundedTo(tb->iconSize), + QPixmap pixmap = tb->icon.pixmap(tb->rect.size().boundedTo(tb->iconSize), p->device()->devicePixelRatio(), iconMode, iconState); // Draw the text if it's needed. if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) { needText = true; + QSizeF size = pixmap.deviceIndependentSize(); if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { - pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio() + 6); + pr.setHeight(size.height() + 6); cr.adjust(0, pr.bottom(), 0, -3); alignment |= Qt::AlignCenter; } else { - pr.setWidth(pixmap.width() / pixmap.devicePixelRatio() + 8); + pr.setWidth(size.width() + 8); cr.adjust(pr.right(), 0, 0, 0); alignment |= Qt::AlignLeft | Qt::AlignVCenter; } @@ -3654,7 +3551,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter QCommonStyle::drawControl(ce, &myTb, p, w); } } else -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) { QCommonStyle::drawControl(ce, &myTb, p, w); } @@ -3697,14 +3594,27 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter 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 + // This is more convoluted than we initially 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; + + // With the 'momentary push in' type this gives an impression of a + // button in a 'pressed' state (the 'momentary push in' does + // not show its state otherwise): [pb highlight:isPressed]; - pb.state = isHighlighted && !isPressed ? NSControlStateValueOn : NSControlStateValueOff; + + + if (cw.type == QMacStylePrivate::Button_SquareButton) { + pb.state = isHighlighted && !isPressed ? NSControlStateValueOn : NSControlStateValueOff; + } else { + // For default/checked button this will give the required + // button accent color: + pb.keyEquivalent = isHighlighted ? @"\r" : @""; + } + d->drawNSViewInRect(pb, frameRect, p, ^(CGContextRef, const CGRect &r) { [pb.cell drawBezelWithFrame:r inView:pb.superview]; }); @@ -3731,24 +3641,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter arrowOpt.rect = ar; proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, w); } - - - if (btn->state & State_HasFocus) { - // 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); - QRectF focusRect; - if (cw.type == QMacStylePrivate::Button_SquareButton) { - focusRect = frameRect; - } else { - 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); - } } break; case CE_PushButtonLabel: @@ -3764,16 +3656,28 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const bool hasText = !btn.text.isEmpty(); const bool isActive = btn.state & State_Active; const bool isPressed = btn.state & State_Sunken; + const bool isDefault = (btn.features & QStyleOptionButton::DefaultButton && !d->autoDefaultButton) + || d->autoDefaultButton == btn.styleObject; + // cocoaControlType evaluates the type based on the control's geometry, not on the + // label's geometry + const QRect oldRect = btn.rect; + if (w) + btn.rect = w->rect(); const auto ct = cocoaControlType(&btn, w); + btn.rect = oldRect; if (!hasMenu && ct != QMacStylePrivate::Button_SquareButton) { - if (isPressed - || (isActive && isEnabled - && ((btn.state & State_On) - || ((btn.features & QStyleOptionButton::DefaultButton) && !d->autoDefaultButton) - || d->autoDefaultButton == btn.styleObject))) - btn.palette.setColor(QPalette::ButtonText, Qt::white); + if (isPressed || (isActive && isEnabled && ((btn.state & State_On) || isDefault))) + btn.palette.setColor(QPalette::ButtonText, Qt::white); + } + + if (isEnabled && !isDarkMode() && QOperatingSystemVersion::current() > QOperatingSystemVersion::MacOSBigSur) { + if (!isDefault && !(btn.state & State_On)) { + // On macOS 12 it's a gray button, white text color (if set in the + // previous statement) would be almost invisible. + btn.palette.setColor(QPalette::ButtonText, Qt::black); + } } if ((!hasIcon && !hasMenu) || (hasIcon && !hasText)) { @@ -3783,7 +3687,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter QRect textRect = itemTextRect( btn.fontMetrics, freeContentRect, Qt::AlignCenter, isEnabled, btn.text); if (hasMenu) { - textRect.moveTo(w ? 15 : 11, textRect.top()); // Supports Qt Quick Controls + if (ct == QMacStylePrivate::Button_SquareButton) + textRect.moveTo(w ? 8 : 11, textRect.top()); + else + textRect.moveTo(w ? (15 - pushButtonBevelRectOffsets[d->effectiveAquaSizeConstrain(b, w)]) + : 11, textRect.top()); // Supports Qt Quick Controls } // Draw the icon: if (hasIcon) { @@ -3797,13 +3705,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter QIcon::State state = QIcon::Off; if (btn.state & State_On) state = QIcon::On; - QPixmap pixmap = btn.icon.pixmap(window, btn.iconSize, mode, state); - int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); - int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); - contentW += pixmapWidth + QMacStylePrivate::PushButtonContentPadding; + QPixmap pixmap = btn.icon.pixmap(btn.iconSize, p->device()->devicePixelRatio(), mode, state); + QSizeF pixmapSize = pixmap.deviceIndependentSize(); + contentW += pixmapSize.width() + QMacStylePrivate::PushButtonContentPadding; int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2; - int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapHeight) / 2; - QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapWidth, pixmapHeight); + int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapSize.height()) / 2; + QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapSize.width(), pixmapSize.height()); QRect visualIconDestRect = visualRect(btn.direction, freeContentRect, iconDestRect); proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap); int newOffset = iconDestRect.x() + iconDestRect.width() @@ -3875,16 +3782,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // inFrame:withView:], -[drawRect:] or anything in between. Besides, // there's no public API do draw the pressed state, AFAICS. We'll use // a push NSButton instead and clip the CGContext. - // NOTE/TODO: this is not true. On 10.13 NSSegmentedControl works with - // some (black?) magic/magic dances, on 10.14 it simply works (was - // it fixed in AppKit?). But, indeed, we cannot make a tab 'pressed' - // with NSSegmentedControl (only selected), so we stay with buttons - // (mixing buttons and NSSegmentedControl for such a simple thing - // is too much work). const auto cs = d->effectiveAquaSizeConstrain(opt, w); // Extra hacks to get the proper pressed appreance when not selected or selected and inactive const bool needsInactiveHack = (!isActive && isSelected); + const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; const auto ct = !needsInactiveHack && (isSelected || tp == QStyleOptionTab::OnlyOneTab) ? QMacStylePrivate::Button_PushButton : QMacStylePrivate::Button_PopupButton; @@ -3893,6 +3795,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); auto vOffset = isPopupButton ? 1 : 2; + if (isBigSurOrAbove) { + // Make it 1, otherwise, offset is very visible compared + // to selected tab (which is not a popup button). + vOffset = 1; + } + if (tabDirection == QMacStylePrivate::East) vOffset -= 1; const auto outerAdjust = isPopupButton ? 1 : 4; @@ -3909,16 +3817,35 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); else frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); + + if (isSelected && isBigSurOrAbove) { + // 1 pixed of 'roundness' is still visible on the right + // (the left is OK, it's rounded). + frameRect = frameRect.adjusted(0, 0, 1, 0); + } + break; case QStyleOptionTab::Middle: frameRect = frameRect.adjusted(-innerAdjust, 0, innerAdjust, 0); + + if (isSelected && isBigSurOrAbove) { + // 1 pixel of 'roundness' is still visible on both + // sides - left and right. + frameRect = frameRect.adjusted(-1, 0, 1, 0); + } break; + case QStyleOptionTab::Moving: // Moving tab treated like End case QStyleOptionTab::End: // Pressed state hack: tweak adjustments in preparation for flip below if (isSelected || tabDirection == QMacStylePrivate::West) frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); else frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); + + if (isSelected && isBigSurOrAbove) { + // 1 pixel of 'roundness' is still visible on the left. + frameRect = frameRect.adjusted(-1, 0, 0, 0); + } break; case QStyleOptionTab::OnlyOneTab: frameRect = frameRect.adjusted(-outerAdjust, 0, outerAdjust, 0); @@ -3926,10 +3853,22 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } pb.frame = frameRect.toCGRect(); + if (!isPopupButton) { + // Note: these days we use 'momentary push in' for Button_PushButton, + // but tabs are also rendered using NSButton/ButtonPushButton, and + // here we need 'push on/off' to make it work (tab highlight colors). + pb.buttonType = NSButtonTypePushOnPushOff; + } + pb.enabled = isEnabled; [pb highlight:isPressed]; + // Set off state when inactive. See needsInactiveHack for when it's selected - pb.state = (isActive && isSelected && !isPressed) ? NSControlStateValueOn : NSControlStateValueOff; + // On macOS 12, don't set the Off state for selected tabs as it draws a gray backgorund even when highlighted + if (QOperatingSystemVersion::current() > QOperatingSystemVersion::MacOSBigSur) + pb.state = (isActive && isSelected) ? NSControlStateValueOn : NSControlStateValueOff; + else + pb.state = (isActive && isSelected && !isPressed) ? NSControlStateValueOn : NSControlStateValueOff; const auto drawBezelBlock = ^(CGContextRef ctx, const CGRect &r) { CGContextClipToRect(ctx, opt->rect.toCGRect()); @@ -3966,7 +3905,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter NSPopUpArrowPosition oldPosition = NSPopUpArrowAtCenter; NSPopUpButtonCell *pbCell = nil; auto rAdjusted = r; - if (isPopupButton && tp == QStyleOptionTab::OnlyOneTab) { + if (isPopupButton && (tp == QStyleOptionTab::OnlyOneTab || isBigSurOrAbove)) { + // Note: starting from macOS BigSur NSPopupButton has this + // arrow 'button' in a different place and it became + // quite visible 'in between' inactive tabs. pbCell = static_cast<NSPopUpButtonCell *>(pb.cell); oldPosition = pbCell.arrowPosition; pbCell.arrowPosition = NSPopUpNoArrow; @@ -3974,6 +3916,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // NSPopUpButton in this state is smaller. rAdjusted.origin.x -= 3; rAdjusted.size.width += 6; + if (isBigSurOrAbove) { + if (tp == QStyleOptionTab::End) + rAdjusted.origin.x -= 2; + } } } @@ -3985,7 +3931,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (needsInactiveHack) { // First, render tab as non-selected tab on a pixamp - const qreal pixelRatio = p->device()->devicePixelRatioF(); + const qreal pixelRatio = p->device()->devicePixelRatio(); QImage tabPixmap(opt->rect.size() * pixelRatio, QImage::Format_ARGB32_Premultiplied); tabPixmap.setDevicePixelRatio(pixelRatio); tabPixmap.fill(Qt::transparent); @@ -4037,32 +3983,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } p->restore(); } - - // TODO Needs size adjustment to fit the focus ring - if (tabOpt->state & State_HasFocus) { - QMacStylePrivate::CocoaControlType focusRingType; - switch (tp) { - case QStyleOptionTab::Beginning: - focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_Last - : QMacStylePrivate::SegmentedControl_First; - break; - case QStyleOptionTab::Middle: - focusRingType = QMacStylePrivate::SegmentedControl_Middle; - break; - case QStyleOptionTab::End: - focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_First - : QMacStylePrivate::SegmentedControl_Last; - break; - case QStyleOptionTab::OnlyOneTab: - focusRingType = QMacStylePrivate::SegmentedControl_Single; - break; - } - } } break; case CE_TabBarTabLabel: if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { QStyleOptionTab myTab = *tab; + const auto foregroundRole = w ? w->foregroundRole() : QPalette::WindowText; const auto tabDirection = QMacStylePrivate::tabDirection(tab->shape); const bool verticalTabs = tabDirection == QMacStylePrivate::East || tabDirection == QMacStylePrivate::West; @@ -4076,11 +4002,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (!myTab.documentMode && (myTab.state & State_Selected) && (myTab.state & State_Active)) if (const auto *tabBar = qobject_cast<const QTabBar *>(w)) if (!tabBar->tabTextColor(tabBar->currentIndex()).isValid()) - myTab.palette.setColor(QPalette::WindowText, Qt::white); + myTab.palette.setColor(foregroundRole, Qt::white); if (myTab.documentMode && isDarkMode()) { bool active = (myTab.state & State_Selected) && (myTab.state & State_Active); - myTab.palette.setColor(QPalette::WindowText, active ? Qt::white : Qt::gray); + myTab.palette.setColor(foregroundRole, active ? Qt::white : Qt::gray); } int heightOffset = 0; @@ -4109,12 +4035,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } // fill title bar background - QLinearGradient linearGrad; - linearGrad.setStart(QPointF(0, 0)); - linearGrad.setFinalStop(QPointF(0, 2 * effectiveRect.height())); - linearGrad.setColorAt(0, opt->palette.button().color()); - linearGrad.setColorAt(1, opt->palette.dark().color()); - p->fillRect(effectiveRect, linearGrad); + p->fillRect(effectiveRect, opt->palette.window()); // draw horizontal line at bottom p->setPen(opt->palette.dark().color()); @@ -4129,7 +4050,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter titleRect.width()); const auto text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); - proxy()->drawItemText(p, titleRect, Qt::AlignCenter, dwOpt->palette, + proxy()->drawItemText(p, titleRect, Qt::AlignCenter | Qt::TextHideMnemonic, dwOpt->palette, dwOpt->state & State_Enabled, text, QPalette::WindowText); } p->restore(); @@ -4141,20 +4062,32 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const auto *ffw = ff ? ff->widget() : nullptr; const auto ct = [=] { if (ffw) { - if (ffw->inherits("QCheckBox")) + if (qobject_cast<const QCheckBox*>(ffw)) return QMacStylePrivate::Button_CheckBox; - if (ffw->inherits("QRadioButton")) + if (qobject_cast<const QRadioButton*>(ffw)) return QMacStylePrivate::Button_RadioButton; - if (ffw->inherits("QLineEdit") || ffw->inherits("QTextEdit")) + if (qobject_cast<const QLineEdit*>(ffw) || qobject_cast<const QTextEdit*>(ffw)) return QMacStylePrivate::TextField; + if (const auto *pb = qobject_cast<const QPushButton *>(ffw)) { + // keep in sync with cocoaControlType + auto sizePolicy = QStyleHelper::widgetSizePolicy(ffw, opt); + if (sizePolicy == QStyleHelper::SizeDefault) + sizePolicy = QStyleHelper::SizeLarge; + if (pb->isFlat() + || (pb->rect().height() != pushButtonDefaultHeight[sizePolicy])) { + return QMacStylePrivate::Button_SquareButton; + } + if (pb->menu() != nullptr) + return QMacStylePrivate::Button_PullDown; + return QMacStylePrivate::Button_PushButton; + } } return QMacStylePrivate::Box; // Not really, just make it the default } (); - const auto cs = ffw ? (ffw->testAttribute(Qt::WA_MacMiniSize) ? QStyleHelper::SizeMini : - ffw->testAttribute(Qt::WA_MacSmallSize) ? QStyleHelper::SizeSmall : - QStyleHelper::SizeLarge) : - QStyleHelper::SizeLarge; + auto cs = QStyleHelper::widgetSizePolicy(ffw, opt); + if (cs == QStyleHelper::SizeDefault) + cs = QStyleHelper::SizeLarge; const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, w); const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt, w); d->drawFocusRing(p, opt->rect, hMargin, vMargin, QMacStylePrivate::CocoaControl(ct, cs)); @@ -4261,14 +4194,13 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter iconSize = comboBox->iconSize(); } #endif - QPixmap pixmap = mi->icon.pixmap(window, iconSize, mode); - int pixw = pixmap.width() / pixmap.devicePixelRatio(); - int pixh = pixmap.height() / pixmap.devicePixelRatio(); + QPixmap pixmap = mi->icon.pixmap(iconSize, p->device()->devicePixelRatio(), mode); QRect cr(xpos, mi->rect.y(), checkcol, mi->rect.height()); - QRect pmr(0, 0, pixw, pixh); + QSize size = pixmap.deviceIndependentSize().toSize(); + QRect pmr(QPoint(0, 0), size); pmr.moveCenter(cr.center()); p->drawPixmap(pmr.topLeft(), pixmap); - xpos += pixw + 6; + xpos += size.width() + 6; } QString s = mi->text; @@ -4279,7 +4211,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter yPos += 1; const bool isSubMenu = mi->menuItemType == QStyleOptionMenuItem::SubMenu; - const int tabwidth = isSubMenu ? 9 : mi->tabWidth; + const int tabwidth = isSubMenu ? 9 : mi->reservedShortcutWidth; QString rightMarginText; if (isSubMenu) @@ -4297,9 +4229,25 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (!rightMarginText.isEmpty()) { p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font())); int xp = mi->rect.right() - tabwidth - macRightBorder + 2; - if (!isSubMenu) + if (isSubMenu) { + p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags | Qt::AlignRight, rightMarginText); + } else { xp -= macItemHMargin + macItemFrame + 3; // Adjust for shortcut - p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags | Qt::AlignRight, rightMarginText); + // try to render modifier part of shortcut string right aligned, key part left aligned + const QKeySequence seq = QKeySequence::fromString(rightMarginText, QKeySequence::NativeText); + if (seq.count() == 1) { // one-combo sequence, the right most character is the key + // we don't know which key of all menu items is the widest, so use the widest possible + const int maxKeyWidth = p->fontMetrics().maxWidth(); + const QChar key = rightMarginText.at(rightMarginText.length() - 1); + const QString modifiers = rightMarginText.left(rightMarginText.size() - 1); + p->drawText(xp + tabwidth - maxKeyWidth, yPos, maxKeyWidth, mi->rect.height(), text_flags, key); + // don't clip the shortcuts; maxKeyWidth might be more than what we have been allotted by the menu + p->drawText(xp, yPos, tabwidth - maxKeyWidth, mi->rect.height(), + text_flags | Qt::AlignRight | Qt::TextDontClip, modifiers); + } else { // draw the whole thing left-aligned for complex or unparsable cases + p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags, rightMarginText); + } + } } if (!s.isEmpty()) { @@ -4341,8 +4289,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // line-break the string if it doesn't fit the given rect. It's better to draw outside // the rect and possibly overlap something than to have part of the text disappear. [s.toNSString() drawAtPoint:CGPointMake(xpos, yPos) - withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c, - NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}]; + withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c, + NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0], + NSUnderlineStyleAttributeName: [NSNumber numberWithInt: myFont.underline() ? NSUnderlineStyleSingle + : NSUnderlineStyleNone]}]; d->restoreNSGraphicsContext(cgCtx); } else { @@ -4369,7 +4319,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter drawItemPixmap(p, mi->rect, Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip | Qt::TextSingleLine, - mi->icon.pixmap(window, QSize(iconExtent, iconExtent), + mi->icon.pixmap(QSize(iconExtent, iconExtent), p->device()->devicePixelRatio(), (mi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled)); } else { drawItemText(p, mi->rect, @@ -4387,7 +4337,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter case CE_ProgressBarContents: if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { const bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0); - const bool vertical = pb->orientation == Qt::Vertical; + const bool vertical = !(pb->state & QStyle::State_Horizontal); const bool inverted = pb->invertedAppearance; bool reverse = (!vertical && (pb->direction == Qt::RightToLeft)); if (inverted) @@ -4524,7 +4474,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter #ifndef QT_NO_TOOLBAR case CE_ToolBar: { const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt); - const bool isDarkMode = qt_mac_applicationIsInDarkMode(); + const bool darkMode = isDarkMode(); // Unified title and toolbar drawing. In this mode the cocoa platform plugin will // fill the top toolbar area part with a background gradient that "unifies" with @@ -4549,7 +4499,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (isEndOfUnifiedArea) { const int margin = qt_mac_aqua_get_metric(SeparatorSize); const auto separatorRect = QRect(opt->rect.left(), opt->rect.bottom(), opt->rect.width(), margin); - p->fillRect(separatorRect, isDarkMode ? darkModeSeparatorLine : opt->palette.dark().color()); + p->fillRect(separatorRect, darkMode ? darkModeSeparatorLine : opt->palette.dark().color()); } break; } @@ -4564,24 +4514,24 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter else linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0); - QColor mainWindowGradientBegin = isDarkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin; - QColor mainWindowGradientEnd = isDarkMode ? darkMainWindowGradientEnd : lightMainWindowGradientEnd; + QColor mainWindowGradientBegin = darkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin; + QColor mainWindowGradientEnd = darkMode ? darkMainWindowGradientEnd : lightMainWindowGradientEnd; linearGrad.setColorAt(0, mainWindowGradientBegin); linearGrad.setColorAt(1, mainWindowGradientEnd); p->fillRect(opt->rect, linearGrad); p->save(); - QRect toolbarRect = isDarkMode ? opt->rect.adjusted(0, 0, 0, 1) : opt->rect; + QRect toolbarRect = darkMode ? opt->rect.adjusted(0, 0, 0, 1) : opt->rect; if (opt->state & State_Horizontal) { - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); + p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); p->drawLine(toolbarRect.topLeft(), toolbarRect.topRight()); - p->setPen(isDarkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114)); + p->setPen(darkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114)); p->drawLine(toolbarRect.bottomLeft(), toolbarRect.bottomRight()); } else { - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); + p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); p->drawLine(toolbarRect.topLeft(), toolbarRect.bottomLeft()); - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114)); + p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114)); p->drawLine(toolbarRect.topRight(), toolbarRect.bottomRight()); } p->restore(); @@ -4890,14 +4840,24 @@ QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt, = qstyleoption_cast<const QStyleOptionButton *>(opt)) { if ((buttonOpt->features & QStyleOptionButton::Flat)) break; // leave rect alone + if ((buttonOpt->features & QStyleOptionButton::CommandLinkButton)) { + rect = opt->rect; + if (controlSize == QStyleHelper::SizeLarge) + rect.adjust(+6, +4, -6, -8); + else if (controlSize == QStyleHelper::SizeSmall) + rect.adjust(+5, +4, -5, -6); + else + rect.adjust(+1, 0, -1, -2); + break; + } } rect = opt->rect; if (controlSize == QStyleHelper::SizeLarge) { - rect.adjust(+6, +4, -6, -8); + rect.adjust(0, +4, 0, -8); } else if (controlSize == QStyleHelper::SizeSmall) { - rect.adjust(+5, +4, -5, -6); + rect.adjust(0, +4, 0, -6); } else { - rect.adjust(+1, 0, -1, -2); + rect.adjust(0, 0, 0, -2); } break; case SE_RadioButtonLayoutItem: @@ -5136,7 +5096,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { const bool drawTrack = sb->subControls & SC_ScrollBarGroove; - const bool drawKnob = sb->subControls & SC_ScrollBarSlider; + const bool drawKnob = sb->subControls & SC_ScrollBarSlider && sb->minimum != sb->maximum; if (!drawTrack && !drawKnob) break; @@ -5150,7 +5110,8 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const auto cocoaSize = d->effectiveAquaSizeConstrain(opt, widget); const CGFloat maxExpandScale = expandedKnobWidths[cocoaSize] / knobWidths[cocoaSize]; - const bool isTransient = proxy()->styleHint(SH_ScrollBar_Transient, opt, widget); + const QStyle *realStyle = widget ? widget->style() : proxy(); + const bool isTransient = realStyle->styleHint(SH_ScrollBar_Transient, opt, widget); if (!isTransient) d->stopAnimation(opt->styleObject); bool wasActive = false; @@ -5336,9 +5297,18 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex CGPoint pressPoint; if (isPressed) { - const CGRect knobRect = [slider.cell knobRectFlipped:NO]; + const CGRect knobRect = [slider.cell knobRectFlipped:slider.isFlipped]; pressPoint.x = CGRectGetMidX(knobRect); pressPoint.y = CGRectGetMidY(knobRect); + + // The only way to tell a NSSlider/NSSliderCell to render as pressed + // is to start tracking. But this API has some weird behaviors that + // we have to account for. First of all, the pressed state will not + // be visually reflected unless we start tracking twice. And secondly + // if we don't track twice, the state of one render-pass will affect + // the render pass of other sliders, even if we set up the shared + // NSSlider with a new slider value. + [slider.cell startTrackingAt:pressPoint inView:slider]; [slider.cell startTrackingAt:pressPoint inView:slider]; } @@ -5368,7 +5338,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } #if 0 - // FIXME: Sadly, this part doesn't work. It seems to somehow polute the + // FIXME: Sadly, this part doesn't work. It seems to somehow pollute the // NSSlider's internal state and, when we need to use the "else" part, // the slider's frame is not in sync with its cell dimensions. const bool drawAllParts = drawKnob && drawBar && (!hasTicks || drawTicks); @@ -5385,8 +5355,6 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } else #endif { - if (!hasDoubleTicks) - fixStaleGeometry(slider); NSSliderCell *cell = slider.cell; const int numberOfTickMarks = slider.numberOfTickMarks; @@ -5394,7 +5362,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex if (hasDoubleTicks) slider.numberOfTickMarks = 0; - const CGRect barRect = [cell barRectFlipped:hasTicks]; + const CGRect barRect = [cell barRectFlipped:slider.isFlipped]; if (drawBar) { if (!isHorizontal && !sl->upsideDown && (hasDoubleTicks || !hasTicks)) { // The logic behind verticalFlip and upsideDown is the twisted one. @@ -5445,8 +5413,12 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } }); - if (isPressed) + if (isPressed) { + // We stop twice to be on the safe side, even if one seems to be enough. + // See startTracking above for why we do this. + [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO]; [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO]; + } } break; #if QT_CONFIG(spinbox) @@ -5508,7 +5480,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const auto cw = QMacStylePrivate::CocoaControl(ct, cs); auto *cc = static_cast<NSControl *>(d->cocoaControl(cw)); cc.enabled = isEnabled; - QRectF frameRect = cw.adjustedControlFrame(combo->rect);; + QRectF frameRect = cw.adjustedControlFrame(combo->rect); if (cw.type == QMacStylePrivate::Button_PopupButton) { // Non-editable QComboBox auto *pb = static_cast<NSPopUpButton *>(cc); @@ -5578,19 +5550,10 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex QPainterPath outerFramePath = d->windowPanelPath(outerFrameRect); p->fillPath(outerFramePath, opt->palette.dark()); - const auto frameAdjust = 1.0 / p->device()->devicePixelRatioF(); + const auto frameAdjust = 1.0 / p->device()->devicePixelRatio(); const auto innerFrameRect = outerFrameRect.adjusted(frameAdjust, frameAdjust, -frameAdjust, 0); QPainterPath innerFramePath = d->windowPanelPath(innerFrameRect); - if (isActive) { - QLinearGradient g; - g.setStart(QPointF(0, 0)); - g.setFinalStop(QPointF(0, 2 * opt->rect.height())); - g.setColorAt(0, opt->palette.button().color()); - g.setColorAt(1, opt->palette.dark().color()); - p->fillPath(innerFramePath, g); - } else { - p->fillPath(innerFramePath, opt->palette.button()); - } + p->fillPath(innerFramePath, opt->palette.button()); if (titlebar->subControls & (SC_TitleBarCloseButton | SC_TitleBarMaxButton @@ -5624,7 +5587,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const auto iconPos = tr.x() - titlebar->icon.actualSize(iconSize).width() - qRound(titleBarIconTitleSpacing); // Only render the icon if it'll be fully visible if (iconPos < tr.right() - titleBarIconTitleSpacing) - p->drawPixmap(iconPos, tr.y(), titlebar->icon.pixmap(window, iconSize, QIcon::Normal)); + p->drawPixmap(iconPos, tr.y(), titlebar->icon.pixmap(iconSize, QIcon::Normal)); } if (!titlebar->text.isEmpty()) @@ -5653,8 +5616,8 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const bool rtl = groupBox.direction == Qt::RightToLeft; const int alignment = Qt::TextHideMnemonic | (rtl ? Qt::AlignRight : Qt::AlignLeft); const QFont savedFont = p->font(); - if (!flat) - p->setFont(d->smallSystemFont); + if (!flat && d->smallSystemFont) + p->setFont(*d->smallSystemFont); proxy()->drawItemText(p, rect, alignment, groupBox.palette, groupBox.state & State_Enabled, groupBox.text, QPalette::WindowText); if (!flat) p->setFont(savedFont); @@ -5664,7 +5627,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex case CC_ToolButton: if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { if (tb->subControls & SC_ToolButtonMenu) { QStyleOption arrowOpt = *tb; @@ -5690,7 +5653,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } proxy()->drawControl(CE_ToolButtonLabel, opt, p, widget); } else -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) { auto bflags = tb->state; if (tb->subControls & SC_ToolButton) @@ -5781,8 +5744,8 @@ QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc, break; NSSliderCell *cell = slider.cell; - const auto barRect = QRectF::fromCGRect([cell barRectFlipped:hasTicks]); - const auto knobRect = QRectF::fromCGRect([cell knobRectFlipped:NO]); + const auto barRect = QRectF::fromCGRect([cell barRectFlipped:slider.isFlipped]); + const auto knobRect = QRectF::fromCGRect([cell knobRectFlipped:slider.isFlipped]); if (knobRect.contains(pt)) { sc = SC_SliderHandle; } else if (barRect.contains(pt)) { @@ -5886,7 +5849,7 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op NSSliderCell *cell = slider.cell; if (sc == SC_SliderHandle) { - ret = QRectF::fromCGRect([cell knobRectFlipped:NO]).toRect(); + ret = QRectF::fromCGRect([cell knobRectFlipped:slider.isFlipped]).toRect(); if (isHorizontal) { ret.setTop(sl->rect.top()); ret.setBottom(sl->rect.bottom()); @@ -5895,7 +5858,7 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op ret.setRight(sl->rect.right()); } } else if (sc == SC_SliderGroove) { - ret = QRectF::fromCGRect([cell barRectFlipped:hasTicks]).toRect(); + ret = QRectF::fromCGRect([cell barRectFlipped:slider.isFlipped]).toRect(); } else if (hasTicks && sc == SC_SliderTickmarks) { const auto tickMarkRect = QRectF::fromCGRect([cell rectOfTickMarkAtIndex:0]); if (isHorizontal) @@ -5937,7 +5900,7 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op qreal controlsSpacing = lastButtonRect.right() + titleBarButtonSpacing; if (!titlebar->icon.isNull()) { const auto iconSize = proxy()->pixelMetric(PM_SmallIconSize); - const auto actualIconSize = titlebar->icon.actualSize(QSize(iconSize, iconSize)).width();; + const auto actualIconSize = titlebar->icon.actualSize(QSize(iconSize, iconSize)).width(); controlsSpacing += actualIconSize + titleBarIconTitleSpacing; } @@ -6018,7 +5981,9 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op const int margin = flat || hasNoText ? 0 : 9; ret = groupBox->rect.adjusted(margin, 0, -margin, 0); - const QFontMetricsF fm = flat || fontIsSet ? QFontMetricsF(groupBox->fontMetrics) : QFontMetricsF(d->smallSystemFont); + const QFontMetricsF fm = flat || fontIsSet || !d->smallSystemFont + ? QFontMetricsF(groupBox->fontMetrics) + : QFontMetricsF(*d->smallSystemFont); const QSizeF s = fm.size(Qt::AlignHCenter | Qt::AlignVCenter, qt_mac_removeMnemonics(groupBox->text), 0, nullptr); const int tw = qCeil(s.width()); const int h = qCeil(fm.height()); @@ -6172,7 +6137,7 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op case CC_ToolButton: ret = QCommonStyle::subControlRect(cc, opt, sc, widget); if (sc == SC_ToolButtonMenu) { -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) ret.adjust(-toolButtonArrowMargin, 0, 0, 0); #endif @@ -6328,14 +6293,16 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, break; #endif case QStyle::CT_PushButton: { - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) + bool isFlat = false; + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { if (btn->features & QStyleOptionButton::CommandLinkButton) return QCommonStyle::sizeFromContents(ct, opt, sz, widget); + isFlat = btn->features & QStyleOptionButton::Flat; + } // 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. - // 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(). @@ -6346,12 +6313,24 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, // 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.setHeight(pushButtonDefaultHeight[controlSize]); + + if (controlSize == QStyleHelper::SizeLarge) { + if (!isFlat) + sz.rwidth() -= 12; + if (sz.height() > 16) + sz.rheight() += pushButtonDefaultHeight[QStyleHelper::SizeLarge] - 16; + else + sz.setHeight(pushButtonDefaultHeight[QStyleHelper::SizeLarge]); + } else { + if (!isFlat) + sz.rwidth() -= 10; + if (controlSize == QStyleHelper::SizeMini) + sz.setHeight(24); + else + sz.setHeight(pushButtonDefaultHeight[QStyleHelper::SizeSmall]); + } + if (isFlat) + sz.rwidth() -= 13; break; } case QStyle::CT_MenuItem: @@ -6422,9 +6401,9 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { const auto controlSize = d->effectiveAquaSizeConstrain(opt, widget); if (!cb->editable) { - // Same as CT_PushButton, because we have to fit the focus + // See CT_PushButton; we have to fit the focus // ring and a non-editable combo box is a NSPopUpButton. - sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 12; + sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 24; // All values as measured from HIThemeGetButtonBackgroundBounds() if (controlSize != QStyleHelper::SizeMini) sz.rwidth() += 12; // We like 12 over here. @@ -6517,7 +6496,7 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole) const { - if(flags & Qt::TextShowMnemonic) + if (flags & Qt::TextShowMnemonic) flags |= Qt::TextHideMnemonic; QCommonStyle::drawItemText(p, r, flags, pal, enabled, text, textRole); } @@ -6525,8 +6504,8 @@ void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPale bool QMacStyle::event(QEvent *e) { Q_D(QMacStyle); - if(e->type() == QEvent::FocusIn) { - QWidget *f = 0; + if (e->type() == QEvent::FocusIn) { + QWidget *f = nullptr; QWidget *focusWidget = QApplication::focusWidget(); #if QT_CONFIG(graphicsview) if (QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(focusWidget)) { @@ -6548,14 +6527,14 @@ bool QMacStyle::event(QEvent *e) f = focusWidget; } if (f) { - if(!d->focusWidget) + if (!d->focusWidget) d->focusWidget = new QFocusFrame(f); d->focusWidget->setWidget(f); - } else if(d->focusWidget) { + } else if (d->focusWidget) { d->focusWidget->setWidget(0); } - } else if(e->type() == QEvent::FocusOut) { - if(d->focusWidget) + } else if (e->type() == QEvent::FocusOut) { + if (d->focusWidget) d->focusWidget->setWidget(0); } return false; @@ -6569,7 +6548,7 @@ QIcon QMacStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *o return QCommonStyle::standardIcon(standardIcon, opt, widget); case SP_ToolBarHorizontalExtensionButton: case SP_ToolBarVerticalExtensionButton: { - QPixmap pixmap(QLatin1String(":/qt-project.org/styles/macstyle/images/toolbar-ext.png")); + QPixmap pixmap(QLatin1String(":/qt-project.org/styles/macstyle/images/toolbar-ext-macstyle.png")); if (standardIcon == SP_ToolBarVerticalExtensionButton) { QPixmap pix2(pixmap.height(), pixmap.width()); pix2.setDevicePixelRatio(pixmap.devicePixelRatio()); |