diff options
Diffstat (limited to 'src/quicknativestyle/qstyle/mac')
-rw-r--r-- | src/quicknativestyle/qstyle/mac/mac.pri | 12 | ||||
-rw-r--r-- | src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm | 6072 | ||||
-rw-r--r-- | src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p.h | 104 | ||||
-rw-r--r-- | src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p_p.h | 230 |
4 files changed, 0 insertions, 6418 deletions
diff --git a/src/quicknativestyle/qstyle/mac/mac.pri b/src/quicknativestyle/qstyle/mac/mac.pri deleted file mode 100644 index 2f7ee7d4..00000000 --- a/src/quicknativestyle/qstyle/mac/mac.pri +++ /dev/null @@ -1,12 +0,0 @@ -LIBS_PRIVATE += -framework AppKit - -INCLUDEPATH += $$PWD - -HEADERS += \ - $$PWD/qquickmacstyle_mac_p.h \ - $$PWD/qquickmacstyle_mac_p_p.h - -SOURCES += - -OBJECTIVE_SOURCES += \ - $$PWD/qquickmacstyle_mac.mm diff --git a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm b/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm deleted file mode 100644 index 5d061667..00000000 --- a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac.mm +++ /dev/null @@ -1,6072 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* - Note: The qdoc comments for QMacStyle are contained in - .../doc/src/qstyles.qdoc. -*/ - -#include <AppKit/AppKit.h> - -#include "qquickmacstyle_mac_p.h" -#include "qquickmacstyle_mac_p_p.h" -#include "qquickstylehelper_p.h" - -#include <QtQuickTemplates2/private/qquickcontrol_p.h> - -#define QMAC_QAQUASTYLE_SIZE_CONSTRAIN -//#define DEBUG_SIZE_CONSTRAINT - -#include <QtCore/qoperatingsystemversion.h> -#include <QtCore/qvariant.h> -#include <QtCore/qvarlengtharray.h> - -#include <QtGui/qpainterpath.h> -#include <QtGui/qpa/qplatformnativeinterface.h> -#include <QtGui/qpa/qplatformfontdatabase.h> -#include <QtGui/qpa/qplatformtheme.h> - -#include <QtCore/private/qcore_mac_p.h> -#include <QtGui/private/qcoregraphics_p.h> -#include <QtGui/private/qguiapplication_p.h> - -#include <cmath> - -QT_USE_NAMESPACE - -// OBS! Changing QT_MANGLE_NAMESPACE and QT_NAMESPACE_ALIAS_OBJC_CLASS to take -// both QT_NAMESPACE and QQC2_NAMESPACE into account (and not only QT_NAMESPACE, which -// would otherwise be the case). This will make it possible to link in both widgets and -// controls in the same application when building statically. -#undef QT_MANGLE_NAMESPACE -#undef QT_NAMESPACE_ALIAS_OBJC_CLASS - -#define QQC2_MANGLE1(a, b) a##_##b -#define QQC2_MANGLE2(a, b) QQC2_MANGLE1(a, b) - -#if defined(QT_NAMESPACE) - #define QT_MANGLE_NAMESPACE(name) QQC2_MANGLE2(QQC2_MANGLE1(name, QQC2_NAMESPACE), QT_NAMESPACE) - #define QT_NAMESPACE_ALIAS_OBJC_CLASS(name) @compatibility_alias name QT_MANGLE_NAMESPACE(name) -#else - #define QT_MANGLE_NAMESPACE(name) QQC2_MANGLE2(name, QQC2_NAMESPACE) - #define QT_NAMESPACE_ALIAS_OBJC_CLASS(name) @compatibility_alias name QT_MANGLE_NAMESPACE(name) -#endif - -@interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator - -@property (readonly, nonatomic) NSInteger animators; - -- (instancetype)init; - -- (void)startAnimation; -- (void)stopAnimation; - -- (void)drawWithFrame:(CGRect)rect inView:(NSView *)view; - -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(QIndeterminateProgressIndicator); - -@implementation QIndeterminateProgressIndicator - -- (instancetype)init -{ - if ((self = [super init])) { - _animators = 0; - self.indeterminate = YES; - self.usesThreadedAnimation = NO; - self.alphaValue = 0.0; - } - - return self; -} - -- (void)startAnimation -{ - if (_animators == 0) { - self.hidden = NO; - [super startAnimation:self]; - } - ++_animators; -} - -- (void)stopAnimation -{ - --_animators; - if (_animators == 0) { - [super stopAnimation:self]; - self.hidden = YES; - [self removeFromSuperviewWithoutNeedingDisplay]; - } -} - -- (void)drawWithFrame:(CGRect)rect inView:(NSView *)view -{ - // The alphaValue change is not strictly necessary, but feels safer. - self.alphaValue = 1.0; - if (self.superview != view) - [view addSubview:self]; - if (!CGRectEqualToRect(self.frame, rect)) - self.frame = rect; - [self drawRect:rect]; - self.alphaValue = 0.0; -} - -@end - -@interface QT_MANGLE_NAMESPACE(QVerticalSplitView) : NSSplitView -- (BOOL)isVertical; -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(QVerticalSplitView); - -@implementation QVerticalSplitView -- (BOOL)isVertical -{ - return YES; -} -@end - -// See render code in drawPrimitive(PE_FrameTabWidget) -@interface QT_MANGLE_NAMESPACE(QDarkNSBox) : NSBox -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(QDarkNSBox); - -@implementation QDarkNSBox -- (instancetype)init -{ - if ((self = [super init])) { - self.title = @""; - self.titlePosition = NSNoTitle; - self.boxType = NSBoxCustom; - self.cornerRadius = 3; - self.borderColor = [NSColor.controlColor colorWithAlphaComponent:0.1]; - self.fillColor = [NSColor.darkGrayColor colorWithAlphaComponent:0.2]; - } - - return self; -} - -- (void)drawRect:(NSRect)rect -{ - [super drawRect:rect]; -} -@end - -QT_BEGIN_NAMESPACE - -namespace QQC2_NAMESPACE { - -// The following constants are used for adjusting the size -// of push buttons so that they are drawn inside their bounds. -const int QMacStylePrivate::PushButtonLeftOffset = 6; -const int QMacStylePrivate::PushButtonRightOffset = 12; -const int QMacStylePrivate::PushButtonContentPadding = 6; - -QVector<QPointer<QObject> > QMacStylePrivate::scrollBars; - -// Title bar gradient colors for Lion were determined by inspecting PSDs exported -// using CoreUI's CoreThemeDocument; there is no public API to retrieve them - -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; -} - -/* - Since macOS 10.14 AppKit is using transparency more extensively, especially for the - dark theme. Inactive buttons, for example, are semi-transparent. And we use them to - draw tab widget's tab bar. The combination of NSBox (also a part of tab widget) - and these transparent buttons gives us an undesired side-effect: an outline of - NSBox is visible through transparent buttons. To avoid this, we have this hack below: - we clip the area where the line would be visible through the buttons. The area we - want to clip away can be described as an intersection of the option's rect and - the tab widget's tab bar rect. But some adjustments are required, since those rects - are anyway adjusted during the rendering and they are not exactly what you'll see on - the screen. Thus this switch-statement inside. -*/ -static void clipTabBarFrame(const QStyleOption *option, const QMacStyle *style, CGContextRef ctx) -{ - Q_ASSERT(option); - Q_ASSERT(style); - Q_ASSERT(ctx); - - if (qt_mac_applicationIsInDarkMode()) { -// QTabWidget *tabWidget = qobject_cast<QTabWidget *>(option->styleObject); -// Q_ASSERT(tabWidget); - -// QRect tabBarRect = style->subElementRect(QStyle::SE_TabWidgetTabBar, option, tabWidget).adjusted(2, 0, -3, 0); -// switch (tabWidget->tabPosition()) { -// case QTabWidget::South: -// tabBarRect.setY(tabBarRect.y() + tabBarRect.height() / 2); -// break; -// case QTabWidget::North: -// case QTabWidget::West: -// tabBarRect = tabBarRect.adjusted(0, 2, 0, -2); -// break; -// case QTabWidget::East: -// tabBarRect = tabBarRect.adjusted(tabBarRect.width() / 2, 2, tabBarRect.width() / 2, -2); -// } - -// const QRegion clipPath = QRegion(option->rect) - tabBarRect; -// QVarLengthArray<CGRect, 3> cgRects; -// for (const QRect &qtRect : clipPath) -// cgRects.push_back(qtRect.toCGRect()); -// if (cgRects.size()) -// CGContextClipToRects(ctx, &cgRects[0], size_t(cgRects.size())); - } -} - -static const QColor titlebarSeparatorLineActive(111, 111, 111); -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 background. -static const QColor lightMainWindowGradientBegin(240, 240, 240); -static const QColor lightMainWindowGradientEnd(200, 200, 200); -static const QColor darkMainWindowGradientBegin(47, 47, 47); -static const QColor darkMainWindowGradientEnd(47, 47, 47); - -static const int DisclosureOffset = 4; - -static const qreal titleBarIconTitleSpacing = 5; -static const qreal titleBarTitleRightMargin = 12; -static const qreal titleBarButtonSpacing = 8; - -// Tab bar colors -// active: window is active -// selected: tab is selected -// hovered: tab is hovered -bool isDarkMode() { return qt_mac_applicationIsInDarkMode(); } - -static const QColor lightTabBarTabBackgroundActive(190, 190, 190); -static const QColor darkTabBarTabBackgroundActive(38, 38, 38); -static const QColor tabBarTabBackgroundActive() { return isDarkMode() ? darkTabBarTabBackgroundActive : lightTabBarTabBackgroundActive; } - -static const QColor lightTabBarTabBackgroundActiveHovered(178, 178, 178); -static const QColor darkTabBarTabBackgroundActiveHovered(32, 32, 32); -static const QColor tabBarTabBackgroundActiveHovered() { return isDarkMode() ? darkTabBarTabBackgroundActiveHovered : lightTabBarTabBackgroundActiveHovered; } - -static const QColor lightTabBarTabBackgroundActiveSelected(211, 211, 211); -static const QColor darkTabBarTabBackgroundActiveSelected(52, 52, 52); -static const QColor tabBarTabBackgroundActiveSelected() { return isDarkMode() ? darkTabBarTabBackgroundActiveSelected : lightTabBarTabBackgroundActiveSelected; } - -static const QColor lightTabBarTabBackground(227, 227, 227); -static const QColor darkTabBarTabBackground(38, 38, 38); -static const QColor tabBarTabBackground() { return isDarkMode() ? darkTabBarTabBackground : lightTabBarTabBackground; } - -static const QColor lightTabBarTabBackgroundSelected(246, 246, 246); -static const QColor darkTabBarTabBackgroundSelected(52, 52, 52); -static const QColor tabBarTabBackgroundSelected() { return isDarkMode() ? darkTabBarTabBackgroundSelected : lightTabBarTabBackgroundSelected; } - -static const QColor lightTabBarTabLineActive(160, 160, 160); -static const QColor darkTabBarTabLineActive(90, 90, 90); -static const QColor tabBarTabLineActive() { return isDarkMode() ? darkTabBarTabLineActive : lightTabBarTabLineActive; } - -static const QColor lightTabBarTabLineActiveHovered(150, 150, 150); -static const QColor darkTabBarTabLineActiveHovered(90, 90, 90); -static const QColor tabBarTabLineActiveHovered() { return isDarkMode() ? darkTabBarTabLineActiveHovered : lightTabBarTabLineActiveHovered; } - -static const QColor lightTabBarTabLine(210, 210, 210); -static const QColor darkTabBarTabLine(90, 90, 90); -static const QColor tabBarTabLine() { return isDarkMode() ? darkTabBarTabLine : lightTabBarTabLine; } - -static const QColor lightTabBarTabLineSelected(189, 189, 189); -static const QColor darkTabBarTabLineSelected(90, 90, 90); -static const QColor tabBarTabLineSelected() { return isDarkMode() ? darkTabBarTabLineSelected : lightTabBarTabLineSelected; } - -static const QColor tabBarCloseButtonBackgroundHovered(162, 162, 162); -static const QColor tabBarCloseButtonBackgroundPressed(153, 153, 153); -static const QColor tabBarCloseButtonBackgroundSelectedHovered(192, 192, 192); -static const QColor tabBarCloseButtonBackgroundSelectedPressed(181, 181, 181); -static const QColor tabBarCloseButtonCross(100, 100, 100); -static const QColor tabBarCloseButtonCrossSelected(115, 115, 115); - -static const int closeButtonSize = 14; -static const qreal closeButtonCornerRadius = 2.0; - -#ifndef QT_NO_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 - // partially transparent background. The colors must be different - // for 'Aqua' and 'DarkAqua' appearances though. - if (isDarkMode()) - return isOnKeyWindow ? QColor(73, 73, 73, 100) : QColor(56, 56, 56, 100); - - return isOnKeyWindow ? QColor(0, 0, 0, 28) : QColor(0, 0, 0, 21); -} -#endif // QT_NO_ACCESSIBILITY - -static const int headerSectionArrowHeight = 6; -static const int headerSectionSeparatorInset = 2; - -// One for each of QStyleHelper::WidgetSizePolicy -static const QMarginsF comboBoxFocusRingMargins[3] = { - { 0.5, 2, 3.5, 4 }, - { 0.5, 1, 2.5, 4 }, - { 0.5, 1.5, 2.5, 3.5 } -}; - -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 comboBoxDefaultHeight[3] = { - 26, 22, 19 -}; - -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; - -// An application can force 'Aqua' theme while the system theme is one of -// the 'Dark' variants. Since in Qt we sometimes use NSControls and even -// NSCells directly without attaching them to any view hierarchy, we have -// to set NSAppearance.currentAppearance to 'Aqua' manually, to make sure -// the correct rendering path is triggered. Apple recommends us to un-set -// the current appearance back after we finished with drawing. This is what -// AppearanceSync is for. - -class AppearanceSync { -public: - AppearanceSync() - { -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave - && !qt_mac_applicationIsInDarkMode()) { - 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() - { - if (previous) - NSAppearance.currentAppearance = previous; - } - -private: - NSAppearance *previous = nil; - - Q_DISABLE_COPY(AppearanceSync) -}; - -static bool setupScroller(NSScroller *scroller, const QStyleOptionSlider *sb) -{ - const qreal length = sb->maximum - sb->minimum + sb->pageStep; - if (qFuzzyIsNull(length)) - return false; - const qreal proportion = sb->pageStep / length; - const qreal range = qreal(sb->maximum - sb->minimum); - qreal value = range ? qreal(sb->sliderValue - sb->minimum) / range : 0; - if (sb->orientation == Qt::Horizontal && sb->direction == Qt::RightToLeft) - value = 1.0 - value; - - scroller.frame = sb->rect.toCGRect(); - scroller.floatValue = value; - scroller.knobProportion = proportion; - return true; -} - -static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl) -{ - if (sl->minimum >= sl->maximum) - return false; - - slider.frame = sl->rect.toCGRect(); - slider.minValue = sl->minimum; - slider.maxValue = sl->maximum; - slider.intValue = sl->sliderPosition; - slider.enabled = sl->state & QStyle::State_Enabled; - if (sl->tickPosition != QStyleOptionSlider::NoTicks) { - // Set numberOfTickMarks, but TicksBothSides will be treated differently - int interval = sl->tickInterval; - if (interval == 0) { - interval = sl->pageStep; - if (interval == 0) - interval = sl->singleStep; - if (interval == 0) - interval = 1; // return false? - } - slider.numberOfTickMarks = 1 + ((sl->maximum - sl->minimum) / interval); - - const bool ticksAbove = sl->tickPosition == QStyleOptionSlider::TicksAbove; - if (sl->orientation == Qt::Horizontal) - slider.tickMarkPosition = ticksAbove ? NSTickMarkPositionAbove : NSTickMarkPositionBelow; - else - slider.tickMarkPosition = ticksAbove ? NSTickMarkPositionLeading : NSTickMarkPositionTrailing; - } else { - slider.numberOfTickMarks = 0; - } - - // Ensure the values set above are reflected when asking - // the cell for its metrics and to draw itself. - [slider layoutSubtreeIfNeeded]; - - return true; -} - -static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY) -{ - QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); - QPlatformNativeInterface::NativeResourceForIntegrationFunction function = - nativeInterface->nativeResourceFunctionForIntegration("testContentBorderPosition"); - if (!function) - return false; // Not Cocoa platform plugin. - - typedef bool (*TestContentBorderPositionFunction)(QWindow *, int); - return (reinterpret_cast<TestContentBorderPositionFunction>(function))(window, windowY); -} - - -static void drawTabCloseButton(QPainter *p, bool hover, bool selected, bool pressed, bool documentMode) -{ - p->setRenderHints(QPainter::Antialiasing); - QRect rect(0, 0, closeButtonSize, closeButtonSize); - const int width = rect.width(); - const int height = rect.height(); - - if (hover) { - // draw background circle - QColor background; - if (selected) { - if (documentMode) - background = pressed ? tabBarCloseButtonBackgroundSelectedPressed : tabBarCloseButtonBackgroundSelectedHovered; - else - background = QColor(255, 255, 255, pressed ? 150 : 100); // Translucent white - } else { - background = pressed ? tabBarCloseButtonBackgroundPressed : tabBarCloseButtonBackgroundHovered; - if (!documentMode) - background = background.lighter(pressed ? 135 : 140); // Lighter tab background, lighter color - } - - p->setPen(Qt::transparent); - p->setBrush(background); - p->drawRoundedRect(rect, closeButtonCornerRadius, closeButtonCornerRadius); - } - - // draw cross - const int margin = 3; - QPen crossPen; - crossPen.setColor(selected ? (documentMode ? tabBarCloseButtonCrossSelected : Qt::white) : tabBarCloseButtonCross); - crossPen.setWidthF(1.1); - crossPen.setCapStyle(Qt::FlatCap); - p->setPen(crossPen); - p->drawLine(margin, margin, width - margin, height - margin); - p->drawLine(margin, height - margin, width - margin, margin); -} - -QRect rotateTabPainter(QPainter *p, QStyleOptionTab::Shape shape, QRect tabRect) -{ - const auto tabDirection = QMacStylePrivate::tabDirection(shape); - if (QMacStylePrivate::verticalTabs(tabDirection)) { - int newX, newY, newRot; - if (tabDirection == QMacStylePrivate::East) { - newX = tabRect.width(); - newY = tabRect.y(); - newRot = 90; - } else { - newX = 0; - newY = tabRect.y() + tabRect.height(); - newRot = -90; - } - tabRect.setRect(0, 0, tabRect.height(), tabRect.width()); - QTransform transform; - transform.translate(newX, newY); - transform.rotate(newRot); - p->setTransform(transform, true); - } - return tabRect; -} - -void drawTabShape(QPainter *p, const QStyleOptionTab *tabOpt, bool isUnified, int tabOverlap) -{ - QRect rect = tabOpt->rect; - if (QMacStylePrivate::verticalTabs(QMacStylePrivate::tabDirection(tabOpt->shape))) - rect = rect.adjusted(-tabOverlap, 0, 0, 0); - else - rect = rect.adjusted(0, -tabOverlap, 0, 0); - - p->translate(rect.x(), rect.y()); - rect.moveLeft(0); - rect.moveTop(0); - const QRect tabRect = rotateTabPainter(p, tabOpt->shape, rect); - - const int width = tabRect.width(); - const int height = tabRect.height(); - const bool active = (tabOpt->state & QStyle::State_Active); - const bool selected = (tabOpt->state & QStyle::State_Selected); - - const QRect bodyRect(1, 2, width - 2, height - 3); - const QRect topLineRect(1, 0, width - 2, 1); - const QRect bottomLineRect(1, height - 1, width - 2, 1); - if (selected) { - // fill body - if (tabOpt->documentMode && isUnified) { - p->save(); - p->setCompositionMode(QPainter::CompositionMode_Source); - p->fillRect(tabRect, QColor(Qt::transparent)); - p->restore(); - } else if (active) { - p->fillRect(bodyRect, tabBarTabBackgroundActiveSelected()); - // top line - p->fillRect(topLineRect, tabBarTabLineSelected()); - } else { - p->fillRect(bodyRect, tabBarTabBackgroundSelected()); - } - } else { - // when the mouse is over non selected tabs they get a new color - const bool hover = (tabOpt->state & QStyle::State_MouseOver); - if (hover) { - // fill body - p->fillRect(bodyRect, tabBarTabBackgroundActiveHovered()); - // bottom line - p->fillRect(bottomLineRect, isDarkMode() ? QColor(Qt::black) : tabBarTabLineActiveHovered()); - } - } - - // separator lines between tabs - const QRect leftLineRect(0, 1, 1, height - 2); - const QRect rightLineRect(width - 1, 1, 1, height - 2); - const QColor separatorLineColor = active ? tabBarTabLineActive() : tabBarTabLine(); - p->fillRect(leftLineRect, separatorLineColor); - p->fillRect(rightLineRect, separatorLineColor); -} - -void drawTabBase(QPainter *p, const QStyleOptionTabBarBase *tbb) -{ - QRect r = tbb->rect; -// if (QMacStylePrivate::verticalTabs(QMacStylePrivate::tabDirection(tbb->shape))) -// r.setWidth(w->width()); -// else -// r.setHeight(w->height()); - - const QRect tabRect = rotateTabPainter(p, tbb->shape, r); - const int width = tabRect.width(); - const int height = tabRect.height(); - const bool active = (tbb->state & QStyle::State_Active); - - // fill body - const QRect bodyRect(0, 1, width, height - 1); - const QColor bodyColor = active ? tabBarTabBackgroundActive() : tabBarTabBackground(); - p->fillRect(bodyRect, bodyColor); - - // top line - const QRect topLineRect(0, 0, width, 1); - const QColor topLineColor = active ? tabBarTabLineActive() : tabBarTabLine(); - p->fillRect(topLineRect, topLineColor); - - // bottom line - const QRect bottomLineRect(0, height - 1, width, 1); - bool isDocument = false; -// if (const QTabBar *tabBar = qobject_cast<const QTabBar*>(w)) -// isDocument = tabBar->documentMode(); - const QColor bottomLineColor = isDocument && isDarkMode() ? QColor(Qt::black) : active ? tabBarTabLineActive() : tabBarTabLine(); - p->fillRect(bottomLineRect, bottomLineColor); -} - -static QStyleHelper::WidgetSizePolicy getControlSize(const QStyleOption *option) -{ - const auto wsp = QStyleHelper::widgetSizePolicy(option); - if (wsp == QStyleHelper::SizeDefault) - return QStyleHelper::SizeLarge; - - return wsp; -} - -static QString qt_mac_removeMnemonics(const QString &original) -{ - QString returnText(original.size(), QChar()); - int finalDest = 0; - int currPos = 0; - int l = original.length(); - while (l) { - if (original.at(currPos) == QLatin1Char('&')) { - ++currPos; - --l; - if (l == 0) - break; - } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && - original.at(currPos + 1) == QLatin1Char('&') && - original.at(currPos + 2) != QLatin1Char('&') && - original.at(currPos + 3) == QLatin1Char(')')) { - /* remove mnemonics its format is "\s*(&X)" */ - int n = 0; - while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) - ++n; - finalDest -= n; - currPos += 4; - l -= 4; - continue; - } - returnText[finalDest] = original.at(currPos); - ++currPos; - ++finalDest; - --l; - } - returnText.truncate(finalDest); - return returnText; -} - -static bool qt_macWindowMainWindow(const QWindow *window) -{ - if (window->handle()) { - if (NSWindow *nswindow = static_cast<NSWindow*>( - QGuiApplication::platformNativeInterface()-> - nativeResourceForWindow(QByteArrayLiteral("nswindow"), - const_cast<QWindow *>(window)))) { - return [nswindow isMainWindow]; - } - } - return false; -} - -#define LargeSmallMini(option, large, small, mini) \ - (option->state & QStyle::State_Small) ? small : ((option->state & QStyle::State_Mini) ? mini : large) - -/***************************************************************************** - QMacCGStyle globals - *****************************************************************************/ -const int macItemFrame = 2; // menu item frame width -const int macItemHMargin = 3; // menu item hor text margin -const int macRightBorder = 12; // right border on mac - -/***************************************************************************** - QMacCGStyle utility functions - *****************************************************************************/ - -enum QAquaMetric { - // Prepend kThemeMetric to get the HIToolBox constant. - // Represents the values already used in QMacStyle. - CheckBoxHeight = 0, - CheckBoxWidth, - EditTextFrameOutset, - FocusRectOutset, - HSliderHeight, - HSliderTickHeight, - LargeProgressBarThickness, - ListHeaderHeight, - MenuSeparatorHeight, // GetThemeMenuSeparatorHeight - MiniCheckBoxHeight, - MiniCheckBoxWidth, - MiniHSliderHeight, - MiniHSliderTickHeight, - MiniPopupButtonHeight, - MiniPushButtonHeight, - MiniRadioButtonHeight, - MiniRadioButtonWidth, - MiniVSliderTickWidth, - MiniVSliderWidth, - NormalProgressBarThickness, - PopupButtonHeight, - ProgressBarShadowOutset, - PushButtonHeight, - RadioButtonHeight, - RadioButtonWidth, - SeparatorSize, - SmallCheckBoxHeight, - SmallCheckBoxWidth, - SmallHSliderHeight, - SmallHSliderTickHeight, - SmallPopupButtonHeight, - SmallProgressBarShadowOutset, - SmallPushButtonHeight, - SmallRadioButtonHeight, - SmallRadioButtonWidth, - SmallVSliderTickWidth, - SmallVSliderWidth, - VSliderTickWidth, - VSliderWidth -}; - -static const int qt_mac_aqua_metrics[] = { - // Values as of macOS 10.12.4 and Xcode 8.3.1 - 18 /* CheckBoxHeight */, - 18 /* CheckBoxWidth */, - 1 /* EditTextFrameOutset */, - 4 /* FocusRectOutset */, - 22 /* HSliderHeight */, - 5 /* HSliderTickHeight */, - 16 /* LargeProgressBarThickness */, - 17 /* ListHeaderHeight */, - 12 /* MenuSeparatorHeight, aka GetThemeMenuSeparatorHeight */, - 11 /* MiniCheckBoxHeight */, - 11 /* MiniCheckBoxWidth */, - 12 /* MiniHSliderHeight */, - 4 /* MiniHSliderTickHeight */, - 15 /* MiniPopupButtonHeight */, - 16 /* MiniPushButtonHeight */, - 11 /* MiniRadioButtonHeight */, - 11 /* MiniRadioButtonWidth */, - 4 /* MiniVSliderTickWidth */, - 12 /* MiniVSliderWidth */, - 12 /* NormalProgressBarThickness */, - 20 /* PopupButtonHeight */, - 4 /* ProgressBarShadowOutset */, - 20 /* PushButtonHeight */, - 18 /* RadioButtonHeight */, - 18 /* RadioButtonWidth */, - 1 /* SeparatorSize */, - 16 /* SmallCheckBoxHeight */, - 14 /* SmallCheckBoxWidth */, - 15 /* SmallHSliderHeight */, - 4 /* SmallHSliderTickHeight */, - 17 /* SmallPopupButtonHeight */, - 2 /* SmallProgressBarShadowOutset */, - 17 /* SmallPushButtonHeight */, - 15 /* SmallRadioButtonHeight */, - 15 /* SmallRadioButtonWidth */, - 4 /* SmallVSliderTickWidth */, - 15 /* SmallVSliderWidth */, - 5 /* VSliderTickWidth */, - 22 /* VSliderWidth */ -}; - -static inline int qt_mac_aqua_get_metric(QAquaMetric m) -{ - return qt_mac_aqua_metrics[m]; -} - -static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption *opt, - QSize szHint, QStyleHelper::WidgetSizePolicy sz) -{ - QSize ret(-1, -1); - if (sz != QStyleHelper::SizeSmall && sz != QStyleHelper::SizeLarge && sz != QStyleHelper::SizeMini) { - qDebug("Not sure how to return this..."); - return ret; - } -// if ((widget && widget->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) { -// // If you're using a custom font and it's bigger than the default font, -// // then no constraints for you. If you are smaller, we can try to help you out -// QFont font = qt_app_fonts_hash()->value(widget->metaObject()->className(), QFont()); -// if (widget->font().pointSize() > font.pointSize()) -// return ret; -// } - - // TODO: investigate how this function is used. 'ct' can/should be - // filled out correctly in the styleoption already from the styleitem? - Q_ASSERT(ct != QStyle::CT_CustomBase); -// if (ct == QStyle::CT_CustomBase && widget) { -//#if QT_CONFIG(pushbutton) -// if (qobject_cast<const QPushButton *>(widg)) -// ct = QStyle::CT_PushButton; -//#endif -// else if (qobject_cast<const QRadioButton *>(widget)) -// ct = QStyle::CT_RadioButton; -//#if QT_CONFIG(checkbox) -// else if (qobject_cast<const QCheckBox *>(widg)) -// ct = QStyle::CT_CheckBox; -//#endif -//#if QT_CONFIG(combobox) -// else if (qobject_cast<const QComboBox *>(widg)) -// ct = QStyle::CT_ComboBox; -//#endif -//#if QT_CONFIG(toolbutton) -// else if (qobject_cast<const QToolButton *>(widg)) -// ct = QStyle::CT_ToolButton; -//#endif -// else if (qobject_cast<const QSlider *>(widget)) -// ct = QStyle::CT_Slider; -//#if QT_CONFIG(progressbar) -// else if (qobject_cast<const QProgressBar *>(widg)) -// ct = QStyle::CT_ProgressBar; -//#endif -//#if QT_CONFIG(lineedit) -// else if (qobject_cast<const QLineEdit *>(widg)) -// ct = QStyle::CT_LineEdit; -//#endif -//#if QT_CONFIG(itemviews) -// else if (qobject_cast<const QHeaderView *>(widg)) -// ct = QStyle::CT_HeaderSection; -//#endif -//#if QT_CONFIG(menubar) -// else if (qobject_cast<const QMenuBar *>(widg)) -// ct = QStyle::CT_MenuBar; -//#endif -//#if QT_CONFIG(sizegrip) -// else if (qobject_cast<const QSizeGrip *>(widg)) -// ct = QStyle::CT_SizeGrip; -//#endif -// else -// return ret; -// } - - switch (ct) { - case QStyle::CT_PushButton: { - const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt); - if (btn) { - QString buttonText = qt_mac_removeMnemonics(btn->text); - if (buttonText.contains(QLatin1Char('\n'))) - ret = QSize(-1, -1); - else if (sz == QStyleHelper::SizeLarge) - ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight)); - else if (sz == QStyleHelper::SizeSmall) - ret = QSize(-1, qt_mac_aqua_get_metric(SmallPushButtonHeight)); - else if (sz == QStyleHelper::SizeMini) - ret = QSize(-1, qt_mac_aqua_get_metric(MiniPushButtonHeight)); - - if (!btn->icon.isNull()){ - // If the button got an icon, and the icon is larger than the - // button, we can't decide on a default size - ret.setWidth(-1); - if (ret.height() < btn->iconSize.height()) - ret.setHeight(-1); - } - else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){ - // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels. - // 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. - // 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... - if (sz == QStyleHelper::SizeLarge) - ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight)); - else if (sz == QStyleHelper::SizeSmall) - ret = QSize(-1, qt_mac_aqua_get_metric(SmallPushButtonHeight)); - else if (sz == QStyleHelper::SizeMini) - ret = QSize(-1, qt_mac_aqua_get_metric(MiniPushButtonHeight)); - else - // Since there's no default size we return the large size... - ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight)); - } - break; } - case QStyle::CT_SizeGrip: - // Not HIG kosher: mimic what we were doing earlier until we support 4-edge resizing in MDI subwindows - if (sz == QStyleHelper::SizeLarge || sz == QStyleHelper::SizeSmall) { - int s = sz == QStyleHelper::SizeSmall ? 16 : 22; // large: pixel measured from HITheme, small: from my hat - int width = 0; -//#if QT_CONFIG(mdiarea) -// if (widg && qobject_cast<QMdiSubWindow *>(widg->parentWidget())) -// width = s; -//#endif - ret = QSize(width, s); - } - break; - case QStyle::CT_ComboBox: - switch (sz) { - case QStyleHelper::SizeLarge: - ret = QSize(-1, qt_mac_aqua_get_metric(PopupButtonHeight)); - break; - case QStyleHelper::SizeSmall: - ret = QSize(-1, qt_mac_aqua_get_metric(SmallPopupButtonHeight)); - break; - case QStyleHelper::SizeMini: - ret = QSize(-1, qt_mac_aqua_get_metric(MiniPopupButtonHeight)); - break; - default: - break; - } - break; - case QStyle::CT_ToolButton: - if (sz == QStyleHelper::SizeSmall) { - int width = 0, height = 0; - if (szHint == QSize(-1, -1)) { //just 'guess'.. -//#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->icon.isNull()) { -// QSize iconSize = bt->iconSize; -// QSize pmSize = bt->icon.actualSize(QSize(32, 32), QIcon::Normal); -// width = qMax(width, qMax(iconSize.width(), pmSize.width())); -// height = qMax(height, qMax(iconSize.height(), pmSize.height())); -// } -// if (!bt->text.isNull() && bt->toolButtonStyle != Qt::ToolButtonIconOnly) { -// int text_width = bt->fontMetrics.horizontalAdvance(bt->text), -// text_height = bt->fontMetrics.height(); -// if (bt->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { -// width = qMax(width, text_width); -// height += text_height; -// } else { -// width += text_width; -// width = qMax(height, text_height); -// } -// } -// } else -//#endif - { - // Let's return the size hint... - width = szHint.width(); - height = szHint.height(); - } - } else { - width = szHint.width(); - height = szHint.height(); - } - width = qMax(20, width + 5); //border - height = qMax(20, height + 5); //border - ret = QSize(width, height); - } - break; - case QStyle::CT_Slider: { - 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 (sz == QStyleHelper::SizeLarge) { - if (sld->orientation == Qt::Horizontal) { - w = qt_mac_aqua_get_metric(HSliderHeight); - if (sld->tickPosition != QStyleOptionSlider::NoTicks) - w += qt_mac_aqua_get_metric(HSliderTickHeight); - } else { - w = qt_mac_aqua_get_metric(VSliderWidth); - if (sld->tickPosition != QStyleOptionSlider::NoTicks) - w += qt_mac_aqua_get_metric(VSliderTickWidth); - } - } else if (sz == QStyleHelper::SizeSmall) { - if (sld->orientation == Qt::Horizontal) { - w = qt_mac_aqua_get_metric(SmallHSliderHeight); - if (sld->tickPosition != QStyleOptionSlider::NoTicks) - w += qt_mac_aqua_get_metric(SmallHSliderTickHeight); - } else { - w = qt_mac_aqua_get_metric(SmallVSliderWidth); - if (sld->tickPosition != QStyleOptionSlider::NoTicks) - w += qt_mac_aqua_get_metric(SmallVSliderTickWidth); - } - } else if (sz == QStyleHelper::SizeMini) { - if (sld->orientation == Qt::Horizontal) { - w = qt_mac_aqua_get_metric(MiniHSliderHeight); - if (sld->tickPosition != QStyleOptionSlider::NoTicks) - w += qt_mac_aqua_get_metric(MiniHSliderTickHeight); - } else { - w = qt_mac_aqua_get_metric(MiniVSliderWidth); - if (sld->tickPosition != QStyleOptionSlider::NoTicks) - w += qt_mac_aqua_get_metric(MiniVSliderTickWidth); - } - } - } else { - // This is tricky, we were requested to find a size for a slider which is not - // a slider. We don't know if this is vertical or horizontal or if we need to - // have tick marks or not. - // For this case we will return an horizontal slider without tick marks. - w = qt_mac_aqua_get_metric(HSliderHeight); - w += qt_mac_aqua_get_metric(HSliderTickHeight); - } - if (sld->orientation == Qt::Horizontal) - ret.setHeight(w); - else - ret.setWidth(w); - break; - } -//#if QT_CONFIG(progressbar) -// case QStyle::CT_ProgressBar: { -// int finalValue = -1; -// Qt::Orientation orient = Qt::Horizontal; -// if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg)) -// orient = pb->orientation(); - -// if (sz == QStyleHelper::SizeLarge) -// finalValue = qt_mac_aqua_get_metric(LargeProgressBarThickness) -// + qt_mac_aqua_get_metric(ProgressBarShadowOutset); -// else -// finalValue = qt_mac_aqua_get_metric(NormalProgressBarThickness) -// + qt_mac_aqua_get_metric(SmallProgressBarShadowOutset); -// if (orient == Qt::Horizontal) -// ret.setHeight(finalValue); -// else -// ret.setWidth(finalValue); -// break; -// } -//#endif -//#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 -// if (sz == QStyleHelper::SizeLarge) -// ret = QSize(-1, 21); -// else -// ret = QSize(-1, 19); -// } -// break; -//#endif - case QStyle::CT_HeaderSection: -//#if QT_CONFIG(treeview) -// if (isTreeView(widg)) -// ret = QSize(-1, qt_mac_aqua_get_metric(ListHeaderHeight)); -//#endif - break; - case QStyle::CT_MenuBar: - if (sz == QStyleHelper::SizeLarge) { - ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]); - // In the qt_mac_set_native_menubar(false) case, - // we come it here with a zero-height main menu, - // preventing the in-window menu from displaying. - // Use 22 pixels for the height, by observation. - if (ret.height() <= 0) - ret.setHeight(22); - } - break; - default: - break; - } - return ret; -} - -#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT) -static QStyleHelper::WidgetSizePolicy qt_aqua_guess_size( - const QSize &large, - const QSize &small, - const QSize &mini) -{ - if (large == QSize(-1, -1)) { - if (small != QSize(-1, -1)) - return QStyleHelper::SizeSmall; - if (mini != QSize(-1, -1)) - return QStyleHelper::SizeMini; - return QStyleHelper::SizeDefault; - } else if (small == QSize(-1, -1)) { - if (mini != QSize(-1, -1)) - return QStyleHelper::SizeMini; - return QStyleHelper::SizeLarge; - } else if (mini == QSize(-1, -1)) { - return QStyleHelper::SizeLarge; - } - - if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL")) - return QStyleHelper::SizeSmall; - else if (qEnvironmentVariableIsSet("QWIDGET_ALL_MINI")) - return QStyleHelper::SizeMini; - - return QStyleHelper::SizeLarge; -} -#endif - -QPainterPath QMacStylePrivate::windowPanelPath(const QRectF &r) const -{ - static const qreal CornerPointOffset = 5.5; - static const qreal CornerControlOffset = 2.1; - - QPainterPath path; - // Top-left corner - path.moveTo(r.left(), r.top() + CornerPointOffset); - path.cubicTo(r.left(), r.top() + CornerControlOffset, - r.left() + CornerControlOffset, r.top(), - r.left() + CornerPointOffset, r.top()); - // Top-right corner - path.lineTo(r.right() - CornerPointOffset, r.top()); - path.cubicTo(r.right() - CornerControlOffset, r.top(), - r.right(), r.top() + CornerControlOffset, - r.right(), r.top() + CornerPointOffset); - // Bottom-right corner - path.lineTo(r.right(), r.bottom() - CornerPointOffset); - path.cubicTo(r.right(), r.bottom() - CornerControlOffset, - r.right() - CornerControlOffset, r.bottom(), - r.right() - CornerPointOffset, r.bottom()); - // Bottom-right corner - path.lineTo(r.left() + CornerPointOffset, r.bottom()); - path.cubicTo(r.left() + CornerControlOffset, r.bottom(), - r.left(), r.bottom() - CornerControlOffset, - r.left(), r.bottom() - CornerPointOffset); - path.lineTo(r.left(), r.top() + CornerPointOffset); - - return path; -} - -QMacStylePrivate::CocoaControlType QMacStylePrivate::windowButtonCocoaControl(QStyle::SubControl sc) const -{ - struct WindowButtons { - QStyle::SubControl sc; - QMacStylePrivate::CocoaControlType ct; - }; - - static const WindowButtons buttons[] = { - { QStyle::SC_TitleBarCloseButton, QMacStylePrivate::Button_WindowClose }, - { QStyle::SC_TitleBarMinButton, QMacStylePrivate::Button_WindowMiniaturize }, - { QStyle::SC_TitleBarMaxButton, QMacStylePrivate::Button_WindowZoom } - }; - - for (const auto &wb : buttons) - if (wb.sc == sc) - return wb.ct; - - return NoControl; -} - - -void QMacStylePrivate::tabLayout(const QStyleOptionTab *opt, QRect *textRect, QRect *iconRect) const -{ - Q_ASSERT(textRect); - Q_ASSERT(iconRect); - QRect tr = opt->rect; - const bool verticalTabs = opt->shape == QStyleOptionTab::RoundedEast - || opt->shape == QStyleOptionTab::RoundedWest - || opt->shape == QStyleOptionTab::TriangularEast - || opt->shape == QStyleOptionTab::TriangularWest; - if (verticalTabs) - tr.setRect(0, 0, tr.height(), tr.width()); // 0, 0 as we will have a translate transform - - int verticalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftVertical, opt); - int horizontalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, opt); - const int hpadding = 4; - const int vpadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabVSpace, opt) / 2; - if (opt->shape == QStyleOptionTab::RoundedSouth || opt->shape == QStyleOptionTab::TriangularSouth) - verticalShift = -verticalShift; - tr.adjust(hpadding, verticalShift - vpadding, horizontalShift - hpadding, vpadding); - - // left widget - if (!opt->leftButtonSize.isEmpty()) { - const int buttonSize = verticalTabs ? opt->leftButtonSize.height() : opt->leftButtonSize.width(); - tr.setLeft(tr.left() + 4 + buttonSize); - // make text aligned to center - if (opt->rightButtonSize.isEmpty()) - tr.setRight(tr.right() - 4 - buttonSize); - } - // right widget - if (!opt->rightButtonSize.isEmpty()) { - const int buttonSize = verticalTabs ? opt->rightButtonSize.height() : opt->rightButtonSize.width(); - tr.setRight(tr.right() - 4 - buttonSize); - // make text aligned to center - if (opt->leftButtonSize.isEmpty()) - tr.setLeft(tr.left() + 4 + buttonSize); - } - - // icon - if (!opt->icon.isNull()) { - QSize iconSize = opt->iconSize; - if (!iconSize.isValid()) { - int iconExtent = proxyStyle->pixelMetric(QStyle::PM_SmallIconSize); - iconSize = QSize(iconExtent, iconExtent); - } - QSize tabIconSize = opt->icon.actualSize(iconSize, - (opt->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled, - (opt->state & QStyle::State_Selected) ? QIcon::On : QIcon::Off); - // High-dpi icons do not need adjustment; make sure tabIconSize is not larger than iconSize - tabIconSize = QSize(qMin(tabIconSize.width(), iconSize.width()), qMin(tabIconSize.height(), iconSize.height())); - - const int stylePadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabHSpace, opt) / 2 - hpadding; - - if (opt->documentMode) { - // documents show the icon as part of the the text - const int textWidth = - opt->fontMetrics.boundingRect(tr, Qt::AlignCenter | Qt::TextShowMnemonic, opt->text).width(); - *iconRect = QRect(tr.center().x() - textWidth / 2 - stylePadding - tabIconSize.width(), - tr.center().y() - tabIconSize.height() / 2, - tabIconSize.width(), tabIconSize.height()); - } else { - *iconRect = QRect(tr.left() + stylePadding, tr.center().y() - tabIconSize.height() / 2, - tabIconSize.width(), tabIconSize.height()); - } - if (!verticalTabs) - *iconRect = proxyStyle->visualRect(opt->direction, opt->rect, *iconRect); - - tr.setLeft(tr.left() + stylePadding + tabIconSize.width() + 4); - tr.setRight(tr.right() - stylePadding - tabIconSize.width() - 4); - } - - if (!verticalTabs) - tr = proxyStyle->visualRect(opt->direction, opt->rect, tr); - - *textRect = tr; -} - -QMacStylePrivate::Direction QMacStylePrivate::tabDirection(QStyleOptionTab::Shape shape) -{ - switch (shape) { - case QStyleOptionTab::RoundedSouth: - case QStyleOptionTab::TriangularSouth: - return South; - case QStyleOptionTab::RoundedNorth: - case QStyleOptionTab::TriangularNorth: - return North; - case QStyleOptionTab::RoundedWest: - case QStyleOptionTab::TriangularWest: - return West; - case QStyleOptionTab::RoundedEast: - case QStyleOptionTab::TriangularEast: - return East; - } -} - -bool QMacStylePrivate::verticalTabs(QMacStylePrivate::Direction direction) -{ - return (direction == QMacStylePrivate::East - || direction == QMacStylePrivate::West); -} - -QStyleHelper::WidgetSizePolicy QMacStylePrivate::effectiveAquaSizeConstrain(const QStyleOption *option, - QStyle::ContentsType ct, - QSize szHint, QSize *insz) const -{ - QStyleHelper::WidgetSizePolicy sz = aquaSizeConstrain(option, ct, szHint, insz); - if (sz == QStyleHelper::SizeDefault) - return QStyleHelper::SizeLarge; - return sz; -} - -QStyleHelper::WidgetSizePolicy QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, - QStyle::ContentsType ct, QSize szHint, QSize *insz) const -{ - if (!option) - return QStyleHelper::SizeLarge; - - if (option->state & QStyle::State_Small) - return QStyleHelper::SizeSmall; - if (option->state & QStyle::State_Mini) - return QStyleHelper::SizeMini; - - return QStyleHelper::SizeLarge; - -} - -uint qHash(const QMacStylePrivate::CocoaControl &cw, uint seed = 0) -{ - return ((cw.type << 2) | cw.size) ^ seed; -} - -QMacStylePrivate::CocoaControl::CocoaControl() - : type(NoControl), size(QStyleHelper::SizeDefault) -{ -} - -QMacStylePrivate::CocoaControl::CocoaControl(CocoaControlType t, QStyleHelper::WidgetSizePolicy s) - : type(t), size(s) -{ -} - -bool QMacStylePrivate::CocoaControl::operator==(const CocoaControl &other) const -{ - return other.type == type && other.size == size; -} - -QSizeF QMacStylePrivate::CocoaControl::defaultFrameSize() const -{ - // 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 (type == Button_PushButton) - return QSizeF(-1, pushButtonDefaultHeight[size]); - - if (type == Button_PopupButton - || type == Button_PullDown) - return QSizeF(-1, popupButtonDefaultHeight[size]); - - if (type == ComboBox) - return QSizeF(-1, comboBoxDefaultHeight[size]); - - return QSizeF(); -} - -QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect) const -{ - QRectF frameRect; - const auto frameSize = defaultFrameSize(); - if (type == QMacStylePrivate::Button_SquareButton) { - frameRect = rect.adjusted(3, 1, -3, -1) - .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth); - } 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); - } else { - // Center in the style option's rect. - frameRect = QRectF(QPointF(0, (rect.height() - frameSize.height()) / 2.0), - QSizeF(rect.width(), frameSize.height())); - frameRect = frameRect.translated(rect.topLeft()); - if (type == QMacStylePrivate::Button_PullDown || type == QMacStylePrivate::Button_PopupButton) { - if (size == QStyleHelper::SizeLarge) - frameRect = frameRect.adjusted(0, 0, -6, 0).translated(3, 0); - 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::ComboBox) { - frameRect = frameRect.adjusted(0, 0, -6, 0).translated(4, 0); - } - } - - 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, 0, 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) -{ - 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); - } - - if (const auto *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { - if (combo->editable) - return QMacStylePrivate::ComboBox; - // TODO Me may support square, non-editable combo boxes, but not more than that - return QMacStylePrivate::Button_PopupButton; - } - - return QMacStylePrivate::NoControl; -} - -/** - Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain - the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds. -*/ -CGRect QMacStylePrivate::comboboxInnerBounds(const CGRect &outerBounds, const CocoaControl &cocoaWidget) -{ - CGRect innerBounds = outerBounds; - // Carbon draw parts of the view outside the rect. - // So make the rect a bit smaller to compensate - // (I wish HIThemeGetButtonBackgroundBounds worked) - if (cocoaWidget.type == Button_PopupButton) { - switch (cocoaWidget.size) { - case QStyleHelper::SizeSmall: - innerBounds.origin.x += 3; - innerBounds.origin.y += 3; - innerBounds.size.width -= 6; - innerBounds.size.height -= 7; - break; - case QStyleHelper::SizeMini: - innerBounds.origin.x += 2; - innerBounds.origin.y += 2; - innerBounds.size.width -= 5; - innerBounds.size.height -= 6; - break; - case QStyleHelper::SizeLarge: - case QStyleHelper::SizeDefault: - innerBounds.origin.x += 2; - innerBounds.origin.y += 2; - innerBounds.size.width -= 5; - innerBounds.size.height -= 6; - } - } else if (cocoaWidget.type == ComboBox) { - switch (cocoaWidget.size) { - case QStyleHelper::SizeSmall: - innerBounds.origin.x += 3; - innerBounds.origin.y += 3; - innerBounds.size.width -= 7; - innerBounds.size.height -= 8; - break; - case QStyleHelper::SizeMini: - innerBounds.origin.x += 3; - innerBounds.origin.y += 3; - innerBounds.size.width -= 4; - innerBounds.size.height -= 8; - break; - case QStyleHelper::SizeLarge: - case QStyleHelper::SizeDefault: - innerBounds.origin.x += 3; - innerBounds.origin.y += 2; - innerBounds.size.width -= 6; - innerBounds.size.height -= 8; - } - } - - return innerBounds; -} - -/** - Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind - of combobox we choose to draw. This function calculates and returns this size. -*/ -QRectF QMacStylePrivate::comboboxEditBounds(const QRectF &outerBounds, const CocoaControl &cw) -{ - QRectF ret = outerBounds; - if (cw.type == ComboBox) { - switch (cw.size) { - case QStyleHelper::SizeLarge: - ret = ret.adjusted(0, 0, -25, 0).translated(2, 4.5); - ret.setHeight(16); - break; - case QStyleHelper::SizeSmall: - ret = ret.adjusted(0, 0, -22, 0).translated(2, 3); - ret.setHeight(14); - break; - case QStyleHelper::SizeMini: - ret = ret.adjusted(0, 0, -19, 0).translated(2, 2.5); - ret.setHeight(10.5); - break; - default: - break; - } - } else if (cw.type == Button_PopupButton) { - switch (cw.size) { - case QStyleHelper::SizeLarge: - ret.adjust(10, 1, -23, -4); - break; - case QStyleHelper::SizeSmall: - ret.adjust(10, 4, -20, -3); - break; - case QStyleHelper::SizeMini: - ret.adjust(9, 0, -19, 0); - ret.setHeight(13); - break; - default: - break; - } - } - return ret; -} - -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() -{ - QMacAutoReleasePool pool; - for (NSView *b : cocoaControls) - [b release]; - for (NSCell *cell : cocoaCells) - [cell release]; -} - -NSView *QMacStylePrivate::cocoaControl(CocoaControl cocoaControl) const -{ - if (cocoaControl.type == QMacStylePrivate::NoControl - || cocoaControl.size == QStyleHelper::SizeDefault) - return nil; - - if (cocoaControl.type == Box) { - if (__builtin_available(macOS 10.14, *)) { - if (qt_mac_applicationIsInDarkMode()) { - // See render code in drawPrimitive(PE_FrameTabWidget) - cocoaControl.type = Box_Dark; - } - } - } - - NSView *bv = cocoaControls.value(cocoaControl, nil); - if (!bv) { - switch (cocoaControl.type) { - case Box: { - NSBox *box = [[NSBox alloc] init]; - bv = box; - box.title = @""; - box.titlePosition = NSNoTitle; - break; - } - case Box_Dark: - bv = [[QDarkNSBox alloc] init]; - break; - case Button_CheckBox: - case Button_Disclosure: - 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 (cocoaControl.type == Button_PullDown) - bc.pullsDown = YES; - bv = bc; - break; - } - case Button_WindowClose: - case Button_WindowMiniaturize: - case Button_WindowZoom: { - const NSWindowButton button = [=] { - switch (cocoaControl.type) { - case Button_WindowClose: - return NSWindowCloseButton; - case Button_WindowMiniaturize: - return NSWindowMiniaturizeButton; - case Button_WindowZoom: - return NSWindowZoomButton; - default: - break; - } - Q_UNREACHABLE(); - } (); - const auto styleMask = NSWindowStyleMaskTitled - | NSWindowStyleMaskClosable - | NSWindowStyleMaskMiniaturizable - | NSWindowStyleMaskResizable; - bv = [NSWindow standardWindowButton:button forStyleMask:styleMask]; - [bv retain]; - break; - } - case ComboBox: - bv = [[NSComboBox alloc] init]; - break; - case ProgressIndicator_Determinate: - bv = [[NSProgressIndicator alloc] init]; - break; - case ProgressIndicator_Indeterminate: - bv = [[QIndeterminateProgressIndicator alloc] init]; - break; - case Scroller_Horizontal: - bv = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)]; - break; - case Scroller_Vertical: - // Cocoa sets the orientation from the view's frame - // at construction time, and it cannot be changed later. - bv = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)]; - break; - case Slider_Horizontal: - bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)]; - break; - case Slider_Vertical: - // Cocoa sets the orientation from the view's frame - // at construction time, and it cannot be changed later. - bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)]; - break; - case SplitView_Horizontal: - bv = [[NSSplitView alloc] init]; - break; - case SplitView_Vertical: - bv = [[QVerticalSplitView alloc] init]; - break; - case TextField: - bv = [[NSTextField alloc] init]; - break; - default: - break; - } - - if ([bv isKindOfClass:[NSControl class]]) { - auto *ctrl = static_cast<NSControl *>(bv); - switch (cocoaControl.size) { - case QStyleHelper::SizeSmall: - ctrl.controlSize = NSControlSizeSmall; - break; - case QStyleHelper::SizeMini: - ctrl.controlSize = NSControlSizeMini; - break; - default: - break; - } - } else if (cocoaControl.type == ProgressIndicator_Determinate || - cocoaControl.type == ProgressIndicator_Indeterminate) { - auto *pi = static_cast<NSProgressIndicator *>(bv); - pi.indeterminate = (cocoaControl.type == ProgressIndicator_Indeterminate); - switch (cocoaControl.size) { - case QStyleHelper::SizeSmall: - pi.controlSize = NSControlSizeSmall; - break; - case QStyleHelper::SizeMini: - pi.controlSize = NSControlSizeMini; - break; - default: - break; - } - } - - cocoaControls.insert(cocoaControl, bv); - } - - NSButtonType buttonType; - NSBezelStyle bezelStyle; - if (cocoaControl.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; - if (cocoaControl.type == Button_CheckBox) - button.allowsMixedState = YES; - } - - return bv; -} - -NSCell *QMacStylePrivate::cocoaCell(CocoaControl cocoaControl) const -{ - NSCell *cell = cocoaCells[cocoaControl]; - if (!cell) { - switch (cocoaControl.type) { - case Stepper: - cell = [[NSStepperCell alloc] init]; - break; - case Button_Disclosure: { - NSButtonCell *bc = [[NSButtonCell alloc] init]; - bc.buttonType = NSOnOffButton; - bc.bezelStyle = NSDisclosureBezelStyle; - cell = bc; - break; - } - default: - break; - } - - switch (cocoaControl.size) { - case QStyleHelper::SizeSmall: - cell.controlSize = NSControlSizeSmall; - break; - case QStyleHelper::SizeMini: - cell.controlSize = NSControlSizeMini; - break; - default: - break; - } - - cocoaCells.insert(cocoaControl, cell); - } - - return cell; -} - -void QMacStylePrivate::drawNSViewInRect(NSView *view, const QRectF &rect, QPainter *p, DrawRectBlock drawRectBlock) const -{ - QMacAutoReleasePool pool; - QMacCGContext ctx(p); - setupNSGraphicsContext(ctx, YES); - - // FIXME: The rect that we get in is relative to the widget that we're drawing - // style on behalf of, and doesn't take into account the offset of that widget - // to the widget that owns the backingstore, which we are placing the native - // view into below. This means most of the views are placed in the upper left - // corner of backingStoreNSView, which does not map to where the actual widget - // is, and which may cause problems such as triggering a setNeedsDisplay of the - // backingStoreNSView for the wrong rect. We work around this by making the view - // layer-backed, which prevents triggering display of the backingStoreNSView, but - // but there may be other issues lurking here due to the wrong position. QTBUG-68023 - view.wantsLayer = YES; - - // FIXME: We are also setting the frame of the incoming view a lot at the call - // sites of this function, making it unclear who's actually responsible for - // maintaining the size and position of the view. In theory the call sites - // should ensure the _size_ of the view is correct, and then let this code - // take care of _positioning_ the view at the right place inside backingStoreNSView. - // For now we pass on the rect as is, to prevent any regressions until this - // can be investigated properly. - view.frame = rect.toCGRect(); - - [backingStoreNSView addSubview:view]; - - // FIXME: Based on the code below, this method isn't drawing an NSView into - // a rect, it's drawing _part of the NSView_, defined by the incoming clip - // or dirty rect, into the current graphics context. We're doing some manual - // translations at the call sites that would indicate that this relationship - // is a bit fuzzy. - const CGRect dirtyRect = rect.toCGRect(); - - if (drawRectBlock) - drawRectBlock(ctx, dirtyRect); - else - [view drawRect:dirtyRect]; - - [view removeFromSuperviewWithoutNeedingDisplay]; - - restoreNSGraphicsContext(ctx); -} - -void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const -{ - backingStoreNSView = window ? (NSView *)window->winId() : nil; -} - -QMacStyle::QMacStyle() - : QCommonStyle(*new QMacStylePrivate) -{ - QMacAutoReleasePool pool; - - static QMacNotificationObserver scrollbarStyleObserver(nil, - NSPreferredScrollerStyleDidChangeNotification, []() { - // Purge destroyed scroll bars - QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>()); - - QEvent event(QEvent::StyleChange); - for (const auto &o : QMacStylePrivate::scrollBars) - 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) { - d->appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] { - Q_D(QMacStyle); - for (NSView *b : d->cocoaControls) - [b release]; - d->cocoaControls.clear(); - }); - } -#endif -} - -QMacStyle::~QMacStyle() -{ -} - -int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt) const -{ - Q_D(const QMacStyle); - const int controlSize = getControlSize(opt); - int ret = 0; - - switch (metric) { - case PM_TabCloseIndicatorWidth: - case PM_TabCloseIndicatorHeight: - ret = closeButtonSize; - break; - case PM_ToolBarIconSize: - ret = proxy()->pixelMetric(PM_LargeIconSize); - break; - case PM_FocusFrameVMargin: - 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, 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, QStyle::CT_PushButton, QSize(-1, -1), &sz); - if (sz == QSize(-1, -1)) - ret = 70; - else - ret = sz.width(); - break; } - - case PM_MenuBarHMargin: - ret = 8; - break; - - case PM_MenuBarVMargin: - ret = 0; - break; - - case PM_MenuBarPanelWidth: - ret = 0; - break; - - case PM_MenuButtonIndicator: - ret = toolButtonArrowSize; - break; - - case QStyle::PM_MenuDesktopFrameWidth: - ret = 5; - break; - - case PM_CheckBoxLabelSpacing: - case PM_RadioButtonLabelSpacing: - ret = [=] { - if (opt) { - if (opt->state & State_Mini) - return 4; - if (opt->state & State_Small) - return 3; - } - return 2; - } (); - break; - case PM_MenuScrollerHeight: - ret = 15; // I hate having magic numbers in here... - break; - case PM_DefaultFrameWidth: -//#if QT_CONFIG(mainwindow) -// if (widget && (widget->isWindow() || !widget->parentWidget() -// || (qobject_cast<const QMainWindow*>(widget->parentWidget()) -// && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget)) -// && qobject_cast<const QAbstractScrollArea *>(widget)) -// ret = 0; -// else -//#endif - // The combo box popup has no frame. - if (qstyleoption_cast<const QStyleOptionComboBox *>(opt) != 0) - ret = 0; - else - ret = 1; - break; - case PM_MaximumDragDistance: - ret = -1; - break; - case PM_ScrollBarSliderMin: - ret = 24; - break; - case PM_SpinBoxFrameWidth: - ret = qt_mac_aqua_get_metric(EditTextFrameOutset); - break; - case PM_ButtonShiftHorizontal: - case PM_ButtonShiftVertical: - ret = 0; - break; - case PM_SliderLength: - ret = 17; - break; - // Returns the number of pixels to use for the business part of the - // slider (i.e., the non-tickmark portion). The remaining space is shared - // equally between the tickmark regions. - case PM_SliderControlThickness: - if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width(); - int ticks = sl->tickPosition; - int n = 0; - if (ticks & QStyleOptionSlider::TicksAbove) - ++n; - if (ticks & QStyleOptionSlider::TicksBelow) - ++n; - if (!n) { - ret = space; - break; - } - - int thick = 6; // Magic constant to get 5 + 16 + 5 - if (ticks != QStyleOptionSlider::TicksBothSides && ticks != QStyleOptionSlider::NoTicks) - thick += proxy()->pixelMetric(PM_SliderLength, sl) / 4; - - space -= thick; - if (space > 0) - thick += (space * 2) / (n + 2); - ret = thick; - } else { - ret = 0; - } - break; - case PM_SmallIconSize: - ret = int(QStyleHelper::dpiScaled(16., opt)); - break; - - case PM_LargeIconSize: - ret = int(QStyleHelper::dpiScaled(32., opt)); - break; - - case PM_IconViewIconSize: - ret = proxy()->pixelMetric(PM_LargeIconSize, opt); - break; - - case PM_ButtonDefaultIndicator: - ret = 0; - break; - case PM_TitleBarHeight: { - NSUInteger style = NSWindowStyleMaskTitled; -// if (widget && ((widget->windowFlags() & Qt::Tool) == Qt::Tool)) -// style |= NSWindowStyleMaskUtilityWindow; - ret = int([NSWindow frameRectForContentRect:NSZeroRect - styleMask:style].size.height); - break; } - case QStyle::PM_TabBarTabHSpace: - switch (d->aquaSizeConstrain(opt)) { - case QStyleHelper::SizeLarge: - ret = QCommonStyle::pixelMetric(metric, opt); - break; - case QStyleHelper::SizeSmall: - ret = 20; - break; - case QStyleHelper::SizeMini: - ret = 16; - break; - case QStyleHelper::SizeDefault: - const QStyleOptionTab *tb = qstyleoption_cast<const QStyleOptionTab *>(opt); - if (tb && tb->documentMode) - ret = 30; - else - ret = QCommonStyle::pixelMetric(metric, opt); - break; - } - break; - case PM_TabBarTabVSpace: - ret = 4; - break; - case PM_TabBarTabShiftHorizontal: - case PM_TabBarTabShiftVertical: - ret = 0; - break; - case PM_TabBarBaseHeight: - ret = 0; - break; - case PM_TabBarTabOverlap: - ret = 1; - break; - case PM_TabBarBaseOverlap: - switch (d->aquaSizeConstrain(opt)) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - ret = 11; - break; - case QStyleHelper::SizeSmall: - ret = 8; - break; - case QStyleHelper::SizeMini: - ret = 7; - break; - } - break; - case PM_ScrollBarExtent: { - const QStyleHelper::WidgetSizePolicy size = d->effectiveAquaSizeConstrain(opt); - ret = static_cast<int>([NSScroller - scrollerWidthForControlSize:static_cast<NSControlSize>(size) - scrollerStyle:[NSScroller preferredScrollerStyle]]); - break; } - case PM_IndicatorHeight: { - switch (d->aquaSizeConstrain(opt)) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - ret = qt_mac_aqua_get_metric(CheckBoxHeight); - break; - case QStyleHelper::SizeMini: - ret = qt_mac_aqua_get_metric(MiniCheckBoxHeight); - break; - case QStyleHelper::SizeSmall: - ret = qt_mac_aqua_get_metric(SmallCheckBoxHeight); - break; - } - break; } - case PM_IndicatorWidth: { - switch (d->aquaSizeConstrain(opt)) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - ret = qt_mac_aqua_get_metric(CheckBoxWidth); - break; - case QStyleHelper::SizeMini: - ret = qt_mac_aqua_get_metric(MiniCheckBoxWidth); - break; - case QStyleHelper::SizeSmall: - ret = qt_mac_aqua_get_metric(SmallCheckBoxWidth); - break; - } - ++ret; - break; } - case PM_ExclusiveIndicatorHeight: { - switch (d->aquaSizeConstrain(opt)) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - ret = qt_mac_aqua_get_metric(RadioButtonHeight); - break; - case QStyleHelper::SizeMini: - ret = qt_mac_aqua_get_metric(MiniRadioButtonHeight); - break; - case QStyleHelper::SizeSmall: - ret = qt_mac_aqua_get_metric(SmallRadioButtonHeight); - break; - } - break; } - case PM_ExclusiveIndicatorWidth: { - switch (d->aquaSizeConstrain(opt)) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - ret = qt_mac_aqua_get_metric(RadioButtonWidth); - break; - case QStyleHelper::SizeMini: - ret = qt_mac_aqua_get_metric(MiniRadioButtonWidth); - break; - case QStyleHelper::SizeSmall: - ret = qt_mac_aqua_get_metric(SmallRadioButtonWidth); - break; - } - ++ret; - break; } - case PM_MenuVMargin: - ret = 4; - break; - case PM_MenuPanelWidth: - ret = 0; - break; - case PM_ToolTipLabelFrameWidth: - ret = 0; - break; - case PM_SizeGripSize: { - QStyleHelper::WidgetSizePolicy aSize; -// if (widget && widget->window()->windowType() == Qt::Tool) -// aSize = QStyleHelper::SizeSmall; -// else - aSize = QStyleHelper::SizeLarge; - const QSize size = qt_aqua_get_known_size(CT_SizeGrip, opt, QSize(), aSize); - ret = size.width(); - break; } - case PM_MdiSubWindowFrameWidth: - ret = 1; - break; - case PM_DockWidgetFrameWidth: - ret = 0; - break; - case PM_DockWidgetTitleMargin: - ret = 0; - break; - case PM_DockWidgetSeparatorExtent: - ret = 1; - break; - case PM_ToolBarHandleExtent: - ret = 11; - break; - case PM_ToolBarItemMargin: - ret = 0; - break; - case PM_ToolBarItemSpacing: - ret = 4; - break; - case PM_SplitterWidth: - ret = 7; - break; - case PM_LayoutLeftMargin: - case PM_LayoutTopMargin: - case PM_LayoutRightMargin: - case PM_LayoutBottomMargin: - { - if (opt->state & State_Window) { - /* - AHIG would have (20, 8, 10) here but that makes - no sense. It would also have 14 for the top margin - but this contradicts both Builder and most - applications. - */ - return_SIZE(20, 10, 10); // AHIG - } else { - // hack to detect QTabWidget -// if (widget && widget->parentWidget() -// && widget->parentWidget()->sizePolicy().controlType() == QSizePolicy::TabWidget) { -// if (metric == PM_LayoutTopMargin) { -// /* -// Builder would have 14 (= 20 - 6) instead of 12, -// but that makes the tab look disproportionate. -// */ -// return_SIZE(12, 6, 6); // guess -// } else { -// return_SIZE(20 /* Builder */, 8 /* guess */, 8 /* guess */); -// } -// } else { - /* - Child margins are highly inconsistent in AHIG and Builder. - */ - return_SIZE(12, 8, 6); // guess -// } - } - } - case PM_LayoutHorizontalSpacing: - case PM_LayoutVerticalSpacing: - return -1; - case PM_MenuHMargin: - ret = 0; - break; - case PM_ToolBarExtensionExtent: - ret = 21; - break; - case PM_ToolBarFrameWidth: - ret = 1; - break; - case PM_ScrollView_ScrollBarOverlap: - ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay ? - pixelMetric(PM_ScrollBarExtent, opt) : 0; - break; - case PM_PushButtonFocusFrameRadius: - ret = LargeSmallMini(opt, 8, 7, 5); - break; - case PM_CheckBoxFocusFrameRadius: - ret = LargeSmallMini(opt, 6, 5, 4); - break; - case PM_ComboBoxFocusFrameRadius: - ret = LargeSmallMini(opt, 8, 7, 4); - break; - case PM_RadioButtonFocusFrameRadius: - ret = 10; - break; - case PM_SliderFocusFrameRadius: - // QTBUG-93423: We currently need to skip drawing a focus ring around the handle, since - // the handle drawn by the UIKit is not centered inside the rect we get from calling - // [cell knobRectFlipped:slider.isFlipped]. So we choose to draw the focus as - // a rect instead until we have a better solution available. - ret = 0; - break; - case PM_DialFocusFrameRadius: - case PM_SpinBoxFocusFrameRadius: - case PM_TextAreaFocusFrameRadius: - case PM_TextFieldFocusFrameRadius: - ret = 3; - break; - default: - ret = QCommonStyle::pixelMetric(metric, opt); - break; - } - return ret; -} - -//QPalette QMacStyle::standardPalette() const -//{ -// auto platformTheme = QGuiApplicationPrivate::platformTheme(); -// auto styleNames = platformTheme->themeHint(QPlatformTheme::StyleNames); -// if (styleNames.toStringList().contains("macintosh")) -// return QPalette(); // Inherit everything from theme -// else -// return QStyle::standardPalette(); -//} - -int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, QStyleHintReturn *hret) const -{ - QMacAutoReleasePool pool; - - int ret = 0; - switch (sh) { - case SH_Slider_SnapToValue: - case SH_PrintDialog_RightAlignButtons: - case SH_FontDialog_SelectAssociatedText: - case SH_MenuBar_MouseTracking: - case SH_Menu_MouseTracking: - case SH_ComboBox_ListMouseTracking: - case SH_MainWindow_SpaceBelowMenuBar: - case SH_ItemView_ChangeHighlightOnFocus: - ret = 1; - break; - case SH_ToolBox_SelectedPageTitleBold: - ret = 0; - break; - case SH_DialogButtonBox_ButtonsHaveIcons: - ret = 0; - break; - case SH_Menu_SelectionWrap: - ret = false; - break; - case SH_Menu_KeyboardSearch: - ret = true; - break; - case SH_Menu_SpaceActivatesItem: - ret = true; - break; - case SH_Slider_AbsoluteSetButtons: - ret = Qt::LeftButton|Qt::MiddleButton; - break; - case SH_Slider_PageSetButtons: - ret = 0; - break; - case SH_ScrollBar_ContextMenu: - ret = false; - break; - case SH_TitleBar_AutoRaise: - ret = true; - break; - case SH_Menu_AllowActiveAndDisabled: - ret = false; - break; - case SH_Menu_SubMenuPopupDelay: - ret = 100; - break; - case SH_Menu_SubMenuUniDirection: - ret = true; - break; - case SH_Menu_SubMenuSloppySelectOtherActions: - ret = false; - break; - case SH_Menu_SubMenuResetWhenReenteringParent: - ret = true; - break; - case SH_Menu_SubMenuDontStartSloppyOnLeave: - ret = true; - break; - - case SH_ScrollBar_LeftClickAbsolutePosition: { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - bool result = [defaults boolForKey:@"AppleScrollerPagingBehavior"]; -// if(QApplication::keyboardModifiers() & Qt::AltModifier) -// ret = !result; -// else - ret = result; - break; } - case SH_TabBar_PreferNoArrows: - ret = true; - break; - /* - case SH_DialogButtons_DefaultButton: - ret = QDialogButtons::Reject; - break; - */ - case SH_GroupBox_TextLabelVerticalAlignment: - ret = Qt::AlignTop; - break; - case SH_ScrollView_FrameOnlyAroundContents: - ret = QCommonStyle::styleHint(sh, opt, hret); - break; - case SH_Menu_FillScreenWithScroll: - ret = false; - break; - case SH_Menu_Scrollable: - ret = true; - break; - case SH_RichText_FullWidthSelection: - ret = true; - break; - case SH_BlinkCursorWhenTextSelected: - ret = false; - break; - case SH_Slider_StopMouseOverSlider: - ret = true; - break; - case SH_ListViewExpand_SelectMouseType: - ret = QEvent::MouseButtonRelease; - break; - case SH_TabBar_SelectMouseType: - if (const QStyleOptionTabBarBase *opt2 = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) { - ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease; - } else { - ret = QEvent::MouseButtonRelease; - } - break; - case SH_ComboBox_Popup: - if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) - ret = !cmb->editable; - else - ret = 0; - break; - case SH_Workspace_FillSpaceOnMaximize: - ret = true; - break; - case SH_Widget_ShareActivation: - ret = true; - break; - case SH_Header_ArrowAlignment: - ret = Qt::AlignRight; - break; - case SH_TabBar_Alignment: { -//#if QT_CONFIG(tabwidget) -// if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) { -// if (tab->documentMode()) { -// ret = Qt::AlignLeft; -// break; -// } -// } -//#endif -//#if QT_CONFIG(tabbar) -// if (const QTabBar *tab = qobject_cast<const QTabBar*>(w)) { -// if (tab->documentMode()) { -// ret = Qt::AlignLeft; -// break; -// } -// } -//#endif - ret = Qt::AlignCenter; - } break; - case SH_UnderlineShortcut: - ret = false; - break; - case SH_ToolTipLabel_Opacity: - ret = 242; // About 95% - break; - case SH_Button_FocusPolicy: - ret = Qt::TabFocus; - break; - case SH_EtchDisabledText: - ret = false; - break; - case SH_FocusFrame_Mask: { - ret = true; - if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) { - const uchar fillR = 192, fillG = 191, fillB = 190; - QImage img; - - QSize pixmapSize = opt->rect.size(); - if (!pixmapSize.isEmpty()) { - QPixmap pix(pixmapSize); - pix.fill(QColor(fillR, fillG, fillB)); - QPainter pix_paint(&pix); - proxy()->drawControl(CE_FocusFrame, opt, &pix_paint); - pix_paint.end(); - img = pix.toImage(); - } - - const QRgb *sptr = (QRgb*)img.bits(), *srow; - const qsizetype sbpl = img.bytesPerLine(); - const int w = sbpl/4, h = img.height(); - - QImage img_mask(img.width(), img.height(), QImage::Format_ARGB32); - QRgb *dptr = (QRgb*)img_mask.bits(), *drow; - const qsizetype dbpl = img_mask.bytesPerLine(); - - for (int y = 0; y < h; ++y) { - srow = sptr+((y*sbpl)/4); - drow = dptr+((y*dbpl)/4); - for (int x = 0; x < w; ++x) { - const int redDiff = qRed(*srow) - fillR; - const int greenDiff = qGreen(*srow) - fillG; - const int blueDiff = qBlue(*srow) - fillB; - const int diff = (redDiff * redDiff) + (greenDiff * greenDiff) + (blueDiff * blueDiff); - (*drow++) = (diff < 10) ? 0xffffffff : 0xff000000; - ++srow; - } - } - QBitmap qmask = QBitmap::fromImage(img_mask); - mask->region = QRegion(qmask); - } - break; } - case SH_TitleBar_NoBorder: - ret = 1; - break; - case SH_RubberBand_Mask: - ret = 0; - break; - case SH_ComboBox_LayoutDirection: - ret = Qt::LeftToRight; - break; - case SH_ItemView_EllipsisLocation: - ret = Qt::AlignHCenter; - break; - case SH_ItemView_ShowDecorationSelected: - ret = true; - break; - case SH_TitleBar_ModifyNotification: - ret = false; - break; - case SH_ScrollBar_RollBetweenButtons: - ret = true; - break; - case SH_WindowFrame_Mask: - ret = false; - break; - case SH_TabBar_ElideMode: - ret = Qt::ElideRight; - break; -// case SH_DialogButtonLayout: -// ret = QDialogButtonBox::MacLayout; -// break; -// case SH_FormLayoutWrapPolicy: -// ret = QFormLayout::DontWrapRows; -// break; -// case SH_FormLayoutFieldGrowthPolicy: -// ret = QFormLayout::FieldsStayAtSizeHint; -// break; - case SH_FormLayoutFormAlignment: - ret = Qt::AlignHCenter | Qt::AlignTop; - break; - case SH_FormLayoutLabelAlignment: - ret = Qt::AlignRight; - break; -// case SH_ComboBox_PopupFrameStyle: -// ret = QFrame::NoFrame; -// break; - 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; - case SH_MenuBar_AltKeyNavigation: - ret = false; - break; - case SH_ItemView_MovementWithoutUpdatingSelection: - ret = false; - break; - case SH_FocusFrame_AboveWidget: - ret = true; - break; -// case SH_WizardStyle: -// ret = QWizard::MacStyle; -// break; - case SH_ItemView_ArrowKeysNavigateIntoChildren: - ret = false; - break; - case SH_Menu_FlashTriggeredItem: - ret = true; - break; - case SH_Menu_FadeOutOnHide: - ret = true; - break; - case SH_ItemView_PaintAlternatingRowColorsForEmptyArea: - ret = true; - break; - case SH_TabBar_CloseButtonPosition: - ret = QStyleOptionTabBarBase::LeftSide; - break; - case SH_DockWidget_ButtonsHaveFrame: - ret = false; - break; - case SH_ScrollBar_Transient: - // For the initial version in QQC2, we don't support transient scrollbars. When the - // time comes, consider doing all such animations from QML. - // ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay; - ret = false; - break; - case SH_TitleBar_ShowToolTipsOnButtons: - // min/max/close buttons on windows don't show tool tips - ret = false; - break; - case SH_ComboBox_AllowWheelScrolling: - ret = false; - break; - case SH_SpinBox_ButtonsInsideFrame: - ret = false; - break; - case SH_Table_GridLineColor: - ret = int(qt_mac_toQColor(NSColor.gridColor).rgba()); - break; - default: - ret = QCommonStyle::styleHint(sh, opt, hret); - break; - } - return ret; -} - -QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, - const QStyleOption *opt) const -{ - switch (iconMode) { - case QIcon::Disabled: { - QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32); - int imgh = img.height(); - int imgw = img.width(); - QRgb pixel; - for (int y = 0; y < imgh; ++y) { - for (int x = 0; x < imgw; ++x) { - pixel = img.pixel(x, y); - img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), - qAlpha(pixel) / 2)); - } - } - return QPixmap::fromImage(img); - } - default: - ; - } - return QCommonStyle::generatedIconPixmap(iconMode, pixmap, opt); -} - - -QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt) const -{ - // The default implementation of QStyle::standardIconImplementation() is to call standardPixmap() - // I don't want infinite recursion so if we do get in that situation, just return the Window's - // standard pixmap instead (since there is no mac-specific icon then). This should be fine until - // someone changes how Windows standard - // pixmap works. - static bool recursionGuard = false; - - if (recursionGuard) - return QCommonStyle::standardPixmap(standardPixmap, opt); - - recursionGuard = true; - QIcon icon = proxy()->standardIcon(standardPixmap, opt); - recursionGuard = false; - int size; - switch (standardPixmap) { - default: - size = 32; - break; - case SP_MessageBoxCritical: - case SP_MessageBoxQuestion: - case SP_MessageBoxInformation: - case SP_MessageBoxWarning: - size = 64; - break; - } - return icon.pixmap(opt->window, QSize(size, size)); -} - -void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const -{ - Q_D(const QMacStyle); - - const AppearanceSync appSync; - QMacCGContext cg(p); - d->resolveCurrentNSView(opt->window); - - switch (pe) { - case PE_IndicatorArrowUp: - case PE_IndicatorArrowDown: - case PE_IndicatorArrowRight: - case PE_IndicatorArrowLeft: { - p->save(); - p->setRenderHint(QPainter::Antialiasing); - const int xOffset = 1; // FIXME: opt->direction == Qt::LeftToRight ? 2 : -1; - qreal halfSize = 0.5 * qMin(opt->rect.width(), opt->rect.height()); - const qreal penWidth = qMax(halfSize / 3.0, 1.25); -//#if QT_CONFIG(toolbutton) -// if (const QToolButton *tb = qobject_cast<const QToolButton *>(w)) { -// // When stroking the arrow, make sure it fits in the tool button -// if (tb->arrowType() != Qt::NoArrow -// || tb->popupMode() == QToolButton::MenuButtonPopup) -// halfSize -= penWidth; -// } -//#endif - - QTransform transform; - transform.translate(opt->rect.center().x() + xOffset, opt->rect.center().y() + 2); - QPainterPath path; - switch(pe) { - default: - case PE_IndicatorArrowDown: - break; - case PE_IndicatorArrowUp: - transform.rotate(180); - break; - case PE_IndicatorArrowLeft: - transform.rotate(90); - break; - case PE_IndicatorArrowRight: - transform.rotate(-90); - break; - } - p->setTransform(transform); - - path.moveTo(-halfSize, -halfSize * 0.5); - path.lineTo(0.0, halfSize * 0.5); - path.lineTo(halfSize, -halfSize * 0.5); - - const QPen arrowPen(opt->palette.text(), penWidth, - Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); - p->strokePath(path, arrowPen); - p->restore(); - break; } - case PE_FrameTabBarBase: - if (const QStyleOptionTabBarBase *tbb - = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) { - if (tbb->documentMode) { - p->save(); - drawTabBase(p, tbb); - p->restore(); - return; - } - QRegion region(tbb->rect); - region -= tbb->tabBarRect; - p->save(); - p->setClipRegion(region); - QStyleOptionTabWidgetFrame twf; - twf.QStyleOption::operator=(*tbb); - twf.shape = tbb->shape; - switch (QMacStylePrivate::tabDirection(twf.shape)) { - case QMacStylePrivate::North: - twf.rect = twf.rect.adjusted(0, 0, 0, 10); - break; - case QMacStylePrivate::South: - twf.rect = twf.rect.adjusted(0, -10, 0, 0); - break; - case QMacStylePrivate::West: - twf.rect = twf.rect.adjusted(0, 0, 10, 0); - break; - case QMacStylePrivate::East: - twf.rect = twf.rect.adjusted(0, -10, 0, 0); - break; - } - proxy()->drawPrimitive(PE_FrameTabWidget, &twf, p); - p->restore(); - } - break; - case PE_PanelTipLabel: - p->fillRect(opt->rect, opt->palette.brush(QPalette::ToolTipBase)); - break; - case PE_FrameGroupBox: - if (const auto *groupBox = qstyleoption_cast<const QStyleOptionFrame *>(opt)) - if (groupBox->features & QStyleOptionFrame::Flat) { - QCommonStyle::drawPrimitive(pe, groupBox, p); - break; - } - Q_FALLTHROUGH(); - case PE_FrameTabWidget: - { - const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Box, QStyleHelper::SizeLarge); - auto *box = static_cast<NSBox *>(d->cocoaControl(cw)); - // FIXME Since macOS 10.14, simply calling drawRect: won't display anything anymore. - // The AppKit team is aware of this and has proposed a couple of solutions. - // The first solution was to call displayRectIgnoringOpacity:inContext: instead. - // However, it doesn't seem to work on 10.13. More importantly, dark mode on 10.14 - // is extremely slow. Light mode works fine. - // The second solution is to subclass NSBox and reimplement a trivial drawRect: which - // would only call super. This works without any issue on 10.13, but a double border - // shows on 10.14 in both light and dark modes. - // The code below picks what works on each version and mode. On 10.13 and earlier, we - // simply call drawRect: on a regular NSBox. On 10.14, we call displayRectIgnoringOpacity: - // inContext:, but only in light mode. In dark mode, we use a custom NSBox subclass, - // QDarkNSBox, of type NSBoxCustom. Its appearance is close enough to the real thing so - // we can use this for now. - auto adjustedRect = opt->rect; - bool needTranslation = false; - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave - && !qt_mac_applicationIsInDarkMode()) { - // 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:. - // Unfortunately, the resulting box is smaller then the actual rect we - // wanted. This can be seen, e.g. because tabs (buttons) are misaligned - // 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), - // more or less is what we really want. We'll have to adjust CTM - // and translate accordingly. - adjustedRect.adjust(0, 0, 6, 6); - needTranslation = true; - } - d->drawNSViewInRect(box, adjustedRect, p, ^(CGContextRef ctx, const CGRect &rect) { -//#if QT_CONFIG(tabwidget) -// if (QTabWidget *tabWidget = qobject_cast<QTabWidget *>(opt->styleObject)) -// clipTabBarFrame(opt, this, ctx); -//#endif - QMacAutoReleasePool pool; - CGContextTranslateCTM(ctx, 0, rect.origin.y + rect.size.height); - CGContextScaleCTM(ctx, 1, -1); - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave - || [box isMemberOfClass:QDarkNSBox.class]) { - [box drawRect:rect]; - } else { - if (needTranslation) - CGContextTranslateCTM(ctx, -3.0, 5.0); - [box displayRectIgnoringOpacity:box.bounds inContext:NSGraphicsContext.currentContext]; - } - }); - break; - } - case PE_IndicatorToolBarSeparator: { - QPainterPath path; - if (opt->state & State_Horizontal) { - int xpoint = opt->rect.center().x(); - path.moveTo(xpoint + 0.5, opt->rect.top() + 1); - path.lineTo(xpoint + 0.5, opt->rect.bottom()); - } else { - int ypoint = opt->rect.center().y(); - path.moveTo(opt->rect.left() + 2 , ypoint + 0.5); - path.lineTo(opt->rect.right() + 1, ypoint + 0.5); - } - QPainterPathStroker theStroker; - 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() - : 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")) { -// p->save(); -// p->setPen(QPen(frame->palette.dark().color(), frame->lineWidth)); -// p->setBrush(frame->palette.window()); -// p->drawRect(frame->rect); -// p->restore(); -// } - } - break; - case PE_IndicatorDockWidgetResizeHandle: { - // The docwidget resize handle is drawn as a one-pixel wide line. - p->save(); - if (opt->state & State_Horizontal) { - p->setPen(QColor(160, 160, 160)); - p->drawLine(opt->rect.topLeft(), opt->rect.topRight()); - } else { - p->setPen(QColor(145, 145, 145)); - p->drawLine(opt->rect.topRight(), opt->rect.bottomRight()); - } - p->restore(); - } break; - case PE_IndicatorToolBarHandle: { - p->save(); - QPainterPath path; - int x = opt->rect.x() + 6; - int y = opt->rect.y() + 7; - static const int RectHeight = 2; - if (opt->state & State_Horizontal) { - while (y < opt->rect.height() - RectHeight - 5) { - path.moveTo(x, y); - path.addEllipse(x, y, RectHeight, RectHeight); - y += 6; - } - } else { - while (x < opt->rect.width() - RectHeight - 5) { - path.moveTo(x, y); - path.addEllipse(x, y, RectHeight, RectHeight); - x += 6; - } - } - p->setPen(Qt::NoPen); - QColor dark = opt->palette.dark().color().darker(); - dark.setAlphaF(0.50); - p->fillPath(path, dark); - p->restore(); - - break; - } - case PE_IndicatorHeaderArrow: - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { - // In HITheme, up is down, down is up and hamburgers eat people. - if (header->sortIndicator != QStyleOptionHeader::None) - proxy()->drawPrimitive( - (header->sortIndicator == QStyleOptionHeader::SortDown) ? - PE_IndicatorArrowUp : PE_IndicatorArrowDown, header, p); - } - break; - case PE_IndicatorMenuCheckMark: { - QColor pc; - if (opt->state & State_On) - pc = opt->palette.highlightedText().color(); - else - pc = opt->palette.text().color(); - - QCFType<CGColorRef> checkmarkColor = CGColorCreateGenericRGB(static_cast<CGFloat>(pc.redF()), - static_cast<CGFloat>(pc.greenF()), - static_cast<CGFloat>(pc.blueF()), - static_cast<CGFloat>(pc.alphaF())); - // kCTFontUIFontSystem and others give the same result - // as kCTFontUIFontMenuItemMark. However, the latter is - // more reminiscent to HITheme's kThemeMenuItemMarkFont. - // See also the font for small- and mini-sized widgets, - // where we end up using the generic system font type. - const CTFontUIFontType fontType = (opt->state & State_Mini) ? kCTFontUIFontMiniSystem : - (opt->state & State_Small) ? kCTFontUIFontSmallSystem : - kCTFontUIFontMenuItemMark; - // Similarly for the font size, where there is a small difference - // between regular combobox and item view items, and and menu items. - // However, we ignore any difference for small- and mini-sized widgets. - const CGFloat fontSize = fontType == kCTFontUIFontMenuItemMark ? opt->fontMetrics.height() : 0.0; - QCFType<CTFontRef> checkmarkFont = CTFontCreateUIFontForLanguage(fontType, fontSize, NULL); - - CGContextSaveGState(cg); - CGContextSetShouldSmoothFonts(cg, NO); // Same as HITheme and Cocoa menu checkmarks - - // Baseline alignment tweaks for QComboBox and QMenu - const CGFloat vOffset = (opt->state & State_Mini) ? 0.0 : - (opt->state & State_Small) ? 1.0 : - 0.75; - - CGContextTranslateCTM(cg, 0, opt->rect.bottom()); - CGContextScaleCTM(cg, 1, -1); - // Translate back to the original position and add rect origin and offset - CGContextTranslateCTM(cg, opt->rect.x(), vOffset); - - // CTFont has severe difficulties finding the checkmark character among its - // glyphs. Fortunately, CTLine knows its ways inside the Cocoa labyrinth. - static const CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName }; - static const int numValues = sizeof(keys) / sizeof(keys[0]); - const CFTypeRef values[] = { (CFTypeRef)checkmarkFont, (CFTypeRef)checkmarkColor }; - Q_STATIC_ASSERT((sizeof(values) / sizeof(values[0])) == numValues); - QCFType<CFDictionaryRef> attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, - numValues, NULL, NULL); - // U+2713: CHECK MARK - QCFType<CFAttributedStringRef> checkmarkString = CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@"\u2713", attributes); - QCFType<CTLineRef> line = CTLineCreateWithAttributedString(checkmarkString); - - CTLineDraw((CTLineRef)line, cg); - CGContextFlush(cg); // CTLineDraw's documentation says it doesn't flush - - CGContextRestoreGState(cg); - break; } - case PE_IndicatorItemViewItemCheck: - case PE_IndicatorRadioButton: - case PE_IndicatorCheckBox: { - const bool isEnabled = opt->state & State_Enabled; - const bool isPressed = opt->state & State_Sunken; - const bool isRadioButton = (pe == PE_IndicatorRadioButton); - const auto ct = isRadioButton ? QMacStylePrivate::Button_RadioButton : QMacStylePrivate::Button_CheckBox; - const auto cs = d->effectiveAquaSizeConstrain(opt); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *tb = static_cast<NSButton *>(d->cocoaControl(cw)); - tb.enabled = isEnabled; - tb.state = (opt->state & State_NoChange) ? NSMixedState : - (opt->state & State_On) ? NSOnState : NSOffState; - [tb highlight:isPressed]; - const auto vOffset = [=] { - // As measured - if (cs == QStyleHelper::SizeMini) - return ct == QMacStylePrivate::Button_CheckBox ? -0.5 : 0.5; - - return cs == QStyleHelper::SizeSmall ? 0.5 : 0.0; - } (); - d->drawNSViewInRect(tb, opt->rect, p, ^(CGContextRef ctx, const CGRect &rect) { - QMacAutoReleasePool pool; - CGContextTranslateCTM(ctx, 0, vOffset); - [tb.cell drawInteriorWithFrame:rect inView:tb]; - }); - break; } - case PE_FrameFocusRect: - // Use the our own focus widget stuff. - break; - case PE_IndicatorBranch: { - if (!(opt->state & State_Children)) - break; - const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Button_Disclosure, QStyleHelper::SizeLarge); - NSButtonCell *triangleCell = static_cast<NSButtonCell *>(d->cocoaCell(cw)); - [triangleCell setState:(opt->state & State_Open) ? NSOnState : NSOffState]; -// bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus); - bool viewHasFocus = false; - [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight]; - - d->setupNSGraphicsContext(cg, NO); - - QRect qtRect = opt->rect.adjusted(DisclosureOffset, 0, -DisclosureOffset, 0); - CGRect rect = CGRectMake(qtRect.x() + 1, qtRect.y(), qtRect.width(), qtRect.height()); - CGContextTranslateCTM(cg, rect.origin.x, rect.origin.y + rect.size.height); - CGContextScaleCTM(cg, 1, -1); - CGContextTranslateCTM(cg, -rect.origin.x, -rect.origin.y); - - [triangleCell drawBezelWithFrame:NSRectFromCGRect(rect) inView:[triangleCell controlView]]; - - d->restoreNSGraphicsContext(cg); - break; } - - case PE_Frame: { - const QPen oldPen = p->pen(); - QPen penCpy = p->pen(); - penCpy.setWidth(2); - penCpy.setColor(opt->palette.dark().color()); - p->setPen(penCpy); - p->drawRect(opt->rect); - p->setPen(oldPen); - break; } - case PE_PanelLineEdit: - case PE_FrameLineEdit: - if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { - if (frame->state & State_Sunken) { - const bool isEnabled = opt->state & State_Enabled; - const bool isReadOnly = opt->state & State_ReadOnly; - const bool isRounded = frame->features & QStyleOptionFrame::Rounded; - const auto cs = d->effectiveAquaSizeConstrain(opt, CT_LineEdit); - const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::TextField, cs); - auto *tf = static_cast<NSTextField *>(d->cocoaControl(cw)); - tf.enabled = isEnabled; - tf.editable = !isReadOnly; - tf.bezeled = YES; - static_cast<NSTextFieldCell *>(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel; - tf.frame = opt->rect.toCGRect(); - d->drawNSViewInRect(tf, opt->rect, p, ^(CGContextRef, const CGRect &rect) { - QMacAutoReleasePool pool; - if (!qt_mac_applicationIsInDarkMode()) { - // 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 - // background color. See the comments below for PE_PanelLineEdit. - CGContextRef cgContext = NSGraphicsContext.currentContext.CGContext; - // See QMacCGContext, here we expect bitmap context created with - // color space 'kCGColorSpaceSRGB', if it's something else - we - // give up. - if (cgContext ? bool(CGBitmapContextGetColorSpace(cgContext)) : false) { - tf.drawsBackground = YES; - const QColor bgColor = frame->palette.brush(QPalette::Base).color(); - tf.backgroundColor = [NSColor colorWithSRGBRed:bgColor.redF() - green:bgColor.greenF() - blue:bgColor.blueF() - alpha:bgColor.alphaF()]; - if (bgColor.alpha() != 255) { - // No way we can have it bezeled and transparent ... - tf.bordered = YES; - } - } - } - - [tf.cell drawWithFrame:rect inView:tf]; - }); - } else { - QCommonStyle::drawPrimitive(pe, opt, p); - } - } - break; - case PE_PanelScrollAreaCorner: { - const QBrush brush(opt->palette.brush(QPalette::Base)); - p->fillRect(opt->rect, brush); - p->setPen(QPen(QColor(217, 217, 217))); - p->drawLine(opt->rect.topLeft(), opt->rect.topRight()); - p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft()); - } break; - case PE_FrameStatusBarItem: - break; -//#if QT_CONFIG(tabbar) -// case PE_IndicatorTabClose: { -// // Make close button visible only on the hovered tab. -// QTabBar *tabBar = qobject_cast<QTabBar*>(w->parentWidget()); -// const QWidget *closeBtn = w; -// if (!tabBar) { -// // QStyleSheetStyle instead of CloseButton (which has -// // a QTabBar as a parent widget) uses the QTabBar itself: -// tabBar = qobject_cast<QTabBar *>(const_cast<QWidget*>(w)); -// closeBtn = decltype(closeBtn)(property("_q_styleSheetRealCloseButton").value<void *>()); -// } -// if (tabBar) { -// const bool documentMode = tabBar->documentMode(); -// const QTabBarPrivate *tabBarPrivate = static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar)); -// const int hoveredTabIndex = tabBarPrivate->hoveredTabIndex(); -// if (!documentMode || -// (hoveredTabIndex != -1 && ((closeBtn == tabBar->tabButton(hoveredTabIndex, QTabBar::LeftSide)) || -// (closeBtn == tabBar->tabButton(hoveredTabIndex, QTabBar::RightSide))))) { -// const bool hover = (opt->state & State_MouseOver); -// const bool selected = (opt->state & State_Selected); -// const bool pressed = (opt->state & State_Sunken); -// drawTabCloseButton(p, hover, selected, pressed, documentMode); -// } -// } -// } break; -//#endif // QT_CONFIG(tabbar) - case PE_PanelStatusBar: { - // Fill the status bar with the titlebar gradient. - QLinearGradient linearGrad; - const bool isMainWindow = qt_macWindowMainWindow(opt->window); - if (isMainWindow) - linearGrad = titlebarGradientActive(); - else - linearGrad = titlebarGradientInactive(); - - linearGrad.setStart(0, opt->rect.top()); - linearGrad.setFinalStop(0, opt->rect.bottom()); - p->fillRect(opt->rect, linearGrad); - - // Draw the black separator line at the top of the status bar. - if (isMainWindow) - p->setPen(titlebarSeparatorLineActive); - else - p->setPen(titlebarSeparatorLineInactive); - p->drawLine(opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.top()); - - break; - } - case PE_PanelMenu: { - p->save(); - p->fillRect(opt->rect, Qt::transparent); - p->setPen(Qt::transparent); - p->setBrush(opt->palette.window()); - p->setRenderHint(QPainter::Antialiasing, true); - const QPainterPath path = d->windowPanelPath(opt->rect); - p->drawPath(path); - p->restore(); - } break; - - default: - QCommonStyle::drawPrimitive(pe, opt, p); - break; - } -} - -static QPixmap darkenPixmap(const QPixmap &pixmap) -{ - QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32); - int imgh = img.height(); - int imgw = img.width(); - int h, s, v, a; - QRgb pixel; - for (int y = 0; y < imgh; ++y) { - for (int x = 0; x < imgw; ++x) { - pixel = img.pixel(x, y); - a = qAlpha(pixel); - QColor hsvColor(pixel); - hsvColor.getHsv(&h, &s, &v); - s = qMin(100, s * 2); - v = v / 2; - hsvColor.setHsv(h, s, v); - pixel = hsvColor.rgb(); - img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), a)); - } - } - return QPixmap::fromImage(img); -} - -void QMacStylePrivate::setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const -{ - if (vertical) { - CGContextTranslateCTM(cg, rect.size.height, 0); - CGContextRotateCTM(cg, M_PI_2); - } - if (vertical != reverse) { - CGContextTranslateCTM(cg, rect.size.width, 0); - CGContextScaleCTM(cg, -1, 1); - } -} - -void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p) const -{ - Q_D(const QMacStyle); - - const AppearanceSync sync; - const QMacAutoReleasePool pool; - - QMacCGContext cg(p); - d->resolveCurrentNSView(opt->window); - - switch (ce) { - case CE_HeaderSection: - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { - State flags = header->state; - QRect ir = header->rect; - const bool pressed = (flags & State_Sunken) && !(flags & State_On); - p->fillRect(ir, pressed ? header->palette.dark() : header->palette.button()); - p->setPen(QPen(header->palette.dark(), 1.0)); - if (header->orientation == Qt::Horizontal) - p->drawLine(QLineF(ir.right() + 0.5, ir.top() + headerSectionSeparatorInset, - ir.right() + 0.5, ir.bottom() - headerSectionSeparatorInset)); - else - p->drawLine(QLineF(ir.left() + headerSectionSeparatorInset, ir.bottom(), - ir.right() - headerSectionSeparatorInset, ir.bottom())); - } - - 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(opt->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 (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { - QRect cr = tb->rect; - int shiftX = 0; - int shiftY = 0; - bool needText = false; - int alignment = 0; - bool down = tb->state & (State_Sunken | State_On); - if (down) { - shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb); - shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, tb); - } - // The down state is special for QToolButtons in a toolbar on the Mac - // The text is a bit bolder and gets a drop shadow and the icons are also darkened. - // This doesn't really fit into any particular case in QIcon, so we - // do the majority of the work ourselves. - if (!(tb->features & QStyleOptionToolButton::Arrow)) { - Qt::ToolButtonStyle tbstyle = tb->toolButtonStyle; - if (tb->icon.isNull() && !tb->text.isEmpty()) - tbstyle = Qt::ToolButtonTextOnly; - - switch (tbstyle) { - case Qt::ToolButtonTextOnly: { - needText = true; - alignment = Qt::AlignCenter; - break; } - case Qt::ToolButtonIconOnly: - case Qt::ToolButtonTextBesideIcon: - case Qt::ToolButtonTextUnderIcon: { - QRect pr = cr; - QIcon::Mode iconMode = (tb->state & State_Enabled) ? QIcon::Normal - : QIcon::Disabled; - QIcon::State iconState = (tb->state & State_On) ? QIcon::On - : QIcon::Off; - QPixmap pixmap = tb->icon.pixmap(opt->window, - tb->rect.size().boundedTo(tb->iconSize), - iconMode, iconState); - - // Draw the text if it's needed. - if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) { - needText = true; - if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { - pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio() + 6); - cr.adjust(0, pr.bottom(), 0, -3); - alignment |= Qt::AlignCenter; - } else { - pr.setWidth(pixmap.width() / pixmap.devicePixelRatio() + 8); - cr.adjust(pr.right(), 0, 0, 0); - alignment |= Qt::AlignLeft | Qt::AlignVCenter; - } - } - if (opt->state & State_Sunken) { - pr.translate(shiftX, shiftY); - pixmap = darkenPixmap(pixmap); - } - proxy()->drawItemPixmap(p, pr, Qt::AlignCenter, pixmap); - break; } - default: - Q_ASSERT(false); - break; - } - - if (needText) { - QPalette pal = tb->palette; - QPalette::ColorRole role = QPalette::NoRole; - if (!proxy()->styleHint(SH_UnderlineShortcut, tb)) - alignment |= Qt::TextHideMnemonic; - if (down) - cr.translate(shiftX, shiftY); - if (tbstyle == Qt::ToolButtonTextOnly - || (tbstyle != Qt::ToolButtonTextOnly && !down)) { - QPen pen = p->pen(); - QColor light = down || isDarkMode() ? Qt::black : Qt::white; - light.setAlphaF(0.375f); - p->setPen(light); - p->drawText(cr.adjusted(0, 1, 0, 1), alignment, tb->text); - p->setPen(pen); - if (down && tbstyle == Qt::ToolButtonTextOnly) { -// pal = QApplication::palette("QMenu"); - pal.setCurrentColorGroup(tb->palette.currentColorGroup()); - role = QPalette::HighlightedText; - } - } - proxy()->drawItemText(p, cr, alignment, pal, - tb->state & State_Enabled, tb->text, role); - } - } else { - QCommonStyle::drawControl(ce, &myTb, p); - } - } else -#endif // QT_NO_ACCESSIBILITY - { - QCommonStyle::drawControl(ce, &myTb, p); - } - } - break; - case CE_ToolBoxTabShape: - QCommonStyle::drawControl(ce, opt, p); - break; - case CE_PushButtonBevel: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { - if (!(btn->state & (State_Raised | State_Sunken | State_On))) - break; - - if (btn->features & QStyleOptionButton::CommandLinkButton) { - QCommonStyle::drawControl(ce, opt, p); - 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) - && isActive && hasFocus) - d->autoDefaultButton = btn->styleObject; - else if (d->autoDefaultButton == btn->styleObject) - d->autoDefaultButton = nullptr; - - 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); - const auto cs = d->effectiveAquaSizeConstrain(btn); - 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(pb, frameRect, p, ^(CGContextRef, const CGRect &r) { - QMacAutoReleasePool pool; - [pb.cell drawBezelWithFrame:r inView:pb.superview]; - }); - [pb highlight:NO]; - - 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); - const auto ir = frameRect.toRect(); - int arrowYOffset = 0; - 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); - } - } - break; - case CE_PushButtonLabel: - if (const QStyleOptionButton *b = qstyleoption_cast<const QStyleOptionButton *>(opt)) { - QStyleOptionButton btn(*b); - // We really don't want the label to be drawn the same as on - // 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); - - 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 ((!hasIcon && !hasMenu) || (hasIcon && !hasText)) { - QCommonStyle::drawControl(ce, &btn, p); - } else { - QRect freeContentRect = btn.rect; - QRect textRect = itemTextRect( - btn.fontMetrics, freeContentRect, Qt::AlignCenter, isEnabled, btn.text); - if (hasMenu) { - textRect.moveTo(11, textRect.top()); - } - // Draw the icon: - if (hasIcon) { - int contentW = textRect.width(); - if (hasMenu) - contentW += proxy()->pixelMetric(PM_MenuButtonIndicator) + 4; - 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: - QIcon::State state = QIcon::Off; - if (btn.state & State_On) - state = QIcon::On; - QPixmap pixmap = btn.icon.pixmap(opt->window, btn.iconSize, mode, state); - int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); - int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); - contentW += pixmapWidth + QMacStylePrivate::PushButtonContentPadding; - int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2; - int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapHeight) / 2; - QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapWidth, pixmapHeight); - QRect visualIconDestRect = visualRect(btn.direction, freeContentRect, iconDestRect); - proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap); - int newOffset = iconDestRect.x() + iconDestRect.width() - + QMacStylePrivate::PushButtonContentPadding - textRect.x(); - textRect.adjust(newOffset, 0, newOffset, 0); - } - // Draw the text: - if (hasText) { - textRect = visualRect(btn.direction, freeContentRect, textRect); - proxy()->drawItemText(p, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, btn.palette, - isEnabled, btn.text, QPalette::ButtonText); - } - } - } - break; - case CE_ComboBoxLabel: - if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { - auto comboCopy = *cb; - comboCopy.direction = Qt::LeftToRight; - // The rectangle will be adjusted to SC_ComboBoxEditField with comboboxEditBounds() - QCommonStyle::drawControl(CE_ComboBoxLabel, &comboCopy, p); - } - break; - case CE_TabBarTabShape: - if (const auto *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) { - if (tabOpt->documentMode) { - p->save(); - bool isUnified = false; -// if (w) { -// QRect tabRect = tabOpt->rect; -// QPoint windowTabStart = w->mapTo(w->window(), tabRect.topLeft()); -// isUnified = isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowTabStart.y()); -// } - - const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, opt); - drawTabShape(p, tabOpt, isUnified, tabOverlap); - - p->restore(); - return; - } - - const bool isActive = tabOpt->state & State_Active; - const bool isEnabled = tabOpt->state & State_Enabled; - const bool isPressed = tabOpt->state & State_Sunken; - const bool isSelected = tabOpt->state & State_Selected; - const auto tabDirection = QMacStylePrivate::tabDirection(tabOpt->shape); - const bool verticalTabs = tabDirection == QMacStylePrivate::East - || tabDirection == QMacStylePrivate::West; - - QStyleOptionTab::TabPosition tp = tabOpt->position; - QStyleOptionTab::SelectedPosition sp = tabOpt->selectedPosition; - if (tabOpt->direction == Qt::RightToLeft && !verticalTabs) { - if (tp == QStyleOptionTab::Beginning) - tp = QStyleOptionTab::End; - else if (tp == QStyleOptionTab::End) - tp = QStyleOptionTab::Beginning; - - if (sp == QStyleOptionTab::NextIsSelected) - sp = QStyleOptionTab::PreviousIsSelected; - else if (sp == QStyleOptionTab::PreviousIsSelected) - sp = QStyleOptionTab::NextIsSelected; - } - - // Alas, NSSegmentedControl and NSSegmentedCell are letting us down. - // We're not able to draw it at will, either calling -[drawSegment: - // 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); - // Extra hacks to get the proper pressed appreance when not selected or selected and inactive - const bool needsInactiveHack = (!isActive && isSelected); - const auto ct = !needsInactiveHack && (isSelected || tp == QStyleOptionTab::OnlyOneTab) ? - QMacStylePrivate::Button_PushButton : - QMacStylePrivate::Button_PopupButton; - const bool isPopupButton = ct == QMacStylePrivate::Button_PopupButton; - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); - - auto vOffset = isPopupButton ? 1 : 2; - if (tabDirection == QMacStylePrivate::East) - vOffset -= 1; - const auto outerAdjust = isPopupButton ? 1 : 4; - const auto innerAdjust = isPopupButton ? 20 : 10; - QRectF frameRect = tabOpt->rect; - if (verticalTabs) - frameRect = QRectF(frameRect.y(), frameRect.x(), frameRect.height(), frameRect.width()); - // Adjust before clipping - frameRect = frameRect.translated(0, vOffset); - switch (tp) { - case QStyleOptionTab::Beginning: - // 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); - break; - case QStyleOptionTab::Middle: - frameRect = frameRect.adjusted(-innerAdjust, 0, innerAdjust, 0); - break; - 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); - break; - case QStyleOptionTab::OnlyOneTab: - frameRect = frameRect.adjusted(-outerAdjust, 0, outerAdjust, 0); - break; - } - pb.frame = frameRect.toCGRect(); - - pb.enabled = isEnabled; - [pb highlight:isPressed]; - // Set off state when inactive. See needsInactiveHack for when it's selected - pb.state = (isActive && isSelected && !isPressed) ? NSOnState : NSOffState; - - const auto drawBezelBlock = ^(CGContextRef ctx, const CGRect &r) { - QMacAutoReleasePool pool; - CGContextClipToRect(ctx, opt->rect.toCGRect()); - if (!isSelected || needsInactiveHack) { - // Final stage of the pressed state hack: flip NSPopupButton rendering - if (!verticalTabs && tp == QStyleOptionTab::End) { - CGContextTranslateCTM(ctx, opt->rect.right(), 0); - CGContextScaleCTM(ctx, -1, 1); - CGContextTranslateCTM(ctx, -frameRect.left(), 0); - } else if (tabDirection == QMacStylePrivate::West && tp == QStyleOptionTab::Beginning) { - CGContextTranslateCTM(ctx, 0, opt->rect.top()); - CGContextScaleCTM(ctx, 1, -1); - CGContextTranslateCTM(ctx, 0, -frameRect.right()); - } else if (tabDirection == QMacStylePrivate::East && tp == QStyleOptionTab::End) { - CGContextTranslateCTM(ctx, 0, opt->rect.bottom()); - CGContextScaleCTM(ctx, 1, -1); - CGContextTranslateCTM(ctx, 0, -frameRect.left()); - } - } - - // Rotate and translate CTM when vertical - // On macOS: positive angle is CW, negative is CCW - if (tabDirection == QMacStylePrivate::West) { - CGContextTranslateCTM(ctx, 0, frameRect.right()); - CGContextRotateCTM(ctx, -M_PI_2); - CGContextTranslateCTM(ctx, -frameRect.left(), 0); - } else if (tabDirection == QMacStylePrivate::East) { - CGContextTranslateCTM(ctx, opt->rect.right(), 0); - CGContextRotateCTM(ctx, M_PI_2); - } - - // Now, if it's a trick with a popup button, it has an arrow - // which makes no sense on tabs. - NSPopUpArrowPosition oldPosition = NSPopUpArrowAtCenter; - NSPopUpButtonCell *pbCell = nil; - if (isPopupButton) { - pbCell = static_cast<NSPopUpButtonCell *>(pb.cell); - oldPosition = pbCell.arrowPosition; - pbCell.arrowPosition = NSPopUpNoArrow; - } - - [pb.cell drawBezelWithFrame:r inView:pb.superview]; - - if (pbCell) // Restore, we may reuse it for a ComboBox. - pbCell.arrowPosition = oldPosition; - }; - - if (needsInactiveHack) { - // First, render tab as non-selected tab on a pixamp - const qreal pixelRatio = p->device()->devicePixelRatioF(); - QImage tabPixmap(opt->rect.size() * pixelRatio, QImage::Format_ARGB32_Premultiplied); - tabPixmap.setDevicePixelRatio(pixelRatio); - tabPixmap.fill(Qt::transparent); - QPainter tabPainter(&tabPixmap); - d->drawNSViewInRect(pb, frameRect, &tabPainter, ^(CGContextRef ctx, const CGRect &r) { - QMacAutoReleasePool pool; - CGContextTranslateCTM(ctx, -opt->rect.left(), -opt->rect.top()); - drawBezelBlock(ctx, r); - }); - tabPainter.end(); - - // Then, darken it with the proper shade of gray - const qreal inactiveGray = 0.898; // As measured - const int inactiveGray8 = qRound(inactiveGray * 255.0); - const QRgb inactiveGrayRGB = qRgb(inactiveGray8, inactiveGray8, inactiveGray8); - for (int l = 0; l < tabPixmap.height(); ++l) { - auto *line = reinterpret_cast<QRgb*>(tabPixmap.scanLine(l)); - for (int i = 0; i < tabPixmap.width(); ++i) { - if (qAlpha(line[i]) == 255) { - line[i] = inactiveGrayRGB; - } else if (qAlpha(line[i]) > 128) { - const int g = qRound(inactiveGray * qRed(line[i])); - line[i] = qRgba(g, g, g, qAlpha(line[i])); - } - } - } - - // Finally, draw the tab pixmap on the current painter - p->drawImage(opt->rect, tabPixmap); - } else { - d->drawNSViewInRect(pb, frameRect, p, drawBezelBlock); - } - - if (!isSelected && sp != QStyleOptionTab::NextIsSelected - && tp != QStyleOptionTab::End - && tp != QStyleOptionTab::OnlyOneTab) { - static const QPen separatorPen(Qt::black, 1.0); - p->save(); - p->setOpacity(isEnabled ? 0.105 : 0.06); // As measured - p->setPen(separatorPen); - if (tabDirection == QMacStylePrivate::West) { - p->drawLine(QLineF(opt->rect.left() + 1.5, opt->rect.bottom(), - opt->rect.right() - 0.5, opt->rect.bottom())); - } else if (tabDirection == QMacStylePrivate::East) { - p->drawLine(QLineF(opt->rect.left(), opt->rect.bottom(), - opt->rect.right() - 0.5, opt->rect.bottom())); - } else { - p->drawLine(QLineF(opt->rect.right(), opt->rect.top() + 1.0, - opt->rect.right(), opt->rect.bottom() - 0.5)); - } - 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 tabDirection = QMacStylePrivate::tabDirection(tab->shape); - const bool verticalTabs = tabDirection == QMacStylePrivate::East - || tabDirection == QMacStylePrivate::West; - - // Check to see if we use have the same as the system font - // (QComboMenuItem is internal and should never be seen by the - // outside world, unless they read the source, in which case, it's - // their own fault). -// const bool nonDefaultFont = p->font() != qt_app_fonts_hash()->value("QComboMenuItem"); - const bool nonDefaultFont = false; - -// 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); - - if (myTab.documentMode && isDarkMode()) { - bool active = (myTab.state & State_Selected) && (myTab.state & State_Active); - myTab.palette.setColor(QPalette::WindowText, active ? Qt::white : Qt::gray); - } - - int heightOffset = 0; - if (verticalTabs) { - heightOffset = -1; - } else if (nonDefaultFont) { - if (p->fontMetrics().height() == myTab.rect.height()) - heightOffset = 2; - } - myTab.rect.setHeight(myTab.rect.height() + heightOffset); - - QCommonStyle::drawControl(ce, &myTab, p); - } - break; - case CE_DockWidgetTitle: - if (const auto *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) { - const bool isVertical = dwOpt->verticalTitleBar; - const auto effectiveRect = isVertical ? opt->rect.transposed() : opt->rect; - p->save(); - if (isVertical) { - p->translate(effectiveRect.left(), effectiveRect.top() + effectiveRect.width()); - p->rotate(-90); - p->translate(-effectiveRect.left(), -effectiveRect.top()); - } - - // 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); - - // draw horizontal line at bottom - p->setPen(opt->palette.dark().color()); - p->drawLine(effectiveRect.bottomLeft(), effectiveRect.bottomRight()); - - if (!dwOpt->title.isEmpty()) { - auto titleRect = proxy()->subElementRect(SE_DockWidgetTitleBarText, opt); - if (isVertical) - titleRect = QRect(effectiveRect.left() + opt->rect.bottom() - titleRect.bottom(), - effectiveRect.top() + titleRect.left() - opt->rect.left(), - titleRect.height(), - titleRect.width()); - - const auto text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); - proxy()->drawItemText(p, titleRect, Qt::AlignCenter, dwOpt->palette, - dwOpt->state & State_Enabled, text, QPalette::WindowText); - } - p->restore(); - } - break; - case CE_FocusFrame: { -// const auto *ff = qobject_cast<const QFocusFrame *>(w); -// const auto *ffw = ff ? ff->widget() : nullptr; -// const auto ct = [=] { -// if (ffw) { -// if (ffw->inherits("QCheckBox")) -// return QMacStylePrivate::Button_CheckBox; -// if (ffw->inherits("QRadioButton")) -// return QMacStylePrivate::Button_RadioButton; -// if (ffw->inherits("QLineEdit") || ffw->inherits("QTextEdit")) -// return QMacStylePrivate::TextField; -// } -// -// 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; -// const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt); -// const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt); -// d->drawFocusRing(p, opt->rect, hMargin, vMargin, QMacStylePrivate::CocoaControl(ct, cs)); - break; } - case CE_MenuEmptyArea: - // Skip: PE_PanelMenu fills in everything - break; - case CE_MenuItem: - case CE_MenuHMargin: - case CE_MenuVMargin: - case CE_MenuTearoff: - case CE_MenuScroller: - if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { - const bool active = mi->state & State_Selected; - if (active) - p->fillRect(mi->rect, mi->palette.highlight()); - - const QStyleHelper::WidgetSizePolicy widgetSize = d->aquaSizeConstrain(opt); - - if (ce == CE_MenuTearoff) { - p->setPen(QPen(mi->palette.dark().color(), 1, Qt::DashLine)); - p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2 - 1, - mi->rect.x() + mi->rect.width() - 4, - mi->rect.y() + mi->rect.height() / 2 - 1); - p->setPen(QPen(mi->palette.light().color(), 1, Qt::DashLine)); - p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2, - mi->rect.x() + mi->rect.width() - 4, - mi->rect.y() + mi->rect.height() / 2); - } else if (ce == CE_MenuScroller) { - const QSize scrollerSize = QSize(10, 8); - const int scrollerVOffset = 5; - const int left = mi->rect.x() + (mi->rect.width() - scrollerSize.width()) / 2; - const int right = left + scrollerSize.width(); - int top; - int bottom; - if (opt->state & State_DownArrow) { - bottom = mi->rect.y() + scrollerVOffset; - top = bottom + scrollerSize.height(); - } else { - bottom = mi->rect.bottom() - scrollerVOffset; - top = bottom - scrollerSize.height(); - } - p->save(); - p->setRenderHint(QPainter::Antialiasing); - QPainterPath path; - path.moveTo(left, bottom); - path.lineTo(right, bottom); - path.lineTo((left + right) / 2, top); - p->fillPath(path, opt->palette.buttonText()); - p->restore(); - } else if (ce != CE_MenuItem) { - break; - } - - if (mi->menuItemType == QStyleOptionMenuItem::Separator) { - CGColorRef separatorColor = [NSColor quaternaryLabelColor].CGColor; - const QRect separatorRect = QRect(mi->rect.left(), mi->rect.center().y(), mi->rect.width(), 2); - p->fillRect(separatorRect, qt_mac_toQColor(separatorColor)); - break; - } - - const int maxpmw = mi->maxIconWidth; - const bool enabled = mi->state & State_Enabled; - - int xpos = mi->rect.x() + 18; - int checkcol = maxpmw; - if (!enabled) - p->setPen(mi->palette.text().color()); - else if (active) - p->setPen(mi->palette.highlightedText().color()); - else - p->setPen(mi->palette.buttonText().color()); - - if (mi->checked) { - QStyleOption checkmarkOpt; -// checkmarkOpt.initFrom(w); - - const int mw = checkcol + macItemFrame; - const int mh = mi->rect.height() + macItemFrame; - const int xp = mi->rect.x() + macItemFrame; - checkmarkOpt.rect = QRect(xp, mi->rect.y() - checkmarkOpt.fontMetrics.descent(), mw, mh); - - checkmarkOpt.state.setFlag(State_On, active); - checkmarkOpt.state.setFlag(State_Enabled, enabled); - if (widgetSize == QStyleHelper::SizeMini) - checkmarkOpt.state |= State_Mini; - else if (widgetSize == QStyleHelper::SizeSmall) - checkmarkOpt.state |= State_Small; - - // We let drawPrimitive(PE_IndicatorMenuCheckMark) pick the right color - checkmarkOpt.palette.setColor(QPalette::HighlightedText, p->pen().color()); - checkmarkOpt.palette.setColor(QPalette::Text, p->pen().color()); - - proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &checkmarkOpt, p); - } - if (!mi->icon.isNull()) { - QIcon::Mode mode = (mi->state & State_Enabled) ? QIcon::Normal - : QIcon::Disabled; - // Always be normal or disabled to follow the Mac style. - int smallIconSize = proxy()->pixelMetric(PM_SmallIconSize); - QSize iconSize(smallIconSize, smallIconSize); -//#if QT_CONFIG(combobox) -// if (const QComboBox *comboBox = qobject_cast<const QComboBox *>(w)) { -// iconSize = comboBox->iconSize(); -// } -//#endif - QPixmap pixmap = mi->icon.pixmap(opt->window, iconSize, mode); - int pixw = pixmap.width() / pixmap.devicePixelRatio(); - int pixh = pixmap.height() / pixmap.devicePixelRatio(); - QRect cr(xpos, mi->rect.y(), checkcol, mi->rect.height()); - QRect pmr(0, 0, pixw, pixh); - pmr.moveCenter(cr.center()); - p->drawPixmap(pmr.topLeft(), pixmap); - xpos += pixw + 6; - } - - QString s = mi->text; - const auto text_flags = Qt::AlignVCenter | Qt::TextHideMnemonic - | Qt::TextSingleLine | Qt::AlignAbsolute; - int yPos = mi->rect.y(); - if (widgetSize == QStyleHelper::SizeMini) - yPos += 1; - - const bool isSubMenu = mi->menuItemType == QStyleOptionMenuItem::SubMenu; - const int tabwidth = isSubMenu ? 9 : mi->tabWidth; - - QString rightMarginText; - if (isSubMenu) - rightMarginText = QStringLiteral("\u25b6\ufe0e"); // U+25B6 U+FE0E: BLACK RIGHT-POINTING TRIANGLE - - // If present, save and remove embedded shorcut from text - const int tabIndex = s.indexOf(QLatin1Char('\t')); - if (tabIndex >= 0) { - if (!isSubMenu) // ... but ignore it if it's a submenu. - rightMarginText = s.mid(tabIndex + 1); - s = s.left(tabIndex); - } - - p->save(); - if (!rightMarginText.isEmpty()) { -// p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font())); - int xp = mi->rect.right() - tabwidth - macRightBorder + 2; - if (!isSubMenu) - xp -= macItemHMargin + macItemFrame + 3; // Adjust for shortcut - p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags | Qt::AlignRight, rightMarginText); - } - - if (!s.isEmpty()) { - const int xm = macItemFrame + maxpmw + macItemHMargin; - QFont myFont = mi->font; - // myFont may not have any "hard" flags set. We override - // the point size so that when it is resolved against the device, this font will win. - // This is mainly to handle cases where someone sets the font on the window - // and then the combo inherits it and passes it onward. At that point the resolve mask - // is very, very weak. This makes it stonger. - myFont.setPointSizeF(QFontInfo(mi->font).pointSizeF()); - - // QTBUG-65653: Our own text rendering doesn't look good enough, especially on non-retina - // displays. Worked around here while waiting for a proper fix in QCoreTextFontEngine. - // Only if we're not using QCoreTextFontEngine we do fallback to our own text rendering. - const auto *fontEngine = QFontPrivate::get(myFont)->engineForScript(QChar::Script_Common); - Q_ASSERT(fontEngine); - if (fontEngine->type() == QFontEngine::Multi) { - fontEngine = static_cast<const QFontEngineMulti *>(fontEngine)->engine(0); - Q_ASSERT(fontEngine); - } - if (fontEngine->type() == QFontEngine::Mac) { - NSFont *f = (NSFont *)(CTFontRef)fontEngine->handle(); - - // Respect the menu item palette as set in the style option. - const auto pc = p->pen().color(); - NSColor *c = [NSColor colorWithSRGBRed:pc.redF() - green:pc.greenF() - blue:pc.blueF() - alpha:pc.alphaF()]; - - s = qt_mac_removeMnemonics(s); - - QMacCGContext cgCtx(p); - d->setupNSGraphicsContext(cgCtx, YES); - - // Draw at point instead of in rect, as the rect we've computed for the menu item - // is based on the font metrics we got from HarfBuzz, so we may risk having CoreText - // 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]}]; - - d->restoreNSGraphicsContext(cgCtx); - } else { - p->setFont(myFont); - p->drawText(xpos, yPos, mi->rect.width() - xm - tabwidth + 1, - mi->rect.height(), text_flags, s); - } - } - p->restore(); - } - break; - case CE_MenuBarItem: - case CE_MenuBarEmptyArea: - if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { - const bool selected = (opt->state & State_Selected) && (opt->state & State_Enabled) && (opt->state & State_Sunken); - const QBrush bg = selected ? mi->palette.highlight() : mi->palette.window(); - p->fillRect(mi->rect, bg); - - if (ce != CE_MenuBarItem) - break; - - if (!mi->icon.isNull()) { - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); - drawItemPixmap(p, mi->rect, - Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip - | Qt::TextSingleLine, - mi->icon.pixmap(opt->window, QSize(iconExtent, iconExtent), - (mi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled)); - } else { - drawItemText(p, mi->rect, - Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip - | Qt::TextSingleLine, - mi->palette, mi->state & State_Enabled, - mi->text, selected ? QPalette::HighlightedText : QPalette::ButtonText); - } - } - break; - case CE_ProgressBarLabel: - case CE_ProgressBarContents: - break; - case CE_ProgressBarGroove: - if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { - const bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0); - const bool inverted = pb->invertedAppearance; - bool reverse = pb->direction == Qt::RightToLeft; - if (inverted) - reverse = !reverse; - - QRect rect = pb->rect; - const CGRect cgRect = rect.toCGRect(); - - const auto aquaSize = d->aquaSizeConstrain(opt); - -// const QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(d->animation(opt->styleObject)); - QIndeterminateProgressIndicator *ipi = nil; -// if (isIndeterminate || animation) - ipi = static_cast<QIndeterminateProgressIndicator *>(d->cocoaControl({ QMacStylePrivate::ProgressIndicator_Indeterminate, aquaSize })); - if (isIndeterminate) { - // QIndeterminateProgressIndicator derives from NSProgressIndicator. We use a single - // instance that we start animating as soon as one of the progress bars is indeterminate. - // Since they will be in sync (as it's the case in Cocoa), we just need to draw it with - // the right geometry when the animation triggers an update. However, we can't hide it - // entirely between frames since that would stop the animation, so we just set its alpha - // value to 0. Same if we remove it from its superview. See QIndeterminateProgressIndicator - // implementation for details. - // - // Quick: consider implementing this animation by using Quick/QML instead. - // -// if (!animation && opt->styleObject) { -// auto *animation = new QProgressStyleAnimation(d->animateSpeed(QMacStylePrivate::AquaProgressBar), opt->styleObject); -// // NSProgressIndicator is heavier to draw than the HITheme API, so we reduce the frame rate a couple notches. -// animation->setFrameRate(QStyleAnimation::FifteenFps); -// d->startAnimation(animation); -// [ipi startAnimation]; -// } - - d->setupNSGraphicsContext(cg, NO); - d->setupVerticalInvertedXform(cg, reverse, false, cgRect); - [ipi drawWithFrame:cgRect inView:d->backingStoreNSView]; - d->restoreNSGraphicsContext(cg); - } else { -// if (animation) { -// d->stopAnimation(opt->styleObject); -// [ipi stopAnimation]; -// } - - const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::ProgressIndicator_Determinate, aquaSize); - auto *pi = static_cast<NSProgressIndicator *>(d->cocoaControl(cw)); - d->drawNSViewInRect(pi, rect, p, ^(CGContextRef ctx, const CGRect &rect) { - QMacAutoReleasePool pool; - d->setupVerticalInvertedXform(ctx, reverse, false, rect); - pi.minValue = pb->minimum; - pi.maxValue = pb->maximum; - pi.doubleValue = pb->progress; - [pi drawRect:rect]; - }); - } - } - break; - case CE_SizeGrip: { - // This is not HIG kosher: Fall back to the old stuff until we decide what to do. -//#ifndef QT_NO_MDIAREA -// if (!w || !qobject_cast<QMdiSubWindow *>(w->parentWidget())) -//#endif -// break; - -// if (w->testAttribute(Qt::WA_MacOpaqueSizeGrip)) -// p->fillRect(opt->rect, opt->palette.window()); - -// QPen lineColor = QColor(82, 82, 82, 192); -// lineColor.setWidth(1); -// p->save(); -// p->setRenderHint(QPainter::Antialiasing); -// p->setPen(lineColor); -// const Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : qApp->layoutDirection(); -// const int NumLines = 3; -// for (int l = 0; l < NumLines; ++l) { -// const int offset = (l * 4 + 3); -// QPoint start, end; -// if (layoutDirection == Qt::LeftToRight) { -// start = QPoint(opt->rect.width() - offset, opt->rect.height() - 1); -// end = QPoint(opt->rect.width() - 1, opt->rect.height() - offset); -// } else { -// start = QPoint(offset, opt->rect.height() - 1); -// end = QPoint(1, opt->rect.height() - offset); -// } -// p->drawLine(start, end); -// } -// p->restore(); - break; - } - case CE_Splitter: - if (opt->rect.width() > 1 && opt->rect.height() > 1) { - const bool isVertical = !(opt->state & QStyle::State_Horizontal); - // Qt refers to the layout orientation, while Cocoa refers to the divider's. - const auto ct = isVertical ? QMacStylePrivate::SplitView_Horizontal : QMacStylePrivate::SplitView_Vertical; - const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge); - auto *sv = static_cast<NSSplitView *>(d->cocoaControl(cw)); - sv.frame = opt->rect.toCGRect(); - d->drawNSViewInRect(sv, opt->rect, p, ^(CGContextRef, const CGRect &rect) { - QMacAutoReleasePool pool; - [sv drawDividerInRect:rect]; - }); - } else { - QPen oldPen = p->pen(); - p->setPen(opt->palette.dark().color()); - if (opt->state & QStyle::State_Horizontal) - p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft()); - else - p->drawLine(opt->rect.topLeft(), opt->rect.topRight()); - p->setPen(oldPen); - } - break; - case CE_RubberBand: - if (const QStyleOptionRubberBand *rubber = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) { - QColor fillColor(opt->palette.color(QPalette::Disabled, QPalette::Highlight)); - if (!rubber->opaque) { - QColor strokeColor; - // I retrieved these colors from the Carbon-Dev mailing list - strokeColor.setHsvF(0, 0, 0.86, 1.0); - fillColor.setHsvF(0, 0, 0.53, 0.25); - if (opt->rect.width() * opt->rect.height() <= 3) { - p->fillRect(opt->rect, strokeColor); - } else { - QPen oldPen = p->pen(); - QBrush oldBrush = p->brush(); - QPen pen(strokeColor); - p->setPen(pen); - p->setBrush(fillColor); - QRect adjusted = opt->rect.adjusted(1, 1, -1, -1); - if (adjusted.isValid()) - p->drawRect(adjusted); - p->setPen(oldPen); - p->setBrush(oldBrush); - } - } else { - p->fillRect(opt->rect, fillColor); - } - } - break; - case CE_ToolBar: { - const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt); - const bool isDarkMode = qt_mac_applicationIsInDarkMode(); - - // 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 - // the title bar. The following code fills the toolBar area with transparent pixels - // to make that gradient visible. -// if (w) { -//#if QT_CONFIG(mainwindow) -// if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window())) { -// if (toolBar && toolBar->toolBarArea == Qt::TopToolBarArea && mainWindow->unifiedTitleAndToolBarOnMac()) { -// // fill with transparent pixels. -// p->save(); -// p->setCompositionMode(QPainter::CompositionMode_Source); -// p->fillRect(opt->rect, Qt::transparent); -// p->restore(); - -// // Draw a horizontal separator line at the toolBar bottom if the "unified" area ends here. -// // There might be additional toolbars or other widgets such as tab bars in document -// // mode below. Determine this by making a unified toolbar area test for the row below -// // this toolbar. -// const QPoint windowToolbarEnd = w->mapTo(w->window(), opt->rect.bottomLeft()); -// const bool isEndOfUnifiedArea = !isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowToolbarEnd.y() + 1); -// 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()); -// } -// break; -// } -// } -//#endif -// } - - // draw background gradient - QLinearGradient linearGrad; - if (opt->state & State_Horizontal) - linearGrad = QLinearGradient(0, opt->rect.top(), 0, opt->rect.bottom()); - else - linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0); - - QColor mainWindowGradientBegin = isDarkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin; - QColor mainWindowGradientEnd = isDarkMode ? 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; - if (opt->state & State_Horizontal) { - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); - p->drawLine(toolbarRect.topLeft(), toolbarRect.topRight()); - p->setPen(isDarkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114)); - p->drawLine(toolbarRect.bottomLeft(), toolbarRect.bottomRight()); - } else { - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); - p->drawLine(toolbarRect.topLeft(), toolbarRect.bottomLeft()); - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114)); - p->drawLine(toolbarRect.topRight(), toolbarRect.bottomRight()); - } - p->restore(); - - break; } - default: - QCommonStyle::drawControl(ce, opt, p); - break; - } -} - -static void setLayoutItemMargins(int left, int top, int right, int bottom, QRect *rect, Qt::LayoutDirection dir) -{ - if (dir == Qt::RightToLeft) { - rect->adjust(-right, top, -left, bottom); - } else { - rect->adjust(left, top, right, bottom); - } -} - -QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt) const -{ - Q_D(const QMacStyle); - QRect rect; - const int controlSize = getControlSize(opt); - - switch (sr) { - case SE_ItemViewItemText: - if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { - int fw = proxy()->pixelMetric(PM_FocusFrameHMargin, opt); - // We add the focusframeargin between icon and text in commonstyle - rect = QCommonStyle::subElementRect(sr, opt); - if (vopt->features & QStyleOptionViewItem::HasDecoration) - rect.adjust(-fw, 0, 0, 0); - } - break; - case SE_ToolBoxTabContents: - rect = QCommonStyle::subElementRect(sr, opt); - break; - case SE_PushButtonContents: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { - // 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); - const auto cs = d->effectiveAquaSizeConstrain(btn); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto frameRect = cw.adjustedControlFrame(btn->rect); - frameRect -= cw.titleMargins(); - rect = frameRect.toRect(); - } - break; - case SE_HeaderLabel: { - int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt); - rect.setRect(opt->rect.x() + margin, opt->rect.y(), - opt->rect.width() - margin * 2, opt->rect.height() - 2); - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { - // Subtract width needed for arrow, if there is one - if (header->sortIndicator != QStyleOptionHeader::None) { - if (opt->state & State_Horizontal) - rect.setWidth(rect.width() - (headerSectionArrowHeight) - (margin * 2)); - else - rect.setHeight(rect.height() - (headerSectionArrowHeight) - (margin * 2)); - } - } - rect = visualRect(opt->direction, opt->rect, rect); - break; - } - case SE_HeaderArrow: { - int h = opt->rect.height(); - int w = opt->rect.width(); - int x = opt->rect.x(); - int y = opt->rect.y(); - int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt); - - if (opt->state & State_Horizontal) { - rect.setRect(x + w - margin * 2 - headerSectionArrowHeight, y + 5, - headerSectionArrowHeight, h - margin * 2 - 5); - } else { - rect.setRect(x + 5, y + h - margin * 2 - headerSectionArrowHeight, - w - margin * 2 - 5, headerSectionArrowHeight); - } - rect = visualRect(opt->direction, opt->rect, rect); - break; - } - case SE_ProgressBarGroove: - // Wrong in the secondary dimension, but accurate enough in the main dimension. - rect = opt->rect; - break; - case SE_ProgressBarLabel: - break; - case SE_ProgressBarContents: - rect = opt->rect; - break; - case SE_TreeViewDisclosureItem: { - rect = opt->rect; - // As previously returned by HIThemeGetButtonContentBounds - rect.setLeft(rect.left() + 2 + DisclosureOffset); - break; - } - case SE_TabWidgetLeftCorner: - if (const QStyleOptionTabWidgetFrame *twf - = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { - switch (twf->shape) { - case QStyleOptionTab::RoundedNorth: - case QStyleOptionTab::TriangularNorth: - rect = QRect(QPoint(0, 0), twf->leftCornerWidgetSize); - break; - case QStyleOptionTab::RoundedSouth: - case QStyleOptionTab::TriangularSouth: - rect = QRect(QPoint(0, twf->rect.height() - twf->leftCornerWidgetSize.height()), - twf->leftCornerWidgetSize); - break; - default: - break; - } - rect = visualRect(twf->direction, twf->rect, rect); - } - break; - case SE_TabWidgetRightCorner: - if (const QStyleOptionTabWidgetFrame *twf - = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { - switch (twf->shape) { - case QStyleOptionTab::RoundedNorth: - case QStyleOptionTab::TriangularNorth: - rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(), 0), - twf->rightCornerWidgetSize); - break; - case QStyleOptionTab::RoundedSouth: - case QStyleOptionTab::TriangularSouth: - rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(), - twf->rect.height() - twf->rightCornerWidgetSize.height()), - twf->rightCornerWidgetSize); - break; - default: - break; - } - rect = visualRect(twf->direction, twf->rect, rect); - } - break; - case SE_TabWidgetTabContents: - rect = QCommonStyle::subElementRect(sr, opt); - if (const auto *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { - if (twf->lineWidth != 0) { - switch (QMacStylePrivate::tabDirection(twf->shape)) { - case QMacStylePrivate::North: - rect.adjust(+1, +14, -1, -1); - break; - case QMacStylePrivate::South: - rect.adjust(+1, +1, -1, -14); - break; - case QMacStylePrivate::West: - rect.adjust(+14, +1, -1, -1); - break; - case QMacStylePrivate::East: - rect.adjust(+1, +1, -14, -1); - } - } - } - break; - case SE_TabBarTabText: - if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { - QRect dummyIconRect; - d->tabLayout(tab, &rect, &dummyIconRect); - } - break; - case SE_TabBarTabLeftButton: - case SE_TabBarTabRightButton: - if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { - bool selected = tab->state & State_Selected; - int verticalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftVertical, tab); - int horizontalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab); - int hpadding = 5; - - bool verticalTabs = tab->shape == QStyleOptionTab::RoundedEast - || tab->shape == QStyleOptionTab::RoundedWest - || tab->shape == QStyleOptionTab::TriangularEast - || tab->shape == QStyleOptionTab::TriangularWest; - - QRect tr = tab->rect; - if (tab->shape == QStyleOptionTab::RoundedSouth || tab->shape == QStyleOptionTab::TriangularSouth) - verticalShift = -verticalShift; - if (verticalTabs) { - qSwap(horizontalShift, verticalShift); - horizontalShift *= -1; - verticalShift *= -1; - } - if (tab->shape == QStyleOptionTab::RoundedWest || tab->shape == QStyleOptionTab::TriangularWest) - horizontalShift = -horizontalShift; - - tr.adjust(0, 0, horizontalShift, verticalShift); - if (selected) - { - tr.setBottom(tr.bottom() - verticalShift); - tr.setRight(tr.right() - horizontalShift); - } - - QSize size = (sr == SE_TabBarTabLeftButton) ? tab->leftButtonSize : tab->rightButtonSize; - int w = size.width(); - int h = size.height(); - int midHeight = static_cast<int>(qCeil(float(tr.height() - h) / 2)); - int midWidth = ((tr.width() - w) / 2); - - bool atTheTop = true; - switch (tab->shape) { - case QStyleOptionTab::RoundedWest: - case QStyleOptionTab::TriangularWest: - atTheTop = (sr == SE_TabBarTabLeftButton); - break; - case QStyleOptionTab::RoundedEast: - case QStyleOptionTab::TriangularEast: - atTheTop = (sr == SE_TabBarTabRightButton); - break; - default: - if (sr == SE_TabBarTabLeftButton) - rect = QRect(tab->rect.x() + hpadding, midHeight, w, h); - else - rect = QRect(tab->rect.right() - w - hpadding, midHeight, w, h); - rect = visualRect(tab->direction, tab->rect, rect); - } - if (verticalTabs) { - if (atTheTop) - rect = QRect(midWidth, tr.y() + tab->rect.height() - hpadding - h, w, h); - else - rect = QRect(midWidth, tr.y() + hpadding, w, h); - } - } - break; - case SE_LineEditContents: { - // From using pixelTool with XCode/NSTextTextField - int leftPadding = 4; - int rightPadding = 4; - int topPadding = 4; - int bottomPadding = 0; - - if (opt->state & QStyle::State_Small) { - topPadding = 3; - } else if (opt->state & QStyle::State_Mini) { - topPadding = 2; - } - - rect = QRect(leftPadding, topPadding, opt->rect.width() - leftPadding - rightPadding, - opt->rect.height() - topPadding - bottomPadding); - break; } - case SE_CheckBoxLayoutItem: - rect = opt->rect; - if (controlSize == QStyleHelper::SizeLarge) { - setLayoutItemMargins(+2, +2, -3, -2, &rect, opt->direction); - } else if (controlSize == QStyleHelper::SizeSmall) { - setLayoutItemMargins(+1, +2, -2, -1, &rect, opt->direction); - } else { - setLayoutItemMargins(-0, +0, -1, -0, &rect, opt->direction); - } - break; - case SE_ComboBoxLayoutItem: - if (const auto *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { - //#ifndef QT_NO_TOOLBAR - // if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) { - // // Do nothing, because QToolbar needs the entire widget rect. - // // Otherwise it will be clipped. Equivalent to - // // widget->setAttribute(Qt::WA_LayoutUsesWidgetRect), but without - // // all the hassle. - // } else - //#endif - if (combo->editable) - rect = LargeSmallMini(opt, - opt->rect.adjusted(5, 6, -6, -7), - opt->rect.adjusted(4, 4, -5, -7), - opt->rect.adjusted(5, 4, -4, -6)); - else - rect = LargeSmallMini(opt, - opt->rect.adjusted(6, 4, -7, -7), - opt->rect.adjusted(6, 7, -6, -5), - opt->rect.adjusted(9, 5, -5, -7)); - } - break; - case SE_LabelLayoutItem: - rect = opt->rect; - setLayoutItemMargins(+1, 0 /* SHOULD be -1, done for alignment */, 0, 0 /* SHOULD be -1, done for alignment */, &rect, opt->direction); - break; - case SE_ProgressBarLayoutItem: - if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { - const bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0); - rect = opt->rect; - - if (isIndeterminate) { - rect.adjust(1, 2, -1, -2); - } else { - rect.adjust(1, 1, -1, -2); - } - } - break; - case SE_PushButtonLayoutItem: - rect = opt->rect; - if (const QStyleOptionButton *buttonOpt = qstyleoption_cast<const QStyleOptionButton *>(opt)) { - if ((buttonOpt->features & QStyleOptionButton::Flat)) - break; - } - rect = LargeSmallMini(opt, - opt->rect.adjusted(7, 5, -7, -7), - opt->rect.adjusted(6, 6, -6, -6), - opt->rect.adjusted(6, 5, -6, -6)); - break; - case SE_SpinBoxLayoutItem: - rect = LargeSmallMini(opt, - opt->rect.adjusted(2, 3, -2, -2), - opt->rect.adjusted(2, 3, -2, -2), - opt->rect.adjusted(2, 3, -2, -2)); - break; - case SE_RadioButtonLayoutItem: - rect = LargeSmallMini(opt, - opt->rect.adjusted(2, 2, -3, -2), - opt->rect.adjusted(2, 2, -3, -2), - opt->rect.adjusted(1, 2, -3, -2)); - break; - case SE_SliderLayoutItem: - if (const QStyleOptionSlider *sliderOpt - = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - rect = opt->rect; - if (sliderOpt->subControls & QStyle::SC_SliderHandle) { - if (sliderOpt->tickPosition == QStyleOptionSlider::NoTicks) - rect.adjust(3, 3, -3, -3); - } else { - rect.adjust(3, 0, -3, 0); - } - } - break; - case SE_ScrollBarLayoutItem: - if (const QStyleOptionSlider *sliderOpt = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - rect = opt->rect; - } - case SE_FrameLayoutItem: - // hack because QStyleOptionFrame doesn't have a frameStyle member -// if (const QFrame *frame = qobject_cast<const QFrame *>(widget)) { -// rect = opt->rect; -// switch (frame->frameStyle() & QFrame::Shape_Mask) { -// case QFrame::HLine: -// rect.adjust(0, +1, 0, -1); -// break; -// case QFrame::VLine: -// rect.adjust(+1, 0, -1, 0); -// break; -// default: -// ; -// } -// } - break; - case SE_GroupBoxLayoutItem: - rect = opt->rect; - if (const QStyleOptionGroupBox *groupBoxOpt = - qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { - /* - AHIG is very inconsistent when it comes to group boxes. - Basically, we make sure that (non-checkable) group boxes - and tab widgets look good when laid out side by side. - */ - if (groupBoxOpt->subControls & (QStyle::SC_GroupBoxCheckBox - | QStyle::SC_GroupBoxLabel)) { - int delta; - if (groupBoxOpt->subControls & QStyle::SC_GroupBoxCheckBox) { - delta = SIZE(8, 4, 4); // guess - } else { - delta = SIZE(15, 12, 12); // guess - } - rect.setTop(rect.top() + delta); - } - } - rect.setBottom(rect.bottom() - 1); - break; - case SE_TabWidgetLayoutItem: - if (const QStyleOptionTabWidgetFrame *tabWidgetOpt = - qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { - /* - AHIG specifies "12 or 14" as the distance from the window - edge. We choose 14 and since the default top margin is 20, - the overlap is 6. - */ - rect = tabWidgetOpt->rect; - if (tabWidgetOpt->shape == QStyleOptionTab::RoundedNorth) - rect.setTop(rect.top() + SIZE(6 /* AHIG */, 3 /* guess */, 2 /* AHIG */)); - } - break; - case SE_DockWidgetCloseButton: - case SE_DockWidgetFloatButton: - case SE_DockWidgetTitleBarText: - case SE_DockWidgetIcon: { - int iconSize = proxy()->pixelMetric(PM_SmallIconSize, opt); - int buttonMargin = proxy()->pixelMetric(PM_DockWidgetTitleBarButtonMargin, opt); - QRect srect = opt->rect; - - const QStyleOptionDockWidget *dwOpt - = qstyleoption_cast<const QStyleOptionDockWidget*>(opt); - bool canClose = dwOpt == 0 ? true : dwOpt->closable; - bool canFloat = dwOpt == 0 ? false : dwOpt->floatable; - - const bool verticalTitleBar = dwOpt->verticalTitleBar; - - // If this is a vertical titlebar, we transpose and work as if it was - // horizontal, then transpose again. - if (verticalTitleBar) - srect = srect.transposed(); - - do { - int right = srect.right(); - int left = srect.left(); - - QRect closeRect; - if (canClose) { - QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, - opt).actualSize(QSize(iconSize, iconSize)); - sz += QSize(buttonMargin, buttonMargin); - if (verticalTitleBar) - sz = sz.transposed(); - closeRect = QRect(left, - srect.center().y() - sz.height()/2, - sz.width(), sz.height()); - left = closeRect.right() + 1; - } - if (sr == SE_DockWidgetCloseButton) { - rect = closeRect; - break; - } - - QRect floatRect; - if (canFloat) { - QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarNormalButton, - opt).actualSize(QSize(iconSize, iconSize)); - sz += QSize(buttonMargin, buttonMargin); - if (verticalTitleBar) - sz = sz.transposed(); - floatRect = QRect(left, - srect.center().y() - sz.height()/2, - sz.width(), sz.height()); - left = floatRect.right() + 1; - } - if (sr == SE_DockWidgetFloatButton) { - rect = floatRect; - break; - } - - QRect iconRect; -// if (const QDockWidget *dw = qobject_cast<const QDockWidget*>(widget)) { -// QIcon icon; -// if (dw->isFloating()) -// icon = dw->windowIcon(); -// if (!icon.isNull() -// && icon.cacheKey() != QApplication::windowIcon().cacheKey()) { -// QSize sz = icon.actualSize(QSize(rect.height(), rect.height())); -// if (verticalTitleBar) -// sz = sz.transposed(); -// iconRect = QRect(right - sz.width(), srect.center().y() - sz.height()/2, -// sz.width(), sz.height()); -// right = iconRect.left() - 1; -// } -// } - if (sr == SE_DockWidgetIcon) { - rect = iconRect; - break; - } - - QRect textRect = QRect(left, srect.top(), - right - left, srect.height()); - if (sr == SE_DockWidgetTitleBarText) { - rect = textRect; - break; - } - } while (false); - - if (verticalTitleBar) { - rect = QRect(srect.left() + rect.top() - srect.top(), - srect.top() + srect.right() - rect.right(), - rect.height(), rect.width()); - } else { - rect = visualRect(opt->direction, srect, rect); - } - break; - } - default: - rect = QCommonStyle::subElementRect(sr, opt); - break; - } - return rect; -} - -void QMacStylePrivate::drawToolbarButtonArrow(const QStyleOption *opt, QPainter *p) const -{ - 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 -{ - CGContextSaveGState(cg); - [NSGraphicsContext saveGraphicsState]; - - [NSGraphicsContext setCurrentContext: - [NSGraphicsContext graphicsContextWithCGContext:cg flipped:flipped]]; -} - -void QMacStylePrivate::restoreNSGraphicsContext(CGContextRef cg) const -{ - [NSGraphicsContext restoreGraphicsState]; - CGContextRestoreGState(cg); -} - -void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const -{ - Q_D(const QMacStyle); - const AppearanceSync sync; - - QMacCGContext cg(p); - d->resolveCurrentNSView(opt->window); - - switch (cc) { - case CC_ScrollBar: - if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - - const bool drawTrack = sb->subControls & SC_ScrollBarGroove; - const bool drawKnob = sb->subControls & SC_ScrollBarSlider; - if (!drawTrack && !drawKnob) - break; - - const bool isHorizontal = sb->orientation == Qt::Horizontal; - - if (opt && opt->styleObject && !QMacStylePrivate::scrollBars.contains(opt->styleObject)) - QMacStylePrivate::scrollBars.append(QPointer<QObject>(opt->styleObject)); - - static const CGFloat knobWidths[] = { 7.0, 5.0, 5.0 }; - static const CGFloat expandedKnobWidths[] = { 11.0, 9.0, 9.0 }; - const auto cocoaSize = d->effectiveAquaSizeConstrain(opt); - const CGFloat maxExpandScale = expandedKnobWidths[cocoaSize] / knobWidths[cocoaSize]; - - const bool isTransient = proxy()->styleHint(SH_ScrollBar_Transient, opt); -// if (!isTransient) -// d->stopAnimation(opt->styleObject); - bool wasActive = false; - CGFloat opacity = 0.0; - CGFloat expandScale = 1.0; - CGFloat expandOffset = 0.0; - bool shouldExpand = false; - - if (QObject *styleObject = opt->styleObject) { - const int oldPos = styleObject->property("_q_stylepos").toInt(); - const int oldMin = styleObject->property("_q_stylemin").toInt(); - const int oldMax = styleObject->property("_q_stylemax").toInt(); - const QRect oldRect = styleObject->property("_q_stylerect").toRect(); - const QStyle::State oldState = static_cast<QStyle::State>(styleObject->property("_q_stylestate").value<QStyle::State::Int>()); - const uint oldActiveControls = styleObject->property("_q_stylecontrols").toUInt(); - - // a scrollbar is transient when the scrollbar itself and - // its sibling are both inactive (ie. not pressed/hovered/moved) - const bool transient = isTransient && !opt->activeSubControls && !(sb->state & State_On); - - if (!transient || - oldPos != sb->sliderPosition || - oldMin != sb->minimum || - oldMax != sb->maximum || - oldRect != sb->rect || - oldState != sb->state || - oldActiveControls != sb->activeSubControls) { - - // if the scrollbar is transient or its attributes, geometry or - // state has changed, the opacity is reset back to 100% opaque - opacity = 1.0; - - styleObject->setProperty("_q_stylepos", sb->sliderPosition); - styleObject->setProperty("_q_stylemin", sb->minimum); - styleObject->setProperty("_q_stylemax", sb->maximum); - styleObject->setProperty("_q_stylerect", sb->rect); - styleObject->setProperty("_q_stylestate", static_cast<QStyle::State::Int>(sb->state)); - styleObject->setProperty("_q_stylecontrols", static_cast<uint>(sb->activeSubControls)); - -// QScrollbarStyleAnimation *anim = qobject_cast<QScrollbarStyleAnimation *>(d->animation(styleObject)); -// if (transient) { -// if (!anim) { -// anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Deactivating, styleObject); -// d->startAnimation(anim); -// } else if (anim->mode() == QScrollbarStyleAnimation::Deactivating) { -// // the scrollbar was already fading out while the -// // state changed -> restart the fade out animation -// anim->setCurrentTime(0); -// } -// } else if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) { -// d->stopAnimation(styleObject); -// } - } - -// QScrollbarStyleAnimation *anim = qobject_cast<QScrollbarStyleAnimation *>(d->animation(styleObject)); -// if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) { -// // once a scrollbar was active (hovered/pressed), it retains -// // the active look even if it's no longer active while fading out -// if (oldActiveControls) -// anim->setActive(true); - -// wasActive = anim->wasActive(); -// opacity = anim->currentValue(); -// } - - shouldExpand = isTransient && (opt->activeSubControls || wasActive); - if (shouldExpand) { -// if (!anim && !oldActiveControls) { -// // Start expand animation only once and when entering -// anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Activating, styleObject); -// d->startAnimation(anim); -// } -// if (anim && anim->mode() == QScrollbarStyleAnimation::Activating) { -// expandScale = 1.0 + (maxExpandScale - 1.0) * anim->currentValue(); -// expandOffset = 5.5 * (1.0 - anim->currentValue()); -// } else { -// // Keep expanded state after the animation ends, and when fading out -// expandScale = maxExpandScale; -// expandOffset = 0.0; -// } - } - } - - d->setupNSGraphicsContext(cg, NO /* flipped */); - - const auto controlType = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical; - const auto cw = QMacStylePrivate::CocoaControl(controlType, cocoaSize); - NSScroller *scroller = static_cast<NSScroller *>(d->cocoaControl(cw)); - - const QColor bgColor = QStyleHelper::backgroundColor(opt->palette); - const bool hasDarkBg = bgColor.red() < 128 && bgColor.green() < 128 && bgColor.blue() < 128; - if (isTransient) { - // macOS behavior: as soon as one color channel is >= 128, - // the background is considered bright, scroller is dark. - scroller.knobStyle = hasDarkBg? NSScrollerKnobStyleLight : NSScrollerKnobStyleDark; - } else { - scroller.knobStyle = NSScrollerKnobStyleDefault; - } - - scroller.scrollerStyle = isTransient ? NSScrollerStyleOverlay : NSScrollerStyleLegacy; - - if (!setupScroller(scroller, sb)) - break; - - if (isTransient) { - CGContextBeginTransparencyLayerWithRect(cg, scroller.frame, nullptr); - CGContextSetAlpha(cg, opacity); - } - - if (drawTrack) { - // Draw the track when hovering. Expand by shifting the track rect. - if (!isTransient || opt->activeSubControls || wasActive) { - CGRect trackRect = scroller.bounds; - if (isHorizontal) - trackRect.origin.y += expandOffset; - else - trackRect.origin.x += expandOffset; - [scroller drawKnobSlotInRect:trackRect highlight:NO]; - } - } - - if (drawKnob) { - if (shouldExpand) { - // -[NSScroller drawKnob] is not useful here because any scaling applied - // will only be used to draw the hi-DPI artwork. And even if did scale, - // the stretched knob would look wrong, actually. So we need to draw the - // scroller manually when it's being hovered. - const CGFloat scrollerWidth = [NSScroller scrollerWidthForControlSize:scroller.controlSize scrollerStyle:scroller.scrollerStyle]; - const CGFloat knobWidth = knobWidths[cocoaSize] * expandScale; - // Cocoa can help get the exact knob length in the current orientation - const CGRect scrollerKnobRect = CGRectInset([scroller rectForPart:NSScrollerKnob], 1, 1); - const CGFloat knobLength = isHorizontal ? scrollerKnobRect.size.width : scrollerKnobRect.size.height; - const CGFloat knobPos = isHorizontal ? scrollerKnobRect.origin.x : scrollerKnobRect.origin.y; - const CGFloat knobOffset = qRound((scrollerWidth + expandOffset - knobWidth) / 2.0); - const CGFloat knobRadius = knobWidth / 2.0; - CGRect knobRect; - if (isHorizontal) - knobRect = CGRectMake(knobPos, knobOffset, knobLength, knobWidth); - else - knobRect = CGRectMake(knobOffset, knobPos, knobWidth, knobLength); - QCFType<CGPathRef> knobPath = CGPathCreateWithRoundedRect(knobRect, knobRadius, knobRadius, nullptr); - CGContextAddPath(cg, knobPath); - CGContextSetAlpha(cg, 0.5); - CGColorRef knobColor = hasDarkBg ? NSColor.whiteColor.CGColor : NSColor.blackColor.CGColor; - CGContextSetFillColorWithColor(cg, knobColor); - CGContextFillPath(cg); - } else { - [scroller drawKnob]; - - if (!isTransient && opt->state & State_Sunken) { - // The knob should appear darker (going from 0.76 down to 0.49). - // But no blending mode can help darken enough in a single pass, - // so we resort to drawing the knob twice with a small help from - // blending. This brings the gray level to a close enough 0.53. - CGContextSetBlendMode(cg, kCGBlendModePlusDarker); - [scroller drawKnob]; - } - } - } - - if (isTransient) - CGContextEndTransparencyLayer(cg); - - d->restoreNSGraphicsContext(cg); - } - break; - case CC_Slider: - if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - const bool isHorizontal = sl->orientation == Qt::Horizontal; - const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical; - const auto cs = d->effectiveAquaSizeConstrain(opt); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); - if (!setupSlider(slider, sl)) - break; - - const bool hasTicks = sl->tickPosition != QStyleOptionSlider::NoTicks; - const bool hasDoubleTicks = sl->tickPosition == QStyleOptionSlider::TicksBothSides; - const bool drawKnob = sl->subControls & SC_SliderHandle; - const bool drawBar = sl->subControls & SC_SliderGroove; - const bool drawTicks = sl->subControls & SC_SliderTickmarks; - const bool isPressed = sl->state & State_Sunken; - - CGPoint pressPoint; - if (isPressed && drawKnob) { - const CGRect knobRect = [slider.cell knobRectFlipped:slider.isFlipped]; - pressPoint.x = CGRectGetMidX(knobRect); - pressPoint.y = CGRectGetMidY(knobRect); - [slider.cell startTrackingAt:pressPoint inView:slider]; - } - - d->drawNSViewInRect(slider, opt->rect, p, ^(CGContextRef, const CGRect &) { - // Note that we don't support drawing the slider upside down. When this - // is needed, simply set scale = -1 on the QML control / style item instead. - NSSliderCell *cell = slider.cell; - - if (drawBar) { - const CGRect barRect = [cell barRectFlipped:slider.isFlipped]; - // "flipped" will only make a difference when NSSliderCell is vertical. And then - // flipped means fill the groove from bottom-to-top instead of top-to-bottom. - // Bottom-to-top is QSlider's normal mode, which means that we always need to flip - // in vertical mode. (In case NSSlider can also be flipped horizontally in the future, - // we stay on the safe side, and only flip when in vertical mode). - [cell drawBarInside:barRect flipped:!isHorizontal]; - } - - if (drawBar && hasTicks && drawTicks) { - if (!hasDoubleTicks) { - [cell drawTickMarks]; - } else { - if (sl->orientation == Qt::Horizontal) { - slider.tickMarkPosition = NSTickMarkPositionAbove; - [slider layoutSubtreeIfNeeded]; - [cell drawTickMarks]; - slider.tickMarkPosition = NSTickMarkPositionBelow; - [slider layoutSubtreeIfNeeded]; - [cell drawTickMarks]; - } else { - slider.tickMarkPosition = NSTickMarkPositionLeading; - [slider layoutSubtreeIfNeeded]; - [cell drawTickMarks]; - slider.tickMarkPosition = NSTickMarkPositionTrailing; - [slider layoutSubtreeIfNeeded]; - [cell drawTickMarks]; - } - } - } - - if (drawKnob) - [cell drawKnob]; - }); - - if (isPressed && drawKnob) - [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO]; - } - break; - case CC_SpinBox: - if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { - if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) { - const auto lineEditRect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField); - QStyleOptionFrame frame; - static_cast<QStyleOption &>(frame) = *opt; - frame.rect = lineEditRect; - frame.state |= State_Sunken; - frame.lineWidth = 1; - frame.midLineWidth = 0; - frame.features = QStyleOptionFrame::None; - frame.frameShape = QStyleOptionFrame::Box; - drawPrimitive(PE_FrameLineEdit, &frame, p); - } - if (sb->subControls & (SC_SpinBoxUp | SC_SpinBoxDown)) { - const QRect updown = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp) - | proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown); - - d->setupNSGraphicsContext(cg, NO); - - const auto aquaSize = d->effectiveAquaSizeConstrain(opt); - const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Stepper, aquaSize); - NSStepperCell *cell = static_cast<NSStepperCell *>(d->cocoaCell(cw)); - cell.enabled = (sb->state & State_Enabled); - - const CGRect newRect = [cell drawingRectForBounds:updown.toCGRect()]; - - const bool upPressed = sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken); - const bool downPressed = sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken); - const CGFloat x = CGRectGetMidX(newRect); - const CGFloat y = upPressed ? -3 : 3; // Weird coordinate shift going on. Verified with Hopper - const CGPoint pressPoint = CGPointMake(x, y); - // Pretend we're pressing the mouse on the right button. Unfortunately, NSStepperCell has no - // API to highlight a specific button. The highlighted property works only on the down button. - if (upPressed || downPressed) - [cell startTrackingAt:pressPoint inView:d->backingStoreNSView]; - - [cell drawWithFrame:newRect inView:d->backingStoreNSView]; - - if (upPressed || downPressed) - [cell stopTracking:pressPoint at:pressPoint inView:d->backingStoreNSView mouseIsUp:NO]; - - d->restoreNSGraphicsContext(cg); - } - } - break; - case CC_ComboBox: - if (const auto *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { - const bool isEnabled = combo->state & State_Enabled; - const bool isPressed = combo->state & State_Sunken; - - const auto ct = cocoaControlType(combo); - const auto cs = d->effectiveAquaSizeConstrain(combo); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *cc = static_cast<NSControl *>(d->cocoaControl(cw)); - cc.enabled = isEnabled; - QRectF frameRect = cw.adjustedControlFrame(combo->rect);; - if (cw.type == QMacStylePrivate::Button_PopupButton) { - // Non-editable QComboBox - auto *pb = static_cast<NSPopUpButton *>(cc); - // FIXME Old offsets. Try to move to adjustedControlFrame() - if (cw.size == QStyleHelper::SizeSmall) { - frameRect = frameRect.translated(0, 1); - } else if (cw.size == QStyleHelper::SizeMini) { - // Same 0.5 pt misalignment as AppKit and fit the focus ring - frameRect = frameRect.translated(2, -0.5); - } - pb.frame = frameRect.toCGRect(); - [pb highlight:isPressed]; - d->drawNSViewInRect(pb, frameRect, p, ^(CGContextRef, const CGRect &r) { - QMacAutoReleasePool pool; - [pb.cell drawBezelWithFrame:r inView:pb.superview]; - }); - } else if (cw.type == QMacStylePrivate::ComboBox) { - // Editable QComboBox - auto *cb = static_cast<NSComboBox *>(cc); - const auto frameRect = cw.adjustedControlFrame(combo->rect); - cb.frame = frameRect.toCGRect(); - - // This API was requested to Apple in rdar #36197888. We know it's safe to use up to macOS 10.13.3 - if (NSButtonCell *cell = static_cast<NSButtonCell *>([cc.cell qt_valueForPrivateKey:@"_buttonCell"])) { - cell.highlighted = isPressed; - } else { - // TODO Render to pixmap and darken the button manually - } - - d->drawNSViewInRect(cb, frameRect, p, ^(CGContextRef, const CGRect &r) { - // FIXME This is usually drawn in the control's superview, but we wouldn't get inactive look in this case - QMacAutoReleasePool pool; - [cb.cell drawWithFrame:r inView:cb]; - }); - } - } - break; - case CC_TitleBar: - if (const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { - const bool isActive = (titlebar->state & State_Active) - && (titlebar->titleBarState & State_Active); - - p->fillRect(opt->rect, Qt::transparent); - p->setRenderHint(QPainter::Antialiasing); - p->setClipRect(opt->rect, Qt::IntersectClip); - - // FIXME A single drawPath() with 0-sized pen - // doesn't look as good as this double fillPath(). - const auto outerFrameRect = QRectF(opt->rect.adjusted(0, 0, 0, opt->rect.height())); - QPainterPath outerFramePath = d->windowPanelPath(outerFrameRect); - p->fillPath(outerFramePath, opt->palette.dark()); - - const auto frameAdjust = 1.0 / p->device()->devicePixelRatioF(); - 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()); - } - - if (titlebar->subControls & (SC_TitleBarCloseButton - | SC_TitleBarMaxButton - | SC_TitleBarMinButton - | SC_TitleBarNormalButton)) { - const bool isHovered = (titlebar->state & State_MouseOver); - static const SubControl buttons[] = { - SC_TitleBarCloseButton, SC_TitleBarMinButton, SC_TitleBarMaxButton - }; - for (const auto sc : buttons) { - const auto ct = d->windowButtonCocoaControl(sc); - const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge); - auto *wb = static_cast<NSButton *>(d->cocoaControl(cw)); - wb.enabled = (sc & titlebar->subControls) && isActive; - [wb highlight:(titlebar->state & State_Sunken) && (sc & titlebar->activeSubControls)]; - Q_UNUSED(isHovered); // FIXME No public API for this - - const auto buttonRect = proxy()->subControlRect(CC_TitleBar, titlebar, sc); - d->drawNSViewInRect(wb, buttonRect, p, ^(CGContextRef, const CGRect &rect) { - QMacAutoReleasePool pool; - auto *wbCell = static_cast<NSButtonCell *>(wb.cell); - [wbCell drawWithFrame:rect inView:wb]; - }); - } - } - - if (titlebar->subControls & SC_TitleBarLabel) { - const auto tr = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarLabel); - if (!titlebar->icon.isNull()) { - const auto iconExtent = proxy()->pixelMetric(PM_SmallIconSize); - const auto iconSize = QSize(iconExtent, iconExtent); - 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(opt->window, iconSize, QIcon::Normal)); - } - - if (!titlebar->text.isEmpty()) - drawItemText(p, tr, Qt::AlignCenter, opt->palette, isActive, titlebar->text, QPalette::Text); - } - } - break; - case CC_GroupBox: - if (const QStyleOptionGroupBox *gb - = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { - - QStyleOptionGroupBox groupBox(*gb); - const bool flat = groupBox.features & QStyleOptionFrame::Flat; - if (!flat) - groupBox.state |= QStyle::State_Mini; // Force mini-sized checkbox to go with small-sized label - else - groupBox.subControls = groupBox.subControls & ~SC_GroupBoxFrame; // We don't like frames and ugly lines - -// const bool didSetFont = widget && widget->testAttribute(Qt::WA_SetFont); -// const bool didModifySubControls = !didSetFont && QApplication::desktopSettingsAware(); -// if (didModifySubControls) -// groupBox.subControls = groupBox.subControls & ~SC_GroupBoxLabel; - QCommonStyle::drawComplexControl(cc, &groupBox, p); -// if (didModifySubControls) { -// const QRect rect = proxy()->subControlRect(CC_GroupBox, &groupBox, SC_GroupBoxLabel); -// 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); -// proxy()->drawItemText(p, rect, alignment, groupBox.palette, groupBox.state & State_Enabled, groupBox.text, QPalette::WindowText); -// if (!flat) -// p->setFont(savedFont); -// } - } - break; - case CC_ToolButton: - if (const QStyleOptionToolButton *tb - = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { -#ifndef QT_NO_ACCESSIBILITY - if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { - if (tb->subControls & SC_ToolButtonMenu) { - QStyleOption arrowOpt = *tb; - arrowOpt.rect = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu); - arrowOpt.rect.setY(arrowOpt.rect.y() + arrowOpt.rect.height() / 2); - arrowOpt.rect.setHeight(arrowOpt.rect.height() / 2); - proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p); - } else if ((tb->features & QStyleOptionToolButton::HasMenu) - && (tb->toolButtonStyle != Qt::ToolButtonTextOnly && !tb->icon.isNull())) { - d->drawToolbarButtonArrow(tb, p); - } - if (tb->state & State_On) { - NSView *view = reinterpret_cast<NSView *>(opt->window->winId()); - bool isKey = false; - if (view) - isKey = [view.window isKeyWindow]; - - QBrush brush(brushForToolButton(isKey)); - QPainterPath path; - path.addRoundedRect(QRectF(tb->rect.x(), tb->rect.y(), tb->rect.width(), tb->rect.height() + 4), 4, 4); - p->setRenderHint(QPainter::Antialiasing); - p->fillPath(path, brush); - } - proxy()->drawControl(CE_ToolButtonLabel, opt, p); - } else -#endif // QT_NO_ACCESSIBILITY - { - 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)) { - 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); - 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); - d->drawNSViewInRect(pb, buttonRect, p, ^(CGContextRef, const CGRect &rect) { - QMacAutoReleasePool pool; - [pb.cell drawBezelWithFrame:rect inView:pb]; - }); - } - } - - if (tb->subControls & SC_ToolButtonMenu) { - const auto menuRect = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu); - QStyleOption arrowOpt = *tb; - arrowOpt.rect = QRect(menuRect.x() + ((menuRect.width() - toolButtonArrowSize) / 2), - menuRect.height() - (toolButtonArrowSize + toolButtonArrowMargin), - toolButtonArrowSize, - toolButtonArrowSize); - proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p); - } else if (tb->features & QStyleOptionToolButton::HasMenu) { - d->drawToolbarButtonArrow(tb, p); - } - QRect buttonRect = proxy()->subControlRect(CC_ToolButton, tb, SC_ToolButton); - int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt); - QStyleOptionToolButton label = *tb; - label.rect = buttonRect.adjusted(fw, fw, -fw, -fw); - proxy()->drawControl(CE_ToolButtonLabel, &label, p); - } - } - break; - case CC_Dial: - if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt)) - QStyleHelper::drawDial(dial, p); - break; - default: - QCommonStyle::drawComplexControl(cc, opt, p); - break; - } -} - -QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) const -{ - Q_D(const QMacStyle); - - SubControl sc = QStyle::SC_None; - - switch (cc) { - case CC_ComboBox: - if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { - sc = QCommonStyle::hitTestComplexControl(cc, cmb, pt); - if (!cmb->editable && sc != QStyle::SC_None) - sc = SC_ComboBoxArrow; // A bit of a lie, but what we want - } - break; - case CC_Slider: - if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - if (!sl->rect.contains(pt)) - break; - - const bool hasTicks = sl->tickPosition != QStyleOptionSlider::NoTicks; - const bool isHorizontal = sl->orientation == Qt::Horizontal; - const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical; - const auto cs = d->effectiveAquaSizeConstrain(opt); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); - if (!setupSlider(slider, sl)) - break; - - NSSliderCell *cell = slider.cell; - 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)) { - sc = SC_SliderGroove; - } else if (hasTicks) { - sc = SC_SliderTickmarks; - } - } - break; - case CC_ScrollBar: - if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - if (!sb->rect.contains(pt)) { - sc = SC_None; - break; - } - - const bool isHorizontal = sb->orientation == Qt::Horizontal; - const auto ct = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical; - const auto cs = d->effectiveAquaSizeConstrain(opt); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *scroller = static_cast<NSScroller *>(d->cocoaControl(cw)); - if (!setupScroller(scroller, sb)) { - sc = SC_None; - break; - } - - // Since -[NSScroller testPart:] doesn't want to cooperate, we do it the - // straightforward way. In any case, macOS doesn't return line-sized changes - // with NSScroller since 10.7, according to the aforementioned method's doc. - const auto knobRect = QRectF::fromCGRect([scroller rectForPart:NSScrollerKnob]); - if (isHorizontal) { - const bool isReverse = sb->direction == Qt::RightToLeft; - if (pt.x() < knobRect.left()) - sc = isReverse ? SC_ScrollBarAddPage : SC_ScrollBarSubPage; - else if (pt.x() > knobRect.right()) - sc = isReverse ? SC_ScrollBarSubPage : SC_ScrollBarAddPage; - else - sc = SC_ScrollBarSlider; - } else { - if (pt.y() < knobRect.top()) - sc = SC_ScrollBarSubPage; - else if (pt.y() > knobRect.bottom()) - sc = SC_ScrollBarAddPage; - else - sc = SC_ScrollBarSlider; - } - } - break; - default: - sc = QCommonStyle::hitTestComplexControl(cc, opt, pt); - break; - } - return sc; -} - -QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc) const -{ - Q_D(const QMacStyle); - - QRect ret; - - switch (cc) { - case CC_ScrollBar: - if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - const bool isHorizontal = sb->orientation == Qt::Horizontal; - const bool isReverseHorizontal = isHorizontal && (sb->direction == Qt::RightToLeft); - - NSScrollerPart part = NSScrollerNoPart; - if (sc == SC_ScrollBarSlider) { - part = NSScrollerKnob; - } else if (sc == SC_ScrollBarGroove) { - part = NSScrollerKnobSlot; - } else if (sc == SC_ScrollBarSubPage || sc == SC_ScrollBarAddPage) { - if ((!isReverseHorizontal && sc == SC_ScrollBarSubPage) - || (isReverseHorizontal && sc == SC_ScrollBarAddPage)) - part = NSScrollerDecrementPage; - else - part = NSScrollerIncrementPage; - } - // And nothing else since 10.7 - - if (part != NSScrollerNoPart) { - const auto ct = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical; - const auto cs = d->effectiveAquaSizeConstrain(opt); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *scroller = static_cast<NSScroller *>(d->cocoaControl(cw)); - if (setupScroller(scroller, sb)) - ret = QRectF::fromCGRect([scroller rectForPart:part]).toRect(); - } - } - break; - case CC_Slider: - if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - const bool hasTicks = sl->tickPosition != QStyleOptionSlider::NoTicks; - const bool isHorizontal = sl->orientation == Qt::Horizontal; - const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical; - const auto cs = d->effectiveAquaSizeConstrain(opt); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); - if (!setupSlider(slider, sl)) - break; - - NSSliderCell *cell = slider.cell; - if (sc == SC_SliderHandle) { - ret = QRectF::fromCGRect([cell knobRectFlipped:slider.isFlipped]).toRect(); - } else if (sc == SC_SliderGroove) { - ret = QRectF::fromCGRect([cell barRectFlipped:slider.isFlipped]).toRect(); - } else if (hasTicks && sc == SC_SliderTickmarks) { - const auto tickMarkRect = QRectF::fromCGRect([cell rectOfTickMarkAtIndex:0]); - if (isHorizontal) - ret = QRect(sl->rect.left(), tickMarkRect.top(), sl->rect.width(), tickMarkRect.height()); - else - ret = QRect(tickMarkRect.left(), sl->rect.top(), tickMarkRect.width(), sl->rect.height()); - } - -// if (sl->upsideDown) { -// if isHorizontal) { -// } else { -// } -// } - } - break; - case CC_TitleBar: - if (const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { - // The title bar layout is as follows: close, min, zoom, icon, title - // [ x _ + @ Window Title ] - // Center the icon and title until it starts to overlap with the buttons. - // The icon doesn't count towards SC_TitleBarLabel, but it's still rendered - // next to the title text. See drawComplexControl(). - if (sc == SC_TitleBarLabel) { - qreal labelWidth = titlebar->fontMetrics.horizontalAdvance(titlebar->text) + 1; // FIXME Rounding error? - qreal labelHeight = titlebar->fontMetrics.height(); - - const auto lastButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarMaxButton); - 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();; - controlsSpacing += actualIconSize + titleBarIconTitleSpacing; - } - - const qreal labelPos = qMax(controlsSpacing, (opt->rect.width() - labelWidth) / 2.0); - labelWidth = qMin(labelWidth, opt->rect.width() - (labelPos + titleBarTitleRightMargin)); - ret = QRect(labelPos, (opt->rect.height() - labelHeight) / 2, - labelWidth, labelHeight); - } else { - const auto currentButton = d->windowButtonCocoaControl(sc); - if (currentButton == QMacStylePrivate::NoControl) - break; - - QPointF buttonPos = titlebar->rect.topLeft() + QPointF(titleBarButtonSpacing, 0); - QSizeF buttonSize; - for (int ct = QMacStylePrivate::Button_WindowClose; ct <= currentButton; ct++) { - const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::CocoaControlType(ct), - QStyleHelper::SizeLarge); - auto *wb = static_cast<NSButton *>(d->cocoaControl(cw)); - if (ct == currentButton) - buttonSize = QSizeF::fromCGSize(wb.frame.size); - else - buttonPos.rx() += wb.frame.size.width + titleBarButtonSpacing; - } - - const auto vOffset = (opt->rect.height() - buttonSize.height()) / 2.0; - ret = QRectF(buttonPos, buttonSize).translated(0, vOffset).toRect(); - } - } - break; - case CC_ComboBox: - if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { - const auto ct = cocoaControlType(combo); - const auto cs = d->effectiveAquaSizeConstrain(combo); - const auto cw = QMacStylePrivate::CocoaControl(ct, cs); - - // Old widget path. Current not understood why it's needed: - //const auto editRect = QMacStylePrivate::comboboxEditBounds(cw.adjustedControlFrame(combo->rect), cw); - - // New path: - QRectF editRect; - switch (cs) { - case QStyleHelper::SizeLarge: - editRect = combo->rect.adjusted(15, 7, -25, -9); - break; - case QStyleHelper::SizeSmall: - if (combo->editable) - editRect = combo->rect.adjusted(15, 6, -22, -9); - else - editRect = combo->rect.adjusted(15, 8, -22, -6); - break; - default: - if (combo->editable) - editRect = combo->rect.adjusted(15, 6, -20, -7); - else - editRect = combo->rect.adjusted(15, 5, -22, -6); - break; - } - - switch (sc) { - case SC_ComboBoxEditField:{ - ret = editRect.toAlignedRect(); - break; } - case SC_ComboBoxArrow:{ - ret = editRect.toAlignedRect(); - ret.setX(ret.x() + ret.width()); - ret.setWidth(combo->rect.right() - ret.right()); - break; } - case SC_ComboBoxListBoxPopup:{ - if (combo->editable) { - const CGRect inner = QMacStylePrivate::comboboxInnerBounds(combo->rect.toCGRect(), cw); - const int comboTop = combo->rect.top(); - ret = QRect(qRound(inner.origin.x), - comboTop, - qRound(inner.origin.x - combo->rect.left() + inner.size.width), - editRect.bottom() - comboTop + 2); - } else { - ret = QRect(combo->rect.x() + 4 - 11, - combo->rect.y() + 1, - editRect.width() + 10 + 11, - 1); - } - break; } - default: - break; - } - } - break; - case CC_GroupBox: - if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { - bool checkable = groupBox->subControls & SC_GroupBoxCheckBox; - const bool flat = groupBox->features & QStyleOptionFrame::Flat; - bool hasNoText = !checkable && groupBox->text.isEmpty(); - switch (sc) { - case SC_GroupBoxLabel: - case SC_GroupBoxCheckBox: { - // Cheat and use the smaller font if we need to - const bool checkable = groupBox->subControls & SC_GroupBoxCheckBox; - const bool fontIsSet = false; -// const bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont)) -// || !QApplication::desktopSettingsAware(); - 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 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()); - ret.setHeight(h); - - QRect labelRect = alignedRect(groupBox->direction, groupBox->textAlignment, - QSize(tw, h), ret); - if (flat && checkable) - labelRect.moveLeft(labelRect.left() + 4); - int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, opt); - bool rtl = groupBox->direction == Qt::RightToLeft; - if (sc == SC_GroupBoxLabel) { - if (checkable) { - int newSum = indicatorWidth + 1; - int newLeft = labelRect.left() + (rtl ? -newSum : newSum); - labelRect.moveLeft(newLeft); - if (flat) - labelRect.moveTop(labelRect.top() + 3); - else - labelRect.moveTop(labelRect.top() + 4); - } else if (flat) { - int newLeft = labelRect.left() - (rtl ? 3 : -3); - labelRect.moveLeft(newLeft); - labelRect.moveTop(labelRect.top() + 3); - } else { - int newLeft = labelRect.left() - (rtl ? 3 : 2); - labelRect.moveLeft(newLeft); - labelRect.moveTop(labelRect.top() + 4); - } - ret = labelRect; - } - - if (sc == SC_GroupBoxCheckBox) { - int left = rtl ? labelRect.right() - indicatorWidth : labelRect.left() - 1; - int top = flat ? ret.top() + 1 : ret.top() + 5; - ret.setRect(left, top, - indicatorWidth, proxy()->pixelMetric(PM_IndicatorHeight, opt)); - } - break; - } - case SC_GroupBoxContents: - case SC_GroupBoxFrame: { - QFontMetrics fm = groupBox->fontMetrics; - int yOffset = 3; - if (!flat) - yOffset = 5; - - if (hasNoText) - yOffset = -qCeil(QFontMetricsF(fm).height()); - ret = opt->rect.adjusted(0, qCeil(QFontMetricsF(fm).height()) + yOffset, 0, 0); - if (sc == SC_GroupBoxContents) { - if (flat) - ret.adjust(3, -5, -3, -4); // guess too - else - ret.adjust(3, 3, -3, -4); // guess - } - } - break; - default: - ret = QCommonStyle::subControlRect(cc, groupBox, sc); - break; - } - } - break; - case CC_SpinBox: - if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { - QStyleHelper::WidgetSizePolicy aquaSize = d->effectiveAquaSizeConstrain(spin); - const auto fw = proxy()->pixelMetric(PM_SpinBoxFrameWidth, spin); - int spinner_w; - int spinner_h; - int adjust_y; - int spinBoxSep; - switch (aquaSize) { - case QStyleHelper::SizeLarge: - spinner_w = 14; - spinner_h = 24; - adjust_y = -1; - spinBoxSep = 2; - break; - case QStyleHelper::SizeSmall: - spinner_w = 12; - spinner_h = 20; - adjust_y = -1; - spinBoxSep = 2; - break; - case QStyleHelper::SizeMini: - spinner_w = 10; - spinner_h = 16; - adjust_y = -1; - spinBoxSep = 1; - break; - default: - Q_UNREACHABLE(); - } - - switch (sc) { - case SC_SpinBoxUp: - case SC_SpinBoxDown: { - if (spin->buttonSymbols == QStyleOptionSpinBox::NoButtons) - break; - - const int y = fw; - const int x = spin->rect.width() - spinner_w; - ret.setRect(x + spin->rect.x(), y + spin->rect.y(), spinner_w, spinner_h); - - const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Stepper, aquaSize); - NSStepperCell *cell = static_cast<NSStepperCell *>(d->cocoaCell(cw)); - const CGRect outRect = [cell drawingRectForBounds:ret.toCGRect()]; - ret = QRectF::fromCGRect(outRect).toRect(); - - switch (sc) { - case SC_SpinBoxUp: - ret.setHeight(ret.height() / 2); - break; - case SC_SpinBoxDown: - ret.setY(ret.y() + ret.height() / 2); - break; - default: - Q_ASSERT(0); - break; - } - // The buttons are drawn with a top-margin (for some reason) into - // the rect. So undo that margin here: - ret.translate(0, adjust_y); - ret = visualRect(spin->direction, spin->rect, ret); - break; - } - case SC_SpinBoxEditField: - ret = spin->rect.adjusted(fw, fw, -fw, -fw); - if (spin->subControls & SC_SpinBoxUp || spin->subControls & SC_SpinBoxDown) { - ret.setWidth(spin->rect.width() - spinBoxSep - spinner_w); - ret = visualRect(spin->direction, spin->rect, ret); - } - break; - default: - ret = QCommonStyle::subControlRect(cc, spin, sc); - break; - } - } - break; - case CC_ToolButton: - ret = QCommonStyle::subControlRect(cc, opt, sc); - if (sc == SC_ToolButtonMenu) { -#ifndef QT_NO_ACCESSIBILITY - if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) - ret.adjust(-toolButtonArrowMargin, 0, 0, 0); -#endif - ret.adjust(-1, 0, 0, 0); - } - break; - default: - ret = QCommonStyle::subControlRect(cc, opt, sc); - break; - } - return ret; -} - -QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &csz) const -{ - Q_D(const QMacStyle); - - QSize sz(csz); - bool useAquaGuideline = true; - - switch (ct) { - case CT_SpinBox: - if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { - if (vopt->subControls == SC_SpinBoxFrame) { - const QSize minimumSize(20, 24); - if (sz.width() < minimumSize.width()) - sz.setWidth(minimumSize.width()); - if (sz.height() < minimumSize.height()) - sz.setHeight(minimumSize.height()); - } else { - const QSize buttonSize = proxy()->subControlRect(CC_SpinBox, vopt, SC_SpinBoxUp).size(); - const int upAndDownTogetherHeight = buttonSize.height() * 2; - sz += QSize(buttonSize.width(), upAndDownTogetherHeight); - } - } - break; - case QStyle::CT_TabWidget: - // the size between the pane and the "contentsRect" (+4,+4) - // (the "contentsRect" is on the inside of the pane) - sz = QCommonStyle::sizeFromContents(ct, opt, csz); - /** - This is supposed to show the relationship between the tabBar and - the stack widget of a QTabWidget. - Unfortunately ascii is not a good way of representing graphics..... - PS: The '=' line is the painted frame. - - top ---+ - | - | - | - | vvv just outside the painted frame is the "pane" - - -|- - - - - - - - - - <-+ - TAB BAR +=====^============ | +2 pixels - - - -|- - -|- - - - - - - <-+ - | | ^ ^^^ just inside the painted frame is the "contentsRect" - | | | - | overlap | - | | | - bottom ------+ <-+ +14 pixels - | - v - ------------------------------ <- top of stack widget - - - To summarize: - * 2 is the distance between the pane and the contentsRect - * The 14 and the 1's are the distance from the contentsRect to the stack widget. - (same value as used in SE_TabWidgetTabContents) - * overlap is how much the pane should overlap the tab bar - */ - // then add the size between the stackwidget and the "contentsRect" - if (const QStyleOptionTabWidgetFrame *twf - = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { - QSize extra(0,0); - const int overlap = pixelMetric(PM_TabBarBaseOverlap, opt); - const int gapBetweenTabbarAndStackWidget = 2 + 14 - overlap; - - const auto tabDirection = QMacStylePrivate::tabDirection(twf->shape); - if (tabDirection == QMacStylePrivate::North - || tabDirection == QMacStylePrivate::South) { - extra = QSize(2, gapBetweenTabbarAndStackWidget + 1); - } else { - extra = QSize(gapBetweenTabbarAndStackWidget + 1, 2); - } - sz+= extra; - } - break; - case QStyle::CT_TabBarTab: - if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { -// const bool differentFont = (widget && widget->testAttribute(Qt::WA_SetFont)) -// || !QApplication::desktopSettingsAware(); - const bool differentFont = false; - const auto tabDirection = QMacStylePrivate::tabDirection(tab->shape); - const bool verticalTabs = tabDirection == QMacStylePrivate::East - || tabDirection == QMacStylePrivate::West; - if (verticalTabs) - sz = sz.transposed(); - - int defaultTabHeight; - const auto cs = d->effectiveAquaSizeConstrain(opt); - switch (cs) { - case QStyleHelper::SizeLarge: - if (tab->documentMode) - defaultTabHeight = 24; - else - defaultTabHeight = 21; - break; - case QStyleHelper::SizeSmall: - defaultTabHeight = 18; - break; - case QStyleHelper::SizeMini: - defaultTabHeight = 16; - break; - default: - break; - } - - const bool widthSet = !differentFont && tab->icon.isNull(); - if (widthSet) { - const auto textSize = opt->fontMetrics.size(Qt::TextShowMnemonic, tab->text); - sz.rwidth() = textSize.width(); - sz.rheight() = qMax(defaultTabHeight, textSize.height()); - } else { - sz.rheight() = qMax(defaultTabHeight, sz.height()); - } - sz.rwidth() += proxy()->pixelMetric(PM_TabBarTabHSpace, tab); - - if (verticalTabs) - sz = sz.transposed(); - - int maxWidgetHeight = qMax(tab->leftButtonSize.height(), tab->rightButtonSize.height()); - int maxWidgetWidth = qMax(tab->leftButtonSize.width(), tab->rightButtonSize.width()); - - int widgetWidth = 0; - int widgetHeight = 0; - int padding = 0; - if (tab->leftButtonSize.isValid()) { - padding += 8; - widgetWidth += tab->leftButtonSize.width(); - widgetHeight += tab->leftButtonSize.height(); - } - if (tab->rightButtonSize.isValid()) { - padding += 8; - widgetWidth += tab->rightButtonSize.width(); - widgetHeight += tab->rightButtonSize.height(); - } - - if (verticalTabs) { - sz.setWidth(qMax(sz.width(), maxWidgetWidth)); - sz.setHeight(sz.height() + widgetHeight + padding); - } else { - if (widthSet) - sz.setWidth(sz.width() + widgetWidth + padding); - sz.setHeight(qMax(sz.height(), maxWidgetHeight)); - } - } - break; - case CT_LineEdit: - if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { - if (sz.isEmpty()) { - // Minimum size (with padding: 18x24) - sz.rwidth() = 10; - sz.rheight() = 20; - } - // From using pixelTool with XCode/NSTextTextField - int leftPadding = 4; - int rightPadding = 4; - int topPadding = 4; - int bottomPadding = 0; - - if (opt->state & QStyle::State_Small) { - topPadding = 3; - } else if (opt->state & QStyle::State_Mini) { - topPadding = 2; - } - - sz.rwidth() += leftPadding + rightPadding; - sz.rheight() += topPadding + bottomPadding; - } - break; - case QStyle::CT_PushButton: { - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) - if (btn->features & QStyleOptionButton::CommandLinkButton) - return QCommonStyle::sizeFromContents(ct, opt, sz); - - // 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, 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.setHeight(pushButtonDefaultHeight[controlSize]); - break; - } - case QStyle::CT_MenuItem: - if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { - int maxpmw = mi->maxIconWidth; - int w = sz.width(); - int h = sz.height(); - -//#if QT_CONFIG(combobox) -// const QComboBox *comboBox = qobject_cast<const QComboBox *>(widget); -//#endif - - if (mi->menuItemType == QStyleOptionMenuItem::Separator) { - w = 10; - h = qt_mac_aqua_get_metric(MenuSeparatorHeight); - } else { - h = mi->fontMetrics.height() + 2; - if (!mi->icon.isNull()) { -//#if QT_CONFIG(combobox) -// if (comboBox) { -// const QSize &iconSize = comboBox->iconSize(); -// h = qMax(h, iconSize.height() + 4); -// maxpmw = qMax(maxpmw, iconSize.width()); -// } else -//#endif - { - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); - h = qMax(h, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4); - } - } - } - if (mi->text.contains(QLatin1Char('\t'))) - w += 12; - else if (mi->menuItemType == QStyleOptionMenuItem::SubMenu) - w += 35; // Not quite exactly as it seems to depend on other factors - if (maxpmw) - w += maxpmw + 6; - // add space for a check. All items have place for a check too. - w += 20; -// if (comboBox && comboBox->isVisible()) { -// QStyleOptionComboBox cmb; -// cmb.initFrom(comboBox); -// cmb.editable = false; -// cmb.subControls = QStyle::SC_ComboBoxEditField; -// cmb.activeSubControls = QStyle::SC_None; -// w = qMax(w, subControlRect(QStyle::CC_ComboBox, &cmb, -// QStyle::SC_ComboBoxEditField, -// comboBox).width()); -// } else { -// w += 12; - sz = QSize(w, h); - } break; - case CT_MenuBarItem: - if (!sz.isEmpty()) - sz += QSize(12, 4); // Constants from QWindowsStyle - break; - case CT_ToolButton: - sz.rwidth() += 10; - sz.rheight() += 10; - if (const auto *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) - if (tb->features & QStyleOptionToolButton::Menu) - sz.rwidth() += toolButtonArrowMargin; - return sz; - case CT_ComboBox: - if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { - const int controlSize = getControlSize(opt); - - if (!cb->editable) { - if (sz.width() < 10) { - // minimumSize (to ensure a nice nine patch image) - sz.rwidth() += 10; - } - // Same as CT_PushButton, because we have to fit the focus - // ring and a non-editable combo box is a NSPopUpButton. - sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset; - - if (controlSize == QStyleHelper::SizeLarge) { - sz.rwidth() += 30; - } else if (controlSize == QStyleHelper::SizeSmall) { - sz.rwidth() += 26; - } else { - sz.rwidth() += 21; - } - } else { - sz.rwidth() += 50; // FIXME Double check this - } - - // This should be enough to fit the focus ring - if (controlSize == QStyleHelper::SizeMini) - sz.setHeight(24); // FIXME Our previous HITheme-based logic returned this for CT_PushButton. - else - sz.setHeight(pushButtonDefaultHeight[controlSize]); - - return sz; - } - break; - case CT_Menu: { - if (proxy() == this) { - sz = csz; - } else { - QStyleHintReturnMask menuMask; - QStyleOption myOption = *opt; - myOption.rect.setSize(sz); - if (proxy()->styleHint(SH_Menu_Mask, &myOption, &menuMask)) - sz = menuMask.region.boundingRect().size(); - } - break; } - case CT_HeaderSection:{ - const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt); - sz = QCommonStyle::sizeFromContents(ct, opt, csz); - if (header->text.contains(QLatin1Char('\n'))) - useAquaGuideline = false; - break; } - case CT_ScrollBar : - // Make sure that the scroll bar is large enough to display the thumb indicator. - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - const int minimumWidth = 24; - const int absoluteHeight = 14; - if (slider->orientation == Qt::Horizontal) { - sz = sz.expandedTo(QSize(minimumWidth, sz.height())); - sz.setHeight(absoluteHeight); - } else { - sz = sz.expandedTo(QSize(sz.width(), minimumWidth)); - sz.setWidth(absoluteHeight); - } - } - break; - case CT_ItemViewItem: - if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { - sz = QCommonStyle::sizeFromContents(ct, vopt, csz); - sz.setHeight(sz.height() + 2); - } - break; - default: - sz = QCommonStyle::sizeFromContents(ct, opt, csz); - } - - if (useAquaGuideline && ct != CT_PushButton) { - // TODO Probably going away at some point - QSize macsz; - if (d->aquaSizeConstrain(opt, ct, sz, &macsz) != QStyleHelper::SizeDefault) { - if (macsz.width() != -1) - sz.setWidth(macsz.width()); - if (macsz.height() != -1) - sz.setHeight(macsz.height()); - } - } - - // The sizes that Carbon and the guidelines gives us excludes the focus frame. - // We compensate for this by adding some extra space here to make room for the frame when drawing: - if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){ - if (combo->editable) { - const auto widgetSize = d->aquaSizeConstrain(opt); - QMacStylePrivate::CocoaControl cw; - 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); - } - } - return sz; -} - -QFont QMacStyle::font(QStyle::ControlElement element, const QStyle::State state) const -{ - QFont font = QCommonStyle::font(element, state); - - if (state & QStyle::State_Small) { - font.setPixelSize(11); - } else if (state & QStyle::State_Mini) { - font.setPixelSize(9); - } - - return font; -} - -QMargins QMacStyle::ninePatchMargins(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const -{ - QMargins margins; - - switch (cc) { - case CC_ComboBox: { - const QRect arrow = subControlRect(CC_ComboBox, opt, SC_ComboBoxArrow); - margins = QMargins(10, 0, arrow.width() + 1, -1); - break; } - default: - margins = QCommonStyle::ninePatchMargins(cc, opt, imageSize); - break; - } - - return margins; -} - -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) - flags |= Qt::TextHideMnemonic; - QCommonStyle::drawItemText(p, r, flags, pal, enabled, text, textRole); -} - -QIcon QMacStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt) const -{ - switch (standardIcon) { - default: - return QCommonStyle::standardIcon(standardIcon, opt); - case SP_ToolBarHorizontalExtensionButton: - case SP_ToolBarVerticalExtensionButton: { - QPixmap pixmap(QLatin1String(":/qt-project.org/styles/macstyle/images/toolbar-ext.png")); - if (standardIcon == SP_ToolBarVerticalExtensionButton) { - QPixmap pix2(pixmap.height(), pixmap.width()); - pix2.setDevicePixelRatio(pixmap.devicePixelRatio()); - pix2.fill(Qt::transparent); - QPainter p(&pix2); - p.translate(pix2.width(), 0); - p.rotate(90); - p.drawPixmap(0, 0, pixmap); - return pix2; - } - return pixmap; - } - } -} - -} // QQC2_NAMESPACE - -QT_END_NAMESPACE diff --git a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p.h b/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p.h deleted file mode 100644 index b9e1c5f3..00000000 --- a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMACSTYLE_MAC_P_H -#define QMACSTYLE_MAC_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickcommonstyle.h" - -#define QQC2_NAMESPACE QQC2 - -QT_BEGIN_NAMESPACE - -class QPalette; - -namespace QQC2_NAMESPACE { - -class QStyleOptionButton; -class QMacStylePrivate; - -class QMacStyle : public QCommonStyle -{ - Q_OBJECT -public: - QMacStyle(); - ~QMacStyle(); - - void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const override; - void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p) const override; - void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const override; - - QRect subElementRect(SubElement r, const QStyleOption *opt) const override; - QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc) const override; - SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) const override; - - QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize) const override; - QFont font(ControlElement element, const QStyle::State state) const override; - QMargins ninePatchMargins(ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const override; - - int pixelMetric(PixelMetric pm, const QStyleOption *opt = 0) const override; - int styleHint(StyleHint sh, const QStyleOption *opt = 0, QStyleHintReturn *shret = 0) const override; - - QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt) const override; - QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const override; - - void drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal, - bool enabled, const QString &text, - QPalette::ColorRole textRole = QPalette::NoRole) const override; - - QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *opt = 0) const override; - -private: - Q_DISABLE_COPY_MOVE(QMacStyle) - Q_DECLARE_PRIVATE(QMacStyle) -}; - -} // QQC2_NAMESPACE - -QT_END_NAMESPACE - -#endif // QMACSTYLE_MAC_P_H diff --git a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p_p.h b/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p_p.h deleted file mode 100644 index a0c67bfb..00000000 --- a/src/quicknativestyle/qstyle/mac/qquickmacstyle_mac_p_p.h +++ /dev/null @@ -1,230 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QMACSTYLE_MAC_P_P_H -#define QMACSTYLE_MAC_P_P_H - -#include "qquickmacstyle_mac_p.h" -#include "qquickcommonstyle_p.h" -#include "qquickstylehelper_p.h" - -#include <QtCore/qdebug.h> -#include <QtCore/qhash.h> -#include <QtCore/qmap.h> -#include <QtCore/qmath.h> -#include <QtCore/qpair.h> -#include <QtCore/qpointer.h> -#include <QtCore/qtextstream.h> -#include <QtCore/qvector.h> - -#include <QtGui/qbitmap.h> -#include <QtGui/qevent.h> -#include <QtGui/qpaintdevice.h> -#include <QtGui/qpainter.h> -#include <QtGui/qpixmapcache.h> - -#include <QtQuick/qquickitem.h> - -#include <QtCore/private/qcore_mac_p.h> -#include <QtGui/private/qpainter_p.h> - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGContext); - -Q_FORWARD_DECLARE_OBJC_CLASS(NSView); -Q_FORWARD_DECLARE_OBJC_CLASS(NSCell); - -QT_BEGIN_NAMESPACE - -namespace QQC2 { - -/* - AHIG: - macOS Human Interface Guidelines - https://developer.apple.com/macos/human-interface-guidelines/overview/themes/ - - Builder: - Interface Builder in Xcode 8 or later -*/ - -// this works as long as we have at most 16 different control types -#define CT1(c) CT2(c, c) -#define CT2(c1, c2) ((uint(c1) << 16) | uint(c2)) - -#define SIZE(large, small, mini) \ - (controlSize == QStyleHelper::SizeLarge ? (large) : controlSize == QStyleHelper::SizeSmall ? (small) : (mini)) - -// same as return SIZE(...) but optimized -#define return_SIZE(large, small, mini) \ - do { \ - static const int sizes[] = { (large), (small), (mini) }; \ - return sizes[controlSize]; \ - } while (false) - -class QMacStylePrivate : public QCommonStylePrivate -{ - Q_DECLARE_PUBLIC(QMacStyle) - -public: - enum Direction { - North, South, East, West - }; - - enum CocoaControlType { - NoControl, // For when there's no such a control in Cocoa - Box, // QGroupBox - Box_Dark, // FIXME See render code in drawPrimitive(PE_FrameTabWidget) - Button_CheckBox, - Button_Disclosure, // Disclosure triangle, like in QTreeView - Button_PopupButton, // Non-editable QComboBox - Button_PullDown, // QPushButton with menu - Button_PushButton, // Plain QPushButton and QTabBar buttons - Button_RadioButton, - Button_SquareButton, // Oversized QPushButton - Button_WindowClose, - Button_WindowMiniaturize, - Button_WindowZoom, - ComboBox, // Editable QComboBox - ProgressIndicator_Determinate, - ProgressIndicator_Indeterminate, - Scroller_Horizontal, - Scroller_Vertical, - SegmentedControl_First, // QTabBar buttons focus ring - SegmentedControl_Middle, - SegmentedControl_Last, - SegmentedControl_Single, - Slider_Horizontal, - Slider_Vertical, - SplitView_Horizontal, - SplitView_Vertical, - Stepper, // QSpinBox buttons - TextField - }; - - 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 &); - - QMacStylePrivate(); - ~QMacStylePrivate(); - - // Ideally these wouldn't exist, but since they already exist we need some accessors. - static const int PushButtonLeftOffset; - static const int PushButtonRightOffset; - static const int PushButtonContentPadding; - - enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen, AquaScrollBar }; - QStyleHelper::WidgetSizePolicy aquaSizeConstrain(const QStyleOption *option, - QStyle::ContentsType ct = QStyle::CT_CustomBase, - QSize szHint=QSize(-1, -1), QSize *insz = 0) const; - QStyleHelper::WidgetSizePolicy effectiveAquaSizeConstrain(const QStyleOption *option, - QStyle::ContentsType ct = QStyle::CT_CustomBase, - QSize szHint=QSize(-1, -1), QSize *insz = 0) const; - inline int animateSpeed(Animates) const { return 33; } - - // Utility functions - static CGRect comboboxInnerBounds(const CGRect &outterBounds, const CocoaControl &cocoaWidget); - - static QRectF comboboxEditBounds(const QRectF &outterBounds, const CocoaControl &cw); - - void setAutoDefaultButton(QObject *button) const; - - NSView *cocoaControl(CocoaControl cocoaControl) const; - NSCell *cocoaCell(CocoaControl cocoaControl) const; - - void setupNSGraphicsContext(CGContextRef cg, bool flipped) const; - void restoreNSGraphicsContext(CGContextRef cg) const; - - void setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const; - - void drawNSViewInRect(NSView *view, const QRectF &rect, QPainter *p, DrawRectBlock drawRectBlock = nil) const; - void resolveCurrentNSView(QWindow *window) const; - - void drawToolbarButtonArrow(const QStyleOption *opt, QPainter *p) const; - - QPainterPath windowPanelPath(const QRectF &r) const; - - CocoaControlType windowButtonCocoaControl(QStyle::SubControl sc) const; - - void tabLayout(const QStyleOptionTab *opt, QRect *textRect, QRect *iconRect) const override; - static Direction tabDirection(QStyleOptionTab::Shape shape); - static bool verticalTabs(QMacStylePrivate::Direction tabDirection); - -public: - mutable QPointer<QObject> autoDefaultButton; - static QVector<QPointer<QObject> > scrollBars; - - mutable QPointer<QQuickItem> focusWidget; // TODO: rename to focusItem - mutable NSView *backingStoreNSView; - mutable QHash<CocoaControl, NSView *> cocoaControls; - mutable QHash<CocoaControl, NSCell *> cocoaCells; - - QFont smallSystemFont; - QFont miniSystemFont; - - QMacKeyValueObserver appearanceObserver; -}; - -} // namespace QQC2 - -QT_END_NAMESPACE - -#endif // QMACSTYLE_MAC_P_P_H |