1 /****************************************************************************
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
43 Note: The qdoc comments for QMacStyle are contained in
44 .../doc/src/qstyles.qdoc.
47 #include "qmacstyle_mac.h"
49 #if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
50 #define QMAC_QAQUASTYLE_SIZE_CONSTRAIN
51 //#define DEBUG_SIZE_CONSTRAINT
53 #include <private/qapplication_p.h>
54 #include <private/qcombobox_p.h>
55 #include <private/qmacstylepixmaps_mac_p.h>
56 #include <private/qpaintengine_mac_p.h>
57 #include <private/qpainter_p.h>
58 #include <private/qprintengine_mac_p.h>
59 #include <qapplication.h>
61 #include <qcheckbox.h>
62 #include <qcombobox.h>
63 #include <qdialogbuttonbox.h>
64 #include <qdockwidget.h>
66 #include <qfocusframe.h>
67 #include <qformlayout.h>
68 #include <qgroupbox.h>
70 #include <qheaderview.h>
72 #include <qlineedit.h>
73 #include <qlistview.h>
74 #include <qmainwindow.h>
77 #include <qpaintdevice.h>
79 #include <qpixmapcache.h>
81 #include <qprogressbar.h>
82 #include <qpushbutton.h>
83 #include <qradiobutton.h>
84 #include <qrubberband.h>
85 #include <qsizegrip.h>
87 #include <qsplitter.h>
88 #include <qstyleoption.h>
89 #include <qtextedit.h>
90 #include <qtextstream.h>
92 #include <qtoolbutton.h>
93 #include <qtreeview.h>
94 #include <qtableview.h>
98 #include <qdatetimeedit.h>
100 #include <QtGui/qgraphicsproxywidget.h>
101 #include <QtGui/qgraphicsview.h>
102 #include <private/qt_cocoa_helpers_mac_p.h>
103 #include "qmacstyle_mac_p.h"
104 #include <private/qstylehelper_p.h>
108 // The following constants are used for adjusting the size
109 // of push buttons so that they are drawn inside their bounds.
110 const int QMacStylePrivate::PushButtonLeftOffset = 6;
111 const int QMacStylePrivate::PushButtonTopOffset = 4;
112 const int QMacStylePrivate::PushButtonRightOffset = 12;
113 const int QMacStylePrivate::PushButtonBottomOffset = 12;
114 const int QMacStylePrivate::MiniButtonH = 26;
115 const int QMacStylePrivate::SmallButtonH = 30;
116 const int QMacStylePrivate::BevelButtonW = 50;
117 const int QMacStylePrivate::BevelButtonH = 22;
118 const int QMacStylePrivate::PushButtonContentPadding = 6;
120 // These colors specify the titlebar gradient colors on
121 // Leopard. Ideally we should get them from the system.
122 static const QColor titlebarGradientActiveBegin(220, 220, 220);
123 static const QColor titlebarGradientActiveEnd(151, 151, 151);
124 static const QColor titlebarSeparatorLineActive(111, 111, 111);
125 static const QColor titlebarGradientInactiveBegin(241, 241, 241);
126 static const QColor titlebarGradientInactiveEnd(207, 207, 207);
127 static const QColor titlebarSeparatorLineInactive(131, 131, 131);
129 // Gradient colors used for the dock widget title bar and
130 // non-unifed tool bar bacground.
131 static const QColor mainWindowGradientBegin(240, 240, 240);
132 static const QColor mainWindowGradientEnd(200, 200, 200);
134 static const int DisclosureOffset = 4;
136 // Resolve these at run-time, since the functions was moved in Leopard.
137 typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
138 static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
140 static int closeButtonSize = 12;
142 extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
144 static bool isVerticalTabs(const QTabBar::Shape shape) {
145 return (shape == QTabBar::RoundedEast
146 || shape == QTabBar::TriangularEast
147 || shape == QTabBar::RoundedWest
148 || shape == QTabBar::TriangularWest);
151 void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
153 // draw background circle
154 p->setRenderHints(QPainter::Antialiasing);
155 QRect rect(0, 0, closeButtonSize, closeButtonSize);
158 background = QColor(124, 124, 124);
162 background = QColor(104, 104, 104);
164 background = QColor(83, 83, 83);
167 background = QColor(144, 144, 144);
169 background = QColor(114, 114, 114);
172 p->setPen(Qt::transparent);
173 p->setBrush(background);
174 p->drawEllipse(rect);
180 crossPen.setColor(QColor(194, 194, 194));
181 crossPen.setWidthF(1.3);
182 crossPen.setCapStyle(Qt::FlatCap);
184 p->drawLine(min, min, max, max);
185 p->drawLine(min, max, max, min);
188 QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
190 if (isVerticalTabs(shape)) {
191 int newX, newY, newRot;
192 if (shape == QTabBar::RoundedEast
193 || shape == QTabBar::TriangularEast) {
194 newX = tabRect.width();
199 newY = tabRect.y() + tabRect.height();
202 tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
204 m.translate(newX, newY);
206 p->setMatrix(m, true);
211 void drawTabShape(QPainter *p, const QStyleOptionTabV3 *tabOpt)
213 QRect r = tabOpt->rect;
214 p->translate(tabOpt->rect.x(), tabOpt->rect.y());
217 QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
219 int width = tabRect.width();
221 bool active = (tabOpt->state & QStyle::State_Active);
222 bool selected = (tabOpt->state & QStyle::State_Selected);
225 QRect rect(1, 0, width - 2, height);
229 int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 16 : 0;
230 p->fillRect(rect, QColor(151 + d, 151 + d, 151 + d));
232 int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 9 : 0;
233 QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
234 gradient.setColorAt(0, QColor(207 + d, 207 + d, 207 + d));
235 gradient.setColorAt(0.5, QColor(206 + d, 206 + d, 206 + d));
236 gradient.setColorAt(1, QColor(201 + d, 201 + d, 201 + d));
237 p->fillRect(rect, gradient);
244 borderSides = QColor(88, 88, 88);
245 borderBottom = QColor(88, 88, 88);
247 borderSides = QColor(121, 121, 121);
248 borderBottom = QColor(116, 116, 116);
251 p->setPen(borderSides);
255 p->drawLine(0, 1, 0, bottom-2);
257 p->drawLine(width-1, 1, width-1, bottom-2);
261 p->setPen(QColor(168, 168, 168));
262 p->drawLine(3, bottom-1, width-3, bottom-1);
264 p->setPen(borderBottom);
265 p->drawLine(2, bottom, width-2, bottom);
268 QRectF rectangleLeft(1, height - w, w, w);
269 QRectF rectangleRight(width - 2, height - 1, w, w);
270 int startAngle = 180 * 16;
271 int spanAngle = 90 * 16;
272 p->setRenderHint(QPainter::Antialiasing);
273 p->drawArc(rectangleLeft, startAngle, spanAngle);
274 p->drawArc(rectangleRight, startAngle, -spanAngle);
276 // when the mouse is over non selected tabs they get a new color
277 bool hover = (tabOpt->state & QStyle::State_MouseOver);
279 QRect rect(1, 2, width - 1, height - 1);
280 p->fillRect(rect, QColor(110, 110, 110));
283 // seperator lines between tabs
284 bool west = (tabOpt->shape == QTabBar::RoundedWest || tabOpt->shape == QTabBar::TriangularWest);
285 bool drawOnRight = !west;
286 if ((!drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)
287 || (drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)) {
289 QColor borderHighlightColor;
291 borderColor = QColor(64, 64, 64);
292 borderHighlightColor = QColor(140, 140, 140);
294 borderColor = QColor(135, 135, 135);
295 borderHighlightColor = QColor(178, 178, 178);
298 int x = drawOnRight ? width : 0;
300 // tab seperator line
301 p->setPen(borderColor);
302 p->drawLine(x, 2, x, height + 1);
304 // tab seperator highlight
305 p->setPen(borderHighlightColor);
306 p->drawLine(x-1, 2, x-1, height + 1);
307 p->drawLine(x+1, 2, x+1, height + 1);
312 void drawTabBase(QPainter *p, const QStyleOptionTabBarBaseV2 *tbb, const QWidget *w)
315 if (isVerticalTabs(tbb->shape)) {
316 r.setWidth(w->width());
318 r.setHeight(w->height());
320 QRect tabRect = rotateTabPainter(p, tbb->shape, r);
321 int width = tabRect.width();
322 int height = tabRect.height();
323 bool active = (tbb->state & QStyle::State_Active);
326 QColor borderHighlightTop;
329 borderTop = QColor(64, 64, 64);
330 borderHighlightTop = QColor(174, 174, 174);
332 borderTop = QColor(135, 135, 135);
333 borderHighlightTop = QColor(207, 207, 207);
335 p->setPen(borderHighlightTop);
336 p->drawLine(tabRect.x(), 0, width, 0);
337 p->setPen(borderTop);
338 p->drawLine(tabRect.x(), 1, width, 1);
341 QRect centralRect(tabRect.x(), 2, width, height - 2);
343 QColor mainColor = QColor(120, 120, 120);
344 p->fillRect(centralRect, mainColor);
346 QLinearGradient gradient(centralRect.topLeft(), centralRect.bottomLeft());
347 gradient.setColorAt(0, QColor(165, 165, 165));
348 gradient.setColorAt(0.5, QColor(164, 164, 164));
349 gradient.setColorAt(1, QColor(158, 158, 158));
350 p->fillRect(centralRect, gradient);
353 // bottom border lines
354 QColor borderHighlightBottom;
357 borderHighlightBottom = QColor(153, 153, 153);
358 borderBottom = QColor(64, 64, 64);
360 borderHighlightBottom = QColor(177, 177, 177);
361 borderBottom = QColor(127, 127, 127);
363 p->setPen(borderHighlightBottom);
364 p->drawLine(tabRect.x(), height - 2, width, height - 2);
365 p->setPen(borderBottom);
366 p->drawLine(tabRect.x(), height - 1, width, height - 1);
369 static int getControlSize(const QStyleOption *option, const QWidget *widget)
372 if (option->state & (QStyle::State_Small | QStyle::State_Mini))
373 return (option->state & QStyle::State_Mini) ? QAquaSizeMini : QAquaSizeSmall;
375 switch (QMacStyle::widgetSizePolicy(widget)) {
376 case QMacStyle::SizeSmall:
377 return QAquaSizeSmall;
378 case QMacStyle::SizeMini:
379 return QAquaSizeMini;
384 return QAquaSizeLarge;
388 static inline bool isTreeView(const QWidget *widget)
390 return (widget && widget->parentWidget() &&
391 (qobject_cast<const QTreeView *>(widget->parentWidget())
393 || widget->parentWidget()->inherits("Q3ListView")
398 QString qt_mac_removeMnemonics(const QString &original)
400 QString returnText(original.size(), 0);
403 int l = original.length();
405 if (original.at(currPos) == QLatin1Char('&')
406 && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
412 returnText[finalDest] = original.at(currPos);
417 returnText.truncate(finalDest);
421 static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
423 ThemeTabDirection ttd;
425 case QTabBar::RoundedSouth:
426 case QTabBar::TriangularSouth:
427 ttd = kThemeTabSouth;
429 default: // Added to remove the warning, since all values are taken care of, really!
430 case QTabBar::RoundedNorth:
431 case QTabBar::TriangularNorth:
432 ttd = kThemeTabNorth;
434 case QTabBar::RoundedWest:
435 case QTabBar::TriangularWest:
438 case QTabBar::RoundedEast:
439 case QTabBar::TriangularEast:
446 QT_BEGIN_INCLUDE_NAMESPACE
447 #include "moc_qmacstyle_mac.cpp"
448 #include "moc_qmacstyle_mac_p.cpp"
449 QT_END_INCLUDE_NAMESPACE
451 /*****************************************************************************
453 *****************************************************************************/
454 extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
455 extern QRegion qt_mac_convert_mac_region(HIShapeRef); //qregion_mac.cpp
456 void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
457 extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp
459 /*****************************************************************************
461 *****************************************************************************/
462 const int qt_mac_hitheme_version = 0; //the HITheme version we speak
463 const int macItemFrame = 2; // menu item frame width
464 const int macItemHMargin = 3; // menu item hor text margin
465 const int macItemVMargin = 2; // menu item ver text margin
466 const int macRightBorder = 12; // right border on mac
467 const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar.
468 QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background.
470 /*****************************************************************************
471 QMacCGStyle utility functions
472 *****************************************************************************/
473 static inline int qt_mac_hitheme_tab_version()
478 static inline HIRect qt_hirectForQRect(const QRect &convertRect, const QRect &rect = QRect())
480 return CGRectMake(convertRect.x() + rect.x(), convertRect.y() + rect.y(),
481 convertRect.width() - rect.width(), convertRect.height() - rect.height());
484 static inline const QRect qt_qrectForHIRect(const HIRect &hirect)
486 return QRect(QPoint(int(hirect.origin.x), int(hirect.origin.y)),
487 QSize(int(hirect.size.width), int(hirect.size.height)));
490 inline bool qt_mac_is_metal(const QWidget *w)
492 for (; w; w = w->parentWidget()) {
493 if (w->testAttribute(Qt::WA_MacBrushedMetal))
495 if (w->isWindow() && w->testAttribute(Qt::WA_WState_Created)) { // If not created will fall through to the opaque check and be fine anyway.
496 return macWindowIsTextured(qt_mac_window_for(w));
498 if (w->d_func()->isOpaque)
504 static int qt_mac_aqua_get_metric(ThemeMetric met)
507 GetThemeMetric(met, &ret);
511 static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
515 if (sz != QAquaSizeSmall && sz != QAquaSizeLarge && sz != QAquaSizeMini) {
516 qDebug("Not sure how to return this...");
519 if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
520 // If you're using a custom font and it's bigger than the default font,
521 // then no constraints for you. If you are smaller, we can try to help you out
522 QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
523 if (widg->font().pointSize() > font.pointSize())
527 if (ct == QStyle::CT_CustomBase && widg) {
528 if (qobject_cast<const QPushButton *>(widg))
529 ct = QStyle::CT_PushButton;
530 else if (qobject_cast<const QRadioButton *>(widg))
531 ct = QStyle::CT_RadioButton;
532 else if (qobject_cast<const QCheckBox *>(widg))
533 ct = QStyle::CT_CheckBox;
534 else if (qobject_cast<const QComboBox *>(widg))
535 ct = QStyle::CT_ComboBox;
536 else if (qobject_cast<const QToolButton *>(widg))
537 ct = QStyle::CT_ToolButton;
538 else if (qobject_cast<const QSlider *>(widg))
539 ct = QStyle::CT_Slider;
540 else if (qobject_cast<const QProgressBar *>(widg))
541 ct = QStyle::CT_ProgressBar;
542 else if (qobject_cast<const QLineEdit *>(widg))
543 ct = QStyle::CT_LineEdit;
544 else if (qobject_cast<const QHeaderView *>(widg)
546 || widg->inherits("Q3Header")
549 ct = QStyle::CT_HeaderSection;
550 else if (qobject_cast<const QMenuBar *>(widg)
552 || widg->inherits("Q3MenuBar")
555 ct = QStyle::CT_MenuBar;
556 else if (qobject_cast<const QSizeGrip *>(widg))
557 ct = QStyle::CT_SizeGrip;
563 case QStyle::CT_PushButton: {
564 const QPushButton *psh = qobject_cast<const QPushButton *>(widg);
565 // If this comparison is false, then the widget was not a push button.
566 // This is bad and there's very little we can do since we were requested to find a
567 // sensible size for a widget that pretends to be a QPushButton but is not.
569 QString buttonText = qt_mac_removeMnemonics(psh->text());
570 if (buttonText.contains(QLatin1Char('\n')))
572 else if (sz == QAquaSizeLarge)
573 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
574 else if (sz == QAquaSizeSmall)
575 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
576 else if (sz == QAquaSizeMini)
577 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
579 if (!psh->icon().isNull()){
580 // If the button got an icon, and the icon is larger than the
581 // button, we can't decide on a default size
583 if (ret.height() < psh->iconSize().height())
586 else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){
587 // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels.
588 // However, this doesn't work for German, therefore only do it for English,
589 // I suppose it would be better to do some sort of lookups for languages
590 // that like to have really long words.
591 ret.setWidth(77 - 8);
594 // The only sensible thing to do is to return whatever the style suggests...
595 if (sz == QAquaSizeLarge)
596 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
597 else if (sz == QAquaSizeSmall)
598 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
599 else if (sz == QAquaSizeMini)
600 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
602 // Since there's no default size we return the large size...
603 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
605 #if 0 //Not sure we are applying the rules correctly for RadioButtons/CheckBoxes --Sam
606 } else if (ct == QStyle::CT_RadioButton) {
607 QRadioButton *rdo = static_cast<QRadioButton *>(widg);
608 // Exception for case where multiline radio button text requires no size constrainment
609 if (rdo->text().find('\n') != -1)
611 if (sz == QAquaSizeLarge)
612 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricRadioButtonHeight));
613 else if (sz == QAquaSizeSmall)
614 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallRadioButtonHeight));
615 else if (sz == QAquaSizeMini)
616 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniRadioButtonHeight));
617 } else if (ct == QStyle::CT_CheckBox) {
618 if (sz == QAquaSizeLarge)
619 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricCheckBoxHeight));
620 else if (sz == QAquaSizeSmall)
621 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallCheckBoxHeight));
622 else if (sz == QAquaSizeMini)
623 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniCheckBoxHeight));
627 case QStyle::CT_SizeGrip:
628 if (sz == QAquaSizeLarge || sz == QAquaSizeSmall) {
630 HIPoint p = { 0, 0 };
631 HIThemeGrowBoxDrawInfo gbi;
633 gbi.state = kThemeStateActive;
634 gbi.kind = kHIThemeGrowBoxKindNormal;
635 gbi.direction = QApplication::isRightToLeft() ? kThemeGrowLeft | kThemeGrowDown
636 : kThemeGrowRight | kThemeGrowDown;
637 gbi.size = sz == QAquaSizeSmall ? kHIThemeGrowBoxSizeSmall : kHIThemeGrowBoxSizeNormal;
638 if (HIThemeGetGrowBoxBounds(&p, &gbi, &r) == noErr)
639 ret = QSize(r.size.width, r.size.height);
642 case QStyle::CT_ComboBox:
645 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPopupButtonHeight));
648 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPopupButtonHeight));
651 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPopupButtonHeight));
657 case QStyle::CT_ToolButton:
658 if (sz == QAquaSizeSmall) {
659 int width = 0, height = 0;
660 if (szHint == QSize(-1, -1)) { //just 'guess'..
661 const QToolButton *bt = qobject_cast<const QToolButton *>(widg);
662 // If this conversion fails then the widget was not what it claimed to be.
664 if (!bt->icon().isNull()) {
665 QSize iconSize = bt->iconSize();
666 QSize pmSize = bt->icon().actualSize(QSize(32, 32), QIcon::Normal);
667 width = qMax(width, qMax(iconSize.width(), pmSize.width()));
668 height = qMax(height, qMax(iconSize.height(), pmSize.height()));
670 if (!bt->text().isNull() && bt->toolButtonStyle() != Qt::ToolButtonIconOnly) {
671 int text_width = bt->fontMetrics().width(bt->text()),
672 text_height = bt->fontMetrics().height();
673 if (bt->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
674 width = qMax(width, text_width);
675 height += text_height;
678 width = qMax(height, text_height);
682 // Let's return the size hint...
683 width = szHint.width();
684 height = szHint.height();
687 width = szHint.width();
688 height = szHint.height();
690 width = qMax(20, width + 5); //border
691 height = qMax(20, height + 5); //border
692 ret = QSize(width, height);
695 case QStyle::CT_Slider: {
697 const QSlider *sld = qobject_cast<const QSlider *>(widg);
698 // If this conversion fails then the widget was not what it claimed to be.
700 if (sz == QAquaSizeLarge) {
701 if (sld->orientation() == Qt::Horizontal) {
702 w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
703 if (sld->tickPosition() != QSlider::NoTicks)
704 w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
706 w = qt_mac_aqua_get_metric(kThemeMetricVSliderWidth);
707 if (sld->tickPosition() != QSlider::NoTicks)
708 w += qt_mac_aqua_get_metric(kThemeMetricVSliderTickWidth);
710 } else if (sz == QAquaSizeSmall) {
711 if (sld->orientation() == Qt::Horizontal) {
712 w = qt_mac_aqua_get_metric(kThemeMetricSmallHSliderHeight);
713 if (sld->tickPosition() != QSlider::NoTicks)
714 w += qt_mac_aqua_get_metric(kThemeMetricSmallHSliderTickHeight);
716 w = qt_mac_aqua_get_metric(kThemeMetricSmallVSliderWidth);
717 if (sld->tickPosition() != QSlider::NoTicks)
718 w += qt_mac_aqua_get_metric(kThemeMetricSmallVSliderTickWidth);
720 } else if (sz == QAquaSizeMini) {
721 if (sld->orientation() == Qt::Horizontal) {
722 w = qt_mac_aqua_get_metric(kThemeMetricMiniHSliderHeight);
723 if (sld->tickPosition() != QSlider::NoTicks)
724 w += qt_mac_aqua_get_metric(kThemeMetricMiniHSliderTickHeight);
726 w = qt_mac_aqua_get_metric(kThemeMetricMiniVSliderWidth);
727 if (sld->tickPosition() != QSlider::NoTicks)
728 w += qt_mac_aqua_get_metric(kThemeMetricMiniVSliderTickWidth);
732 // This is tricky, we were requested to find a size for a slider which is not
733 // a slider. We don't know if this is vertical or horizontal or if we need to
734 // have tick marks or not.
735 // For this case we will return an horizontal slider without tick marks.
736 w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
737 w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
739 if (sld->orientation() == Qt::Horizontal)
745 case QStyle::CT_ProgressBar: {
747 Qt::Orientation orient = Qt::Horizontal;
748 if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg))
749 orient = pb->orientation();
751 if (sz == QAquaSizeLarge)
752 finalValue = qt_mac_aqua_get_metric(kThemeMetricLargeProgressBarThickness)
753 + qt_mac_aqua_get_metric(kThemeMetricProgressBarShadowOutset);
755 finalValue = qt_mac_aqua_get_metric(kThemeMetricNormalProgressBarThickness)
756 + qt_mac_aqua_get_metric(kThemeMetricSmallProgressBarShadowOutset);
757 if (orient == Qt::Horizontal)
758 ret.setHeight(finalValue);
760 ret.setWidth(finalValue);
763 case QStyle::CT_LineEdit:
764 if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
765 //should I take into account the font dimentions of the lineedit? -Sam
766 if (sz == QAquaSizeLarge)
772 case QStyle::CT_HeaderSection:
773 if (isTreeView(widg))
774 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight));
776 case QStyle::CT_MenuBar:
777 if (sz == QAquaSizeLarge) {
778 #ifndef QT_MAC_USE_COCOA
780 if (!GetThemeMenuBarHeight(&size))
781 ret = QSize(-1, size);
783 ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]);
784 // In the qt_mac_set_native_menubar(false) case,
785 // we come it here with a zero-height main menu,
786 // preventing the in-window menu from displaying.
787 // Use 22 pixels for the height, by observation.
788 if (ret.height() <= 0)
800 #if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
801 static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini)
803 if (large == QSize(-1, -1)) {
804 if (small != QSize(-1, -1))
805 return QAquaSizeSmall;
806 if (mini != QSize(-1, -1))
807 return QAquaSizeMini;
808 return QAquaSizeUnknown;
809 } else if (small == QSize(-1, -1)) {
810 if (mini != QSize(-1, -1))
811 return QAquaSizeMini;
812 return QAquaSizeLarge;
813 } else if (mini == QSize(-1, -1)) {
814 return QAquaSizeLarge;
817 #ifndef QT_NO_MAINWINDOW
818 if (qobject_cast<QDockWidget *>(widg->window()) || !qgetenv("QWIDGET_ALL_SMALL").isNull()) {
819 //if (small.width() != -1 || small.height() != -1)
820 return QAquaSizeSmall;
821 } else if (!qgetenv("QWIDGET_ALL_MINI").isNull()) {
822 return QAquaSizeMini;
827 /* Figure out which size we're closer to, I just hacked this in, I haven't
828 tested it as it would probably look pretty strange to have some widgets
829 big and some widgets small in the same window?? -Sam */
831 if (large.width() != -1) {
832 int delta = large.width() - widg->width();
833 large_delta += delta * delta;
835 if (large.height() != -1) {
836 int delta = large.height() - widg->height();
837 large_delta += delta * delta;
840 if (small.width() != -1) {
841 int delta = small.width() - widg->width();
842 small_delta += delta * delta;
844 if (small.height() != -1) {
845 int delta = small.height() - widg->height();
846 small_delta += delta * delta;
849 if (mini.width() != -1) {
850 int delta = mini.width() - widg->width();
851 mini_delta += delta * delta;
853 if (mini.height() != -1) {
854 int delta = mini.height() - widg->height();
855 mini_delta += delta * delta;
857 if (mini_delta < small_delta && mini_delta < large_delta)
858 return QAquaSizeMini;
859 else if (small_delta < large_delta)
860 return QAquaSizeSmall;
862 return QAquaSizeLarge;
866 QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
867 QStyle::ContentsType ct, QSize szHint, QSize *insz) const
869 #if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
871 if (option->state & QStyle::State_Small)
872 return QAquaSizeSmall;
873 if (option->state & QStyle::State_Mini)
874 return QAquaSizeMini;
880 if (!qgetenv("QWIDGET_ALL_SMALL").isNull())
881 return QAquaSizeSmall;
882 if (!qgetenv("QWIDGET_ALL_MINI").isNull())
883 return QAquaSizeMini;
884 return QAquaSizeUnknown;
886 QSize large = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeLarge),
887 small = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeSmall),
888 mini = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeMini);
889 bool guess_size = false;
890 QAquaWidgetSize ret = QAquaSizeUnknown;
891 QMacStyle::WidgetSizePolicy wsp = q->widgetSizePolicy(widg);
892 if (wsp == QMacStyle::SizeDefault)
894 else if (wsp == QMacStyle::SizeMini)
896 else if (wsp == QMacStyle::SizeSmall)
897 ret = QAquaSizeSmall;
898 else if (wsp == QMacStyle::SizeLarge)
899 ret = QAquaSizeLarge;
901 ret = qt_aqua_guess_size(widg, large, small, mini);
904 if (ret == QAquaSizeSmall)
906 else if (ret == QAquaSizeLarge)
908 else if (ret == QAquaSizeMini)
911 *insz = sz ? *sz : QSize(-1, -1);
912 #ifdef DEBUG_SIZE_CONSTRAINT
914 const char *size_desc = "Unknown";
917 else if (sz == &large)
919 else if (sz == &mini)
921 qDebug("%s - %s: %s taken (%d, %d) [%d, %d]",
922 widg ? widg->objectName().toLatin1().constData() : "*Unknown*",
923 widg ? widg->metaObject()->className() : "*Unknown*", size_desc, widg->width(), widg->height(),
924 sz->width(), sz->height());
934 return QAquaSizeUnknown;
939 Returns the free space awailable for contents inside the
940 button (and not the size of the contents itself)
942 HIRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn,
943 const HIThemeButtonDrawInfo *bdi) const
945 HIRect outerBounds = qt_hirectForQRect(btn->rect);
946 // Adjust the bounds to correct for
947 // carbon not calculating the content bounds fully correct
948 if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){
949 outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
950 outerBounds.size.height -= QMacStylePrivate::PushButtonBottomOffset;
951 } else if (bdi->kind == kThemePushButtonMini) {
952 outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
955 HIRect contentBounds;
956 HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds);
957 return contentBounds;
961 Calculates the size of the button contents.
962 This includes both the text and the icon.
964 QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const
967 QSize iconSize = btn->icon.isNull() ? QSize(0, 0)
968 : (btn->iconSize + QSize(QMacStylePrivate::PushButtonContentPadding, 0));
969 QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1)
970 : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text);
971 csz.setWidth(iconSize.width() + textRect.width()
972 + ((btn->features & QStyleOptionButton::HasMenu)
973 ? q->proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0));
974 csz.setHeight(qMax(iconSize.height(), textRect.height()));
979 Checks if the actual contents of btn fits inside the free content bounds of
980 'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton'
981 for determining which button kind to use for drawing.
983 bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn,
984 HIThemeButtonDrawInfo *bdi,
985 ThemeButtonKind buttonKindToCheck) const
987 ThemeButtonKind tmp = bdi->kind;
988 bdi->kind = buttonKindToCheck;
989 QSize contentSize = pushButtonSizeFromContents(btn);
990 QRect freeContentRect = qt_qrectForHIRect(pushButtonContentBounds(btn, bdi));
992 return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(),
993 contentSize.width(), contentSize.height()));
997 Creates a HIThemeButtonDrawInfo structure that specifies the correct button
998 kind and other details to use for drawing the given push button. Which
999 button kind depends on the size of the button, the size of the contents,
1000 explicit user style settings, etc.
1002 void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn,
1003 const QWidget *widget,
1004 const ThemeDrawState tds,
1005 HIThemeButtonDrawInfo *bdi) const
1007 bool drawColorless = btn->palette.currentColorGroup() == QPalette::Active;
1008 ThemeDrawState tdsModified = tds;
1009 if (btn->state & QStyle::State_On)
1010 tdsModified = kThemeStatePressed;
1011 bdi->version = qt_mac_hitheme_version;
1012 bdi->state = tdsModified;
1013 bdi->value = kThemeButtonOff;
1015 if (drawColorless && tdsModified == kThemeStateInactive)
1016 bdi->state = kThemeStateActive;
1017 if (btn->state & QStyle::State_HasFocus)
1018 bdi->adornment = kThemeAdornmentFocus;
1020 bdi->adornment = kThemeAdornmentNone;
1023 if (btn->features & (QStyleOptionButton::Flat)) {
1024 bdi->kind = kThemeBevelButton;
1026 switch (aquaSizeConstrain(btn, widget)) {
1027 case QAquaSizeSmall:
1028 bdi->kind = kThemePushButtonSmall;
1031 bdi->kind = kThemePushButtonMini;
1033 case QAquaSizeLarge:
1034 // ... We should honor if the user is explicit about using the
1035 // large button. But right now Qt will specify the large button
1036 // as default rather than QAquaSizeUnknown.
1037 // So we treat it like QAquaSizeUnknown
1038 // to get the dynamic choosing of button kind.
1039 case QAquaSizeUnknown:
1040 // Choose the button kind that closest match the button rect, but at the
1041 // same time displays the button contents without clipping.
1042 bdi->kind = kThemeBevelButton;
1043 if (btn->rect.width() >= QMacStylePrivate::BevelButtonW && btn->rect.height() >= QMacStylePrivate::BevelButtonH){
1044 if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) {
1045 if (btn->rect.height() <= QMacStylePrivate::MiniButtonH){
1046 if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini))
1047 bdi->kind = kThemePushButtonMini;
1048 } else if (btn->rect.height() <= QMacStylePrivate::SmallButtonH){
1049 if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall))
1050 bdi->kind = kThemePushButtonSmall;
1051 } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) {
1052 bdi->kind = kThemePushButton;
1055 bdi->kind = kThemePushButton;
1062 bool qt_mac_buttonIsRenderedFlat(const QPushButton *pushButton, const QStyleOptionButton *option)
1064 QMacStyle *macStyle = qobject_cast<QMacStyle *>(pushButton->style());
1066 return true; // revert to 'flat' behavior if not Mac style
1067 HIThemeButtonDrawInfo bdi;
1068 macStyle->d->initHIThemePushButton(option, pushButton, kThemeStateActive, &bdi);
1069 return bdi.kind == kThemeBevelButton;
1073 Creates a HIThemeButtonDrawInfo structure that specifies the correct button
1074 kind and other details to use for drawing the given combobox. Which button
1075 kind depends on the size of the combo, wether or not it is editable,
1076 explicit user style settings, etc.
1078 void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
1079 const QWidget *widget, const ThemeDrawState &tds)
1081 bdi->version = qt_mac_hitheme_version;
1082 bdi->adornment = kThemeAdornmentArrowLeftArrow;
1083 bdi->value = kThemeButtonOff;
1084 if (combo->state & QStyle::State_HasFocus)
1085 bdi->adornment = kThemeAdornmentFocus;
1086 bool drawColorless = combo->palette.currentColorGroup() == QPalette::Active && tds == kThemeStateInactive;
1087 if (combo->activeSubControls & QStyle::SC_ComboBoxArrow)
1088 bdi->state = kThemeStatePressed;
1089 else if (drawColorless)
1090 bdi->state = kThemeStateActive;
1094 QAquaWidgetSize aSize = aquaSizeConstrain(combo, widget);
1097 bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini)
1098 : ThemeButtonKind(kThemePopupButtonMini);
1100 case QAquaSizeSmall:
1101 bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxSmall)
1102 : ThemeButtonKind(kThemePopupButtonSmall);
1104 case QAquaSizeUnknown:
1105 case QAquaSizeLarge:
1106 // Unless the user explicitly specified large buttons, determine the
1107 // kind by looking at the combox size.
1108 // ... specifying small and mini-buttons it not a current feature of
1109 // Qt (e.g. QWidget::getAttribute(WA_ButtonSize)). But when it is, add
1110 // an extra check here before using the mini and small buttons.
1111 int h = combo->rect.size().height();
1112 if (combo->editable){
1114 bdi->kind = kThemeComboBoxMini;
1116 bdi->kind = kThemeComboBoxSmall;
1118 bdi->kind = kThemeComboBox;
1120 // Even if we specify that we want the kThemePopupButton, Carbon
1121 // will use the kThemePopupButtonSmall if the size matches. So we
1122 // do the same size check explicit to have the size of the inner
1123 // text field be correct. Therefore, do this even if the user specifies
1124 // the use of LargeButtons explicit.
1126 bdi->kind = kThemePopupButtonMini;
1128 bdi->kind = kThemePopupButtonSmall;
1130 bdi->kind = kThemePopupButton;
1137 Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain
1138 the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds.
1140 HIRect QMacStylePrivate::comboboxInnerBounds(const HIRect &outerBounds, int buttonKind)
1142 HIRect innerBounds = outerBounds;
1143 // Carbon draw parts of the view outside the rect.
1144 // So make the rect a bit smaller to compensate
1145 // (I wish HIThemeGetButtonBackgroundBounds worked)
1146 switch (buttonKind){
1147 case kThemePopupButton:
1148 innerBounds.origin.x += 2;
1149 innerBounds.origin.y += 2;
1150 innerBounds.size.width -= 5;
1151 innerBounds.size.height -= 6;
1153 case kThemePopupButtonSmall:
1154 innerBounds.origin.x += 3;
1155 innerBounds.origin.y += 3;
1156 innerBounds.size.width -= 6;
1157 innerBounds.size.height -= 7;
1159 case kThemePopupButtonMini:
1160 innerBounds.origin.x += 2;
1161 innerBounds.origin.y += 2;
1162 innerBounds.size.width -= 5;
1163 innerBounds.size.height -= 6;
1165 case kThemeComboBox:
1166 innerBounds.origin.x += 3;
1167 innerBounds.origin.y += 2;
1168 innerBounds.size.width -= 6;
1169 innerBounds.size.height -= 8;
1171 case kThemeComboBoxSmall:
1172 innerBounds.origin.x += 3;
1173 innerBounds.origin.y += 3;
1174 innerBounds.size.width -= 7;
1175 innerBounds.size.height -= 8;
1177 case kThemeComboBoxMini:
1178 innerBounds.origin.x += 3;
1179 innerBounds.origin.y += 3;
1180 innerBounds.size.width -= 4;
1181 innerBounds.size.height -= 8;
1190 Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind
1191 of combobox we choose to draw. This function calculates and returns this size.
1193 QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi)
1195 QRect ret = outerBounds;
1197 case kThemeComboBox:
1198 ret.adjust(5, 5, -22, -5);
1200 case kThemeComboBoxSmall:
1201 ret.adjust(4, 6, -20, 0);
1204 case kThemeComboBoxMini:
1205 ret.adjust(4, 5, -18, -1);
1208 case kThemePopupButton:
1209 ret.adjust(10, 2, -23, -4);
1211 case kThemePopupButtonSmall:
1212 ret.adjust(9, 3, -20, -3);
1214 case kThemePopupButtonMini:
1215 ret.adjust(8, 3, -19, 0);
1223 Carbon comboboxes don't scale (sight). If the size of the combo suggest a scaled version,
1224 create it manually by drawing a small Carbon combo onto a pixmap (use pixmap cache), chop
1225 it up, and copy it back onto the widget. Othervise, draw the combobox supplied by Carbon directly.
1227 void QMacStylePrivate::drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1229 if (!(bdi.kind == kThemeComboBox && outerBounds.size.height > 28)){
1230 // We have an unscaled combobox, or popup-button; use Carbon directly.
1231 HIRect innerBounds = QMacStylePrivate::comboboxInnerBounds(outerBounds, bdi.kind);
1232 HIThemeDrawButton(&innerBounds, &bdi, QMacCGContext(p), kHIThemeOrientationNormal, 0);
1235 QString key = QString(QLatin1String("$qt_cbox%1-%2")).arg(int(bdi.state)).arg(int(bdi.adornment));
1236 if (!QPixmapCache::find(key, buffer)) {
1237 HIRect innerBoundsSmallCombo = {{3, 3}, {29, 25}};
1238 buffer = QPixmap(35, 28);
1239 buffer.fill(Qt::transparent);
1240 QPainter buffPainter(&buffer);
1241 HIThemeDrawButton(&innerBoundsSmallCombo, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1243 QPixmapCache::insert(key, buffer);
1246 const int bwidth = 20;
1247 const int fwidth = 10;
1248 const int fheight = 10;
1249 int w = qRound(outerBounds.size.width);
1250 int h = qRound(outerBounds.size.height);
1251 int bstart = w - bwidth;
1252 int blower = fheight + 1;
1253 int flower = h - fheight;
1254 int sheight = flower - fheight;
1255 int center = qRound(outerBounds.size.height + outerBounds.origin.y) / 2;
1257 // Draw upper and lower gap
1258 p->drawPixmap(fwidth, 0, bstart - fwidth, fheight, buffer, fwidth, 0, 1, fheight);
1259 p->drawPixmap(fwidth, flower, bstart - fwidth, fheight, buffer, fwidth, buffer.height() - fheight, 1, fheight);
1260 // Draw left and right gap. Right gap is drawn top and bottom separatly
1261 p->drawPixmap(0, fheight, fwidth, sheight, buffer, 0, fheight, fwidth, 1);
1262 p->drawPixmap(bstart, fheight, bwidth, center - fheight, buffer, buffer.width() - bwidth, fheight - 1, bwidth, 1);
1263 p->drawPixmap(bstart, center, bwidth, sheight / 2, buffer, buffer.width() - bwidth, fheight + 6, bwidth, 1);
1265 p->drawPixmap(bstart, center - 4, bwidth - 3, 6, buffer, buffer.width() - bwidth, fheight, bwidth - 3, 6);
1267 p->drawPixmap(0, 0, fwidth, fheight, buffer, 0, 0, fwidth, fheight);
1268 p->drawPixmap(bstart, 0, bwidth, fheight, buffer, buffer.width() - bwidth, 0, bwidth, fheight);
1269 p->drawPixmap(0, flower, fwidth, fheight, buffer, 0, buffer.height() - fheight, fwidth, fheight);
1270 p->drawPixmap(bstart, h - blower, bwidth, blower, buffer, buffer.width() - bwidth, buffer.height() - blower, bwidth, blower);
1275 Carbon tableheaders don't scale (sight). So create it manually by drawing a small Carbon header
1276 onto a pixmap (use pixmap cache), chop it up, and copy it back onto the widget.
1278 void QMacStylePrivate::drawTableHeader(const HIRect &outerBounds,
1279 bool drawTopBorder, bool drawLeftBorder, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1281 static SInt32 headerHeight = 0;
1282 static OSStatus err = GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
1286 QString key = QString(QLatin1String("$qt_tableh%1-%2-%3")).arg(int(bdi.state)).arg(int(bdi.adornment)).arg(int(bdi.value));
1287 if (!QPixmapCache::find(key, buffer)) {
1288 HIRect headerNormalRect = {{0., 0.}, {16., CGFloat(headerHeight)}};
1289 buffer = QPixmap(headerNormalRect.size.width, headerNormalRect.size.height);
1290 buffer.fill(Qt::transparent);
1291 QPainter buffPainter(&buffer);
1292 HIThemeDrawButton(&headerNormalRect, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1294 QPixmapCache::insert(key, buffer);
1296 const int buttonw = qRound(outerBounds.size.width);
1297 const int buttonh = qRound(outerBounds.size.height);
1298 const int framew = 1;
1299 const int frameh_n = 4;
1300 const int frameh_s = 3;
1301 const int transh = buffer.height() - frameh_n - frameh_s;
1302 int center = buttonh - frameh_s - int(transh / 2.0f) + 1; // Align bottom;
1304 int skipTopBorder = 0;
1308 p->translate(outerBounds.origin.x, outerBounds.origin.y);
1310 p->drawPixmap(QRect(QRect(0, -skipTopBorder, buttonw - framew , frameh_n)), buffer, QRect(framew, 0, 1, frameh_n));
1311 p->drawPixmap(QRect(0, buttonh - frameh_s, buttonw - framew, frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, frameh_s));
1312 // Draw upper and lower center blocks
1313 p->drawPixmap(QRect(0, frameh_n - skipTopBorder, buttonw - framew, center - frameh_n + skipTopBorder), buffer, QRect(framew, frameh_n, 1, 1));
1314 p->drawPixmap(QRect(0, center, buttonw - framew, buttonh - center - frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, 1));
1315 // Draw right center block borders
1316 p->drawPixmap(QRect(buttonw - framew, frameh_n - skipTopBorder, framew, center - frameh_n), buffer, QRect(buffer.width() - framew, frameh_n, framew, 1));
1317 p->drawPixmap(QRect(buttonw - framew, center, framew, buttonh - center - 1), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, 1));
1318 // Draw right corners
1319 p->drawPixmap(QRect(buttonw - framew, -skipTopBorder, framew, frameh_n), buffer, QRect(buffer.width() - framew, 0, framew, frameh_n));
1320 p->drawPixmap(QRect(buttonw - framew, buttonh - frameh_s, framew, frameh_s), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, frameh_s));
1321 // Draw center transition block
1322 p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), buttonw - framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(framew, frameh_n + 1, 1, transh));
1323 // Draw right center transition block border
1324 p->drawPixmap(QRect(buttonw - framew, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(buffer.width() - framew, frameh_n + 1, framew, transh));
1325 if (drawLeftBorder){
1326 // Draw left center block borders
1327 p->drawPixmap(QRect(0, frameh_n - skipTopBorder, framew, center - frameh_n + skipTopBorder), buffer, QRect(0, frameh_n, framew, 1));
1328 p->drawPixmap(QRect(0, center, framew, buttonh - center - 1), buffer, QRect(0, buffer.height() - frameh_s, framew, 1));
1329 // Draw left corners
1330 p->drawPixmap(QRect(0, -skipTopBorder, framew, frameh_n), buffer, QRect(0, 0, framew, frameh_n));
1331 p->drawPixmap(QRect(0, buttonh - frameh_s, framew, frameh_s), buffer, QRect(0, buffer.height() - frameh_s, framew, frameh_s));
1332 // Draw left center transition block border
1333 p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(0, frameh_n + 1, framew, transh));
1336 p->translate(-outerBounds.origin.x, -outerBounds.origin.y);
1340 Returns cutoff sizes for scroll bars.
1341 thumbIndicatorCutoff is the smallest size where the thumb indicator is drawn.
1342 scrollButtonsCutoff is the smallest size where the up/down buttons is drawn.
1344 enum ScrollBarCutoffType { thumbIndicatorCutoff = 0, scrollButtonsCutoff = 1 };
1345 static int scrollButtonsCutoffSize(ScrollBarCutoffType cutoffType, QMacStyle::WidgetSizePolicy widgetSize)
1347 // Mini scroll bars do not exist as of version 10.4.
1348 if (widgetSize == QMacStyle::SizeMini)
1351 const int sizeIndex = (widgetSize == QMacStyle::SizeSmall) ? 1 : 0;
1352 static const int sizeTable[2][2] = { { 61, 56 }, { 49, 44 } };
1353 return sizeTable[sizeIndex][cutoffType];
1356 void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
1357 HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe)
1359 memset(tdi, 0, sizeof(HIThemeTrackDrawInfo)); // We don't get it all for some reason or another...
1360 tdi->version = qt_mac_hitheme_version;
1363 bool isScrollbar = (cc == QStyle::CC_ScrollBar);
1364 switch (aquaSizeConstrain(0, needToRemoveMe)) {
1365 case QAquaSizeUnknown:
1366 case QAquaSizeLarge:
1368 tdi->kind = kThemeMediumScrollBar;
1370 tdi->kind = kThemeMediumSlider;
1374 tdi->kind = kThemeSmallScrollBar; // should be kThemeMiniScrollBar, but not implemented
1376 tdi->kind = kThemeMiniSlider;
1378 case QAquaSizeSmall:
1380 tdi->kind = kThemeSmallScrollBar;
1382 tdi->kind = kThemeSmallSlider;
1385 tdi->bounds = qt_hirectForQRect(slider->rect);
1386 tdi->min = slider->minimum;
1387 tdi->max = slider->maximum;
1388 tdi->value = slider->sliderPosition;
1389 tdi->attributes = kThemeTrackShowThumb;
1390 if (slider->upsideDown)
1391 tdi->attributes |= kThemeTrackRightToLeft;
1392 if (slider->orientation == Qt::Horizontal) {
1393 tdi->attributes |= kThemeTrackHorizontal;
1394 if (isScrollbar && slider->direction == Qt::RightToLeft) {
1395 if (!slider->upsideDown)
1396 tdi->attributes |= kThemeTrackRightToLeft;
1398 tdi->attributes &= ~kThemeTrackRightToLeft;
1402 // Tiger broke reverse scroll bars so put them back and "fake it"
1403 if (isScrollbar && (tdi->attributes & kThemeTrackRightToLeft)) {
1404 tdi->attributes &= ~kThemeTrackRightToLeft;
1405 tdi->value = tdi->max - slider->sliderPosition;
1408 tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive
1409 : kThemeTrackDisabled;
1410 if (!(slider->state & QStyle::State_Active))
1411 tdi->enableState = kThemeTrackInactive;
1413 if (slider->state & QStyle::QStyle::State_HasFocus)
1414 tdi->attributes |= kThemeTrackHasFocus;
1415 if (slider->tickPosition == QSlider::NoTicks || slider->tickPosition == QSlider::TicksBothSides)
1416 tdi->trackInfo.slider.thumbDir = kThemeThumbPlain;
1417 else if (slider->tickPosition == QSlider::TicksAbove)
1418 tdi->trackInfo.slider.thumbDir = kThemeThumbUpward;
1420 tdi->trackInfo.slider.thumbDir = kThemeThumbDownward;
1422 tdi->trackInfo.scrollbar.viewsize = slider->pageStep;
1427 QMacStylePrivate::QMacStylePrivate(QMacStyle *style)
1428 : timerID(-1), progressFrame(0), q(style), mouseDown(false)
1430 defaultButtonStart = CFAbsoluteTimeGetCurrent();
1431 memset(&buttonState, 0, sizeof(ButtonState));
1433 if (ptrHIShapeGetBounds == 0) {
1434 QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
1435 library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
1436 ptrHIShapeGetBounds = reinterpret_cast<PtrHIShapeGetBounds>(library.resolve("HIShapeGetBounds"));
1441 bool QMacStylePrivate::animatable(QMacStylePrivate::Animates as, const QWidget *w) const
1446 if (as == AquaPushButton) {
1447 QPushButton *pb = const_cast<QPushButton *>(static_cast<const QPushButton *>(w));
1448 if (w->window()->isActiveWindow() && pb && !mouseDown) {
1449 if (static_cast<const QPushButton *>(w) != defaultButton) {
1450 // Changed on its own, update the value.
1451 const_cast<QMacStylePrivate *>(this)->stopAnimate(as, defaultButton);
1452 const_cast<QMacStylePrivate *>(this)->startAnimate(as, pb);
1456 } else if (as == AquaProgressBar) {
1457 if (progressBars.contains((const_cast<QWidget *>(w))))
1463 void QMacStylePrivate::stopAnimate(QMacStylePrivate::Animates as, QWidget *w)
1465 if (as == AquaPushButton && defaultButton) {
1466 QPushButton *tmp = defaultButton;
1469 } else if (as == AquaProgressBar) {
1470 progressBars.removeAll(w);
1474 void QMacStylePrivate::startAnimate(QMacStylePrivate::Animates as, QWidget *w)
1476 if (as == AquaPushButton)
1477 defaultButton = static_cast<QPushButton *>(w);
1478 else if (as == AquaProgressBar)
1479 progressBars.append(w);
1480 startAnimationTimer();
1483 void QMacStylePrivate::startAnimationTimer()
1485 if ((defaultButton || !progressBars.isEmpty()) && timerID <= -1)
1486 timerID = startTimer(animateSpeed(AquaListViewItemOpen));
1489 bool QMacStylePrivate::addWidget(QWidget *w)
1491 //already knew of it
1492 if (static_cast<QPushButton*>(w) == defaultButton
1493 || progressBars.contains(static_cast<QProgressBar*>(w)))
1496 if (QPushButton *btn = qobject_cast<QPushButton *>(w)) {
1497 btn->installEventFilter(this);
1498 if (btn->isDefault() || (btn->autoDefault() && btn->hasFocus()))
1499 startAnimate(AquaPushButton, btn);
1502 bool isProgressBar = (qobject_cast<QProgressBar *>(w)
1504 || w->inherits("Q3ProgressBar")
1507 if (isProgressBar) {
1508 w->installEventFilter(this);
1509 startAnimate(AquaProgressBar, w);
1513 if (w->isWindow()) {
1514 w->installEventFilter(this);
1520 void QMacStylePrivate::removeWidget(QWidget *w)
1522 QPushButton *btn = qobject_cast<QPushButton *>(w);
1523 if (btn && btn == defaultButton) {
1524 stopAnimate(AquaPushButton, btn);
1525 } else if (qobject_cast<QProgressBar *>(w)
1527 || w->inherits("Q3ProgressBar")
1530 stopAnimate(AquaProgressBar, w);
1534 ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
1536 ThemeDrawState tds = kThemeStateActive;
1537 if (flags & QStyle::State_Sunken) {
1538 tds = kThemeStatePressed;
1539 } else if (flags & QStyle::State_Active) {
1540 if (!(flags & QStyle::State_Enabled))
1541 tds = kThemeStateUnavailable;
1543 if (flags & QStyle::State_Enabled)
1544 tds = kThemeStateInactive;
1546 tds = kThemeStateUnavailableInactive;
1551 void QMacStylePrivate::timerEvent(QTimerEvent *)
1554 if (defaultButton && defaultButton->isEnabled() && defaultButton->window()->isActiveWindow()
1555 && defaultButton->isVisibleTo(0) && (defaultButton->isDefault()
1556 || (defaultButton->autoDefault() && defaultButton->hasFocus()))
1557 && doAnimate(AquaPushButton)) {
1559 defaultButton->update();
1561 if (!progressBars.isEmpty()) {
1563 while (i < progressBars.size()) {
1564 QWidget *maybeProgress = progressBars.at(i);
1565 if (!maybeProgress) {
1566 progressBars.removeAt(i);
1568 if (QProgressBar *pb = qobject_cast<QProgressBar *>(maybeProgress)) {
1569 if (pb->maximum() == 0 || (pb->value() > 0 && pb->value() < pb->maximum())) {
1570 if (doAnimate(AquaProgressBar))
1577 QVariant progress = maybeProgress->property("progress");
1578 QVariant totalSteps = maybeProgress->property("totalSteps");
1579 if (progress.isValid() && totalSteps.isValid()) {
1580 int intProgress = progress.toInt();
1581 int intTotalSteps = totalSteps.toInt();
1582 if (intTotalSteps == 0 || intProgress > 0 && intProgress < intTotalSteps) {
1583 if (doAnimate(AquaProgressBar))
1584 maybeProgress->update();
1597 if (animated <= 0) {
1603 bool QMacStylePrivate::eventFilter(QObject *o, QEvent *e)
1606 if (QProgressBar *pb = qobject_cast<QProgressBar *>(o)) {
1607 switch (e->type()) {
1611 if (!progressBars.contains(pb))
1612 startAnimate(AquaProgressBar, pb);
1614 case QEvent::Destroy:
1616 progressBars.removeAll(pb);
1618 } else if (QPushButton *btn = qobject_cast<QPushButton *>(o)) {
1619 switch (e->type()) {
1622 case QEvent::FocusIn:
1623 if (btn->autoDefault())
1624 startAnimate(AquaPushButton, btn);
1626 case QEvent::Destroy:
1628 if (btn == defaultButton)
1629 stopAnimate(AquaPushButton, btn);
1631 case QEvent::MouseButtonPress:
1632 // It is very confusing to keep the button pulsing, so just stop the animation.
1633 if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
1635 stopAnimate(AquaPushButton, btn);
1637 case QEvent::MouseButtonRelease:
1638 if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
1641 case QEvent::FocusOut:
1643 case QEvent::WindowActivate: {
1644 QList<QPushButton *> list = btn->window()->findChildren<QPushButton *>();
1645 for (int i = 0; i < list.size(); ++i) {
1646 QPushButton *pBtn = list.at(i);
1647 if ((e->type() == QEvent::FocusOut
1648 && (pBtn->isDefault() || (pBtn->autoDefault() && pBtn->hasFocus()))
1650 || ((e->type() == QEvent::Show || e->type() == QEvent::MouseButtonRelease
1651 || e->type() == QEvent::WindowActivate)
1652 && pBtn->isDefault())) {
1653 if (pBtn->window()->isActiveWindow()) {
1654 startAnimate(AquaPushButton, pBtn);
1665 bool QMacStylePrivate::doAnimate(QMacStylePrivate::Animates as)
1667 if (as == AquaPushButton) {
1668 } else if (as == AquaProgressBar) {
1669 // something for later...
1670 } else if (as == AquaListViewItemOpen) {
1671 // To be revived later...
1676 void QMacStylePrivate::drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
1677 QPainter *p, const QStyleOption *opt) const
1685 const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
1686 int width = int(macRect.size.width) + extraWidth;
1687 int height = int(macRect.size.height) + extraHeight;
1689 if (width <= 0 || height <= 0)
1690 return; // nothing to draw
1692 QString key = QLatin1String("$qt_mac_style_ctb_") + QString::number(bdi->kind) + QLatin1Char('_')
1693 + QString::number(bdi->value) + QLatin1Char('_') + QString::number(width)
1694 + QLatin1Char('_') + QString::number(height);
1696 if (!QPixmapCache::find(key, pm)) {
1697 QPixmap activePixmap(width, height);
1698 activePixmap.fill(Qt::transparent);
1701 // Carbon combos don't scale. Therefore we draw it
1702 // ourselves, if a scaled version is needed.
1703 QPainter tmpPainter(&activePixmap);
1704 QMacStylePrivate::drawCombobox(macRect, *bdi, &tmpPainter);
1707 QMacCGContext cg(&activePixmap);
1708 HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
1709 HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
1713 if (!combo && bdi->value == kThemeButtonOff) {
1716 QImage image = activePixmap.toImage();
1718 for (int y = 0; y < height; ++y) {
1719 QRgb *scanLine = reinterpret_cast<QRgb *>(image.scanLine(y));
1721 for (int x = 0; x < width; ++x) {
1722 QRgb &pixel = scanLine[x];
1724 int darkest = qRed(pixel);
1725 int mid = qGreen(pixel);
1726 int lightest = qBlue(pixel);
1729 qSwap(darkest, mid);
1731 qSwap(mid, lightest);
1733 qSwap(darkest, mid);
1735 int gray = (mid + 2 * lightest) / 3;
1736 pixel = qRgba(gray, gray, gray, qAlpha(pixel));
1739 pm = QPixmap::fromImage(image);
1741 QImage activeImage = activePixmap.toImage();
1742 QImage colorlessImage;
1744 QPixmap colorlessPixmap(width, height);
1745 colorlessPixmap.fill(Qt::transparent);
1747 QMacCGContext cg(&colorlessPixmap);
1748 HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
1749 int oldValue = bdi->value;
1750 bdi->value = kThemeButtonOff;
1751 HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
1752 bdi->value = oldValue;
1753 colorlessImage = colorlessPixmap.toImage();
1756 for (int y = 0; y < height; ++y) {
1757 QRgb *colorlessScanLine = reinterpret_cast<QRgb *>(colorlessImage.scanLine(y));
1758 const QRgb *activeScanLine = reinterpret_cast<const QRgb *>(activeImage.scanLine(y));
1760 for (int x = 0; x < width; ++x) {
1761 QRgb &colorlessPixel = colorlessScanLine[x];
1762 QRgb activePixel = activeScanLine[x];
1764 if (activePixel != colorlessPixel) {
1765 int max = qMax(qMax(qRed(activePixel), qGreen(activePixel)),
1766 qBlue(activePixel));
1767 QRgb newPixel = qRgba(max, max, max, qAlpha(activePixel));
1768 if (qGray(newPixel) < qGray(colorlessPixel)
1769 || qAlpha(newPixel) > qAlpha(colorlessPixel))
1770 colorlessPixel = newPixel;
1774 pm = QPixmap::fromImage(colorlessImage);
1776 QPixmapCache::insert(key, pm);
1778 p->drawPixmap(int(macRect.origin.x), int(macRect.origin.y) + finalyoff, width, height, pm);
1781 QMacStyle::QMacStyle()
1784 d = new QMacStylePrivate(this);
1787 QMacStyle::~QMacStyle()
1789 delete qt_mac_backgroundPattern;
1790 qt_mac_backgroundPattern = 0;
1795 Generates the standard widget background pattern.
1797 QPixmap QMacStylePrivate::generateBackgroundPattern() const
1800 QMacCGContext cg(&px);
1801 HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationNormal);
1802 const CGRect cgRect = CGRectMake(0, 0, px.width(), px.height());
1803 CGContextFillRect(cg, cgRect);
1808 Fills the given \a rect with the pattern stored in \a brush. As an optimization,
1809 HIThemeSetFill us used directly if we are filling with the standard background.
1811 void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush)
1814 const QPaintDevice *target = painter->device();
1815 const QPaintDevice *redirected = QPainter::redirected(target, &dummy);
1816 const bool usePainter = redirected && redirected != target;
1818 if (!usePainter && qt_mac_backgroundPattern
1819 && qt_mac_backgroundPattern->cacheKey() == brush.texture().cacheKey()) {
1821 painter->setClipRegion(rgn);
1823 QCFType<CGContextRef> cg = qt_mac_cg_context(target);
1824 CGContextSaveGState(cg);
1825 HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationInverted);
1827 const QVector<QRect> &rects = rgn.rects();
1828 for (int i = 0; i < rects.size(); ++i) {
1829 const QRect rect(rects.at(i));
1830 // Anchor the pattern to the top so it stays put when the window is resized.
1831 CGContextSetPatternPhase(cg, CGSizeMake(rect.width(), rect.height()));
1832 CGRect mac_rect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
1833 CGContextFillRect(cg, mac_rect);
1836 CGContextRestoreGState(cg);
1838 const QRect rect(rgn.boundingRect());
1839 painter->setClipRegion(rgn);
1840 painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
1844 void QMacStyle::polish(QPalette &pal)
1846 if (!qt_mac_backgroundPattern) {
1849 qt_mac_backgroundPattern = new QPixmap(d->generateBackgroundPattern());
1852 QColor pc(Qt::black);
1853 pc = qcolorForTheme(kThemeBrushDialogBackgroundActive);
1854 QBrush background(pc, *qt_mac_backgroundPattern);
1855 pal.setBrush(QPalette::All, QPalette::Window, background);
1856 pal.setBrush(QPalette::All, QPalette::Button, background);
1859 const OSErr err = CopyThemeIdentifier(&theme);
1860 if (err == noErr && CFStringCompare(theme, kThemeAppearanceAquaGraphite, 0) == kCFCompareEqualTo) {
1861 pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(240, 240, 240));
1863 pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(237, 243, 254));
1867 void QMacStyle::polish(QApplication *)
1871 void QMacStyle::unpolish(QApplication *)
1875 void QMacStyle::polish(QWidget* w)
1878 if (qt_mac_is_metal(w) && !w->testAttribute(Qt::WA_SetPalette)) {
1879 // Set a clear brush so that the metal shines through.
1880 QPalette pal = w->palette();
1881 QBrush background(Qt::transparent);
1882 pal.setBrush(QPalette::All, QPalette::Window, background);
1883 pal.setBrush(QPalette::All, QPalette::Button, background);
1885 w->setAttribute(Qt::WA_SetPalette, false);
1888 if (qobject_cast<QMenu*>(w) || qobject_cast<QComboBoxPrivateContainer *>(w)) {
1889 w->setWindowOpacity(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 ? 0.985 : 0.94);
1890 if (!w->testAttribute(Qt::WA_SetPalette)) {
1893 HIThemeMenuDrawInfo mtinfo;
1894 mtinfo.version = qt_mac_hitheme_version;
1895 mtinfo.menuType = kThemeMenuTypePopUp;
1896 HIRect rect = CGRectMake(0, 0, px.width(), px.height());
1897 HIThemeDrawMenuBackground(&rect, &mtinfo, QCFType<CGContextRef>(qt_mac_cg_context(&px)),
1898 kHIThemeOrientationNormal);
1899 QPalette pal = w->palette();
1900 QBrush background(px);
1901 pal.setBrush(QPalette::All, QPalette::Window, background);
1902 pal.setBrush(QPalette::All, QPalette::Button, background);
1904 w->setAttribute(Qt::WA_SetPalette, false);
1908 if (QTabBar *tb = qobject_cast<QTabBar*>(w)) {
1909 if (tb->documentMode()) {
1910 w->setAttribute(Qt::WA_Hover);
1911 w->setFont(qt_app_fonts_hash()->value("QSmallFont", QFont()));
1912 QPalette p = w->palette();
1913 p.setColor(QPalette::WindowText, QColor(17, 17, 17));
1918 QWindowsStyle::polish(w);
1920 if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
1921 rubber->setWindowOpacity(0.25);
1922 rubber->setAttribute(Qt::WA_PaintOnScreen, false);
1923 rubber->setAttribute(Qt::WA_NoSystemBackground, false);
1927 void QMacStyle::unpolish(QWidget* w)
1930 if ((qobject_cast<QMenu*>(w) || qt_mac_is_metal(w)) && !w->testAttribute(Qt::WA_SetPalette)) {
1931 QPalette pal = qApp->palette(w);
1933 w->setAttribute(Qt::WA_SetPalette, false);
1934 w->setWindowOpacity(1.0);
1937 if (QComboBox *combo = qobject_cast<QComboBox *>(w)) {
1938 if (!combo->isEditable()) {
1939 if (QWidget *widget = combo->findChild<QComboBoxPrivateContainer *>())
1940 widget->setWindowOpacity(1.0);
1944 if (QRubberBand *rubber = ::qobject_cast<QRubberBand*>(w)) {
1945 rubber->setWindowOpacity(1.0);
1946 rubber->setAttribute(Qt::WA_PaintOnScreen, true);
1947 rubber->setAttribute(Qt::WA_NoSystemBackground, true);
1950 if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w))
1951 frame->setAttribute(Qt::WA_NoSystemBackground, true);
1953 QWindowsStyle::unpolish(w);
1956 int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
1958 int controlSize = getControlSize(opt, widget);
1962 case PM_TabCloseIndicatorWidth:
1963 case PM_TabCloseIndicatorHeight:
1964 ret = closeButtonSize;
1966 case PM_ToolBarIconSize:
1967 ret = proxy()->pixelMetric(PM_LargeIconSize);
1969 case PM_FocusFrameVMargin:
1970 case PM_FocusFrameHMargin:
1971 GetThemeMetric(kThemeMetricFocusRectOutset, &ret);
1973 case PM_DialogButtonsSeparator:
1976 case PM_DialogButtonsButtonHeight: {
1978 ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
1979 if (sz == QSize(-1, -1))
1984 case PM_CheckListButtonSize: {
1985 switch (d->aquaSizeConstrain(opt, widget)) {
1986 case QAquaSizeUnknown:
1987 case QAquaSizeLarge:
1988 GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
1991 GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
1993 case QAquaSizeSmall:
1994 GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
1998 case PM_DialogButtonsButtonWidth: {
2000 ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
2001 if (sz == QSize(-1, -1))
2007 case PM_MenuBarHMargin:
2011 case PM_MenuBarVMargin:
2015 case QStyle::PM_MenuDesktopFrameWidth:
2019 case PM_CheckBoxLabelSpacing:
2020 case PM_RadioButtonLabelSpacing:
2023 case PM_MenuScrollerHeight:
2026 GetThemeMenuItemExtra(kThemeMenuItemScrollUpArrow, &ash, &asw);
2029 ret = 15; // I hate having magic numbers in here...
2032 case PM_DefaultFrameWidth:
2033 #ifndef QT_NO_MAINWINDOW
2034 if (widget && (widget->isWindow() || !widget->parentWidget()
2035 || (qobject_cast<const QMainWindow*>(widget->parentWidget())
2036 && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget))
2037 && (qobject_cast<const QAbstractScrollArea *>(widget)
2039 || widget->inherits("QScrollView")
2041 || widget->inherits("QWorkspaceChild")))
2045 // The combo box popup has no frame.
2046 if (qstyleoption_cast<const QStyleOptionComboBox *>(opt) != 0)
2048 // Frame of mac style line edits is two pixels on top and one on the bottom
2049 else if (qobject_cast<const QLineEdit *>(widget) != 0)
2054 case PM_MaximumDragDistance:
2057 case PM_ScrollBarSliderMin:
2060 case PM_SpinBoxFrameWidth:
2061 GetThemeMetric(kThemeMetricEditTextFrameOutset, &ret);
2062 switch (d->aquaSizeConstrain(opt, widget)) {
2071 case PM_ButtonShiftHorizontal:
2072 case PM_ButtonShiftVertical:
2075 case PM_SliderLength:
2078 case PM_ButtonDefaultIndicator:
2081 case PM_TitleBarHeight:
2082 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
2083 HIThemeWindowDrawInfo wdi;
2084 wdi.version = qt_mac_hitheme_version;
2085 wdi.state = kThemeStateActive;
2086 wdi.windowType = QtWinType;
2087 if (tb->titleBarState)
2088 wdi.attributes = kThemeWindowHasFullZoom | kThemeWindowHasCloseBox
2089 | kThemeWindowHasCollapseBox;
2090 else if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
2091 wdi.attributes = kThemeWindowHasCloseBox;
2094 wdi.titleHeight = tb->rect.height();
2095 wdi.titleWidth = tb->rect.width();
2096 QCFType<HIShapeRef> region;
2097 HIRect hirect = qt_hirectForQRect(tb->rect);
2098 if (hirect.size.width <= 0)
2099 hirect.size.width = 100;
2100 if (hirect.size.height <= 0)
2101 hirect.size.height = 30;
2103 HIThemeGetWindowShape(&hirect, &wdi, kWindowTitleBarRgn, ®ion);
2105 ptrHIShapeGetBounds(region, &rect);
2106 ret = int(rect.size.height);
2110 case PM_TabBarTabVSpace:
2113 case PM_TabBarTabShiftHorizontal:
2114 case PM_TabBarTabShiftVertical:
2117 case PM_TabBarBaseHeight:
2120 case PM_TabBarTabOverlap:
2123 case PM_TabBarBaseOverlap:
2124 switch (d->aquaSizeConstrain(opt, widget)) {
2125 case QAquaSizeUnknown:
2126 case QAquaSizeLarge:
2129 case QAquaSizeSmall:
2137 case PM_ScrollBarExtent: {
2138 switch (d->aquaSizeConstrain(opt, widget)) {
2139 case QAquaSizeUnknown:
2140 case QAquaSizeLarge:
2141 GetThemeMetric(kThemeMetricScrollBarWidth, &ret);
2144 case QAquaSizeSmall:
2145 GetThemeMetric(kThemeMetricSmallScrollBarWidth, &ret);
2149 case PM_IndicatorHeight: {
2150 switch (d->aquaSizeConstrain(opt, widget)) {
2151 case QAquaSizeUnknown:
2152 case QAquaSizeLarge:
2153 GetThemeMetric(kThemeMetricCheckBoxHeight, &ret);
2156 GetThemeMetric(kThemeMetricMiniCheckBoxHeight, &ret);
2158 case QAquaSizeSmall:
2159 GetThemeMetric(kThemeMetricSmallCheckBoxHeight, &ret);
2163 case PM_IndicatorWidth: {
2164 switch (d->aquaSizeConstrain(opt, widget)) {
2165 case QAquaSizeUnknown:
2166 case QAquaSizeLarge:
2167 GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
2170 GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
2172 case QAquaSizeSmall:
2173 GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
2178 case PM_ExclusiveIndicatorHeight: {
2179 switch (d->aquaSizeConstrain(opt, widget)) {
2180 case QAquaSizeUnknown:
2181 case QAquaSizeLarge:
2182 GetThemeMetric(kThemeMetricRadioButtonHeight, &ret);
2185 GetThemeMetric(kThemeMetricMiniRadioButtonHeight, &ret);
2187 case QAquaSizeSmall:
2188 GetThemeMetric(kThemeMetricSmallRadioButtonHeight, &ret);
2192 case PM_ExclusiveIndicatorWidth: {
2193 switch (d->aquaSizeConstrain(opt, widget)) {
2194 case QAquaSizeUnknown:
2195 case QAquaSizeLarge:
2196 GetThemeMetric(kThemeMetricRadioButtonWidth, &ret);
2199 GetThemeMetric(kThemeMetricMiniRadioButtonWidth, &ret);
2201 case QAquaSizeSmall:
2202 GetThemeMetric(kThemeMetricSmallRadioButtonWidth, &ret);
2207 case PM_MenuVMargin:
2210 case PM_MenuPanelWidth:
2213 case PM_ToolTipLabelFrameWidth:
2216 case PM_SizeGripSize: {
2217 QAquaWidgetSize aSize;
2218 if (widget && widget->window()->windowType() == Qt::Tool)
2219 aSize = QAquaSizeSmall;
2221 aSize = QAquaSizeLarge;
2222 const QSize size = qt_aqua_get_known_size(CT_SizeGrip, widget, QSize(), aSize);
2225 case PM_MdiSubWindowFrameWidth:
2228 case PM_DockWidgetFrameWidth:
2231 case PM_DockWidgetTitleMargin:
2234 case PM_DockWidgetSeparatorExtent:
2237 case PM_ToolBarHandleExtent:
2240 case PM_ToolBarItemMargin:
2243 case PM_ToolBarItemSpacing:
2246 case PM_SplitterWidth:
2247 ret = qMax(7, QApplication::globalStrut().width());
2249 case PM_LayoutLeftMargin:
2250 case PM_LayoutTopMargin:
2251 case PM_LayoutRightMargin:
2252 case PM_LayoutBottomMargin:
2254 bool isWindow = false;
2256 isWindow = (opt->state & State_Window);
2257 } else if (widget) {
2258 isWindow = widget->isWindow();
2262 bool isMetal = widget && widget->testAttribute(Qt::WA_MacBrushedMetal);
2264 if (metric == PM_LayoutTopMargin) {
2265 return_SIZE(9 /* AHIG */, 6 /* guess */, 6 /* guess */);
2266 } else if (metric == PM_LayoutBottomMargin) {
2267 return_SIZE(18 /* AHIG */, 15 /* guess */, 13 /* guess */);
2269 return_SIZE(14 /* AHIG */, 11 /* guess */, 9 /* guess */);
2273 AHIG would have (20, 8, 10) here but that makes
2274 no sense. It would also have 14 for the top margin
2275 but this contradicts both Builder and most
2278 return_SIZE(20, 10, 10); // AHIG
2281 // hack to detect QTabWidget
2282 if (widget && widget->parentWidget()
2283 && widget->parentWidget()->sizePolicy().controlType() == QSizePolicy::TabWidget) {
2284 if (metric == PM_LayoutTopMargin) {
2286 Builder would have 14 (= 20 - 6) instead of 12,
2287 but that makes the tab look disproportionate.
2289 return_SIZE(12, 6, 6); // guess
2291 return_SIZE(20 /* Builder */, 8 /* guess */, 8 /* guess */);
2295 Child margins are highly inconsistent in AHIG and Builder.
2297 return_SIZE(12, 8, 6); // guess
2301 case PM_LayoutHorizontalSpacing:
2302 case PM_LayoutVerticalSpacing:
2304 case QStyle::PM_TabBarTabHSpace:
2305 switch (d->aquaSizeConstrain(opt, widget)) {
2306 case QAquaSizeLarge:
2307 case QAquaSizeUnknown:
2308 ret = QWindowsStyle::pixelMetric(metric, opt, widget);
2310 case QAquaSizeSmall:
2318 case PM_MenuHMargin:
2321 case PM_ToolBarFrameWidth:
2324 if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(widget->parent()))
2325 if (mainWindow->unifiedTitleAndToolBarOnMac())
2330 ret = QWindowsStyle::pixelMetric(metric, opt, widget);
2336 QPalette QMacStyle::standardPalette() const
2338 QPalette pal = QWindowsStyle::standardPalette();
2339 pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
2340 pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
2341 pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
2345 int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
2346 QStyleHintReturn *hret) const
2350 case SH_Menu_SelectionWrap:
2353 case SH_Menu_KeyboardSearch:
2356 case SH_Menu_SpaceActivatesItem:
2359 case SH_Slider_AbsoluteSetButtons:
2360 ret = Qt::LeftButton|Qt::MidButton;
2362 case SH_Slider_PageSetButtons:
2365 case SH_ScrollBar_ContextMenu:
2368 case SH_TitleBar_AutoRaise:
2371 case SH_Menu_AllowActiveAndDisabled:
2374 case SH_Menu_SubMenuPopupDelay:
2377 case SH_ScrollBar_LeftClickAbsolutePosition: {
2378 extern bool qt_scrollbar_jump_to_pos; //qapplication_mac.cpp
2379 if(QApplication::keyboardModifiers() & Qt::AltModifier)
2380 ret = !qt_scrollbar_jump_to_pos;
2382 ret = qt_scrollbar_jump_to_pos;
2384 case SH_TabBar_PreferNoArrows:
2387 case SH_LineEdit_PasswordCharacter:
2388 ret = kBulletUnicode;
2391 case SH_DialogButtons_DefaultButton:
2392 ret = QDialogButtons::Reject;
2395 case SH_GroupBox_TextLabelVerticalAlignment:
2398 case SH_ScrollView_FrameOnlyAroundContents:
2399 if (w && (w->isWindow() || !w->parentWidget() || w->parentWidget()->isWindow())
2400 && (w->inherits("QWorkspaceChild")
2402 || w->inherits("QScrollView")
2407 ret = QWindowsStyle::styleHint(sh, opt, w, hret);
2409 case SH_Menu_FillScreenWithScroll:
2412 case SH_Menu_Scrollable:
2415 case SH_RichText_FullWidthSelection:
2418 case SH_BlinkCursorWhenTextSelected:
2421 case SH_ScrollBar_StopMouseOverSlider:
2424 case SH_Q3ListViewExpand_SelectMouseType:
2425 ret = QEvent::MouseButtonRelease;
2427 case SH_TabBar_SelectMouseType:
2428 if (const QStyleOptionTabBarBaseV2 *opt2 = qstyleoption_cast<const QStyleOptionTabBarBaseV2 *>(opt)) {
2429 ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease;
2431 ret = QEvent::MouseButtonRelease;
2434 case SH_ComboBox_Popup:
2435 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt))
2436 ret = !cmb->editable;
2440 case SH_Workspace_FillSpaceOnMaximize:
2443 case SH_Widget_ShareActivation:
2446 case SH_Header_ArrowAlignment:
2447 ret = Qt::AlignRight;
2449 case SH_TabBar_Alignment: {
2450 if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) {
2451 if (tab->documentMode()) {
2452 ret = Qt::AlignLeft;
2456 if (const QTabBar *tab = qobject_cast<const QTabBar*>(w)) {
2457 if (tab->documentMode()) {
2458 ret = Qt::AlignLeft;
2462 ret = Qt::AlignCenter;
2464 case SH_UnderlineShortcut:
2467 case SH_ToolTipLabel_Opacity:
2468 ret = 242; // About 95%
2470 case SH_Button_FocusPolicy:
2473 case SH_EtchDisabledText:
2476 case SH_FocusFrame_Mask: {
2478 if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
2479 const uchar fillR = 192, fillG = 191, fillB = 190;
2482 QSize pixmapSize = opt->rect.size();
2483 if (pixmapSize.isValid()) {
2484 QPixmap pix(pixmapSize);
2485 pix.fill(QColor(fillR, fillG, fillB));
2486 QPainter pix_paint(&pix);
2487 proxy()->drawControl(CE_FocusFrame, opt, &pix_paint, w);
2489 img = pix.toImage();
2492 const QRgb *sptr = (QRgb*)img.bits(), *srow;
2493 const int sbpl = img.bytesPerLine();
2494 const int w = sbpl/4, h = img.height();
2496 QImage img_mask(img.width(), img.height(), QImage::Format_ARGB32);
2497 QRgb *dptr = (QRgb*)img_mask.bits(), *drow;
2498 const int dbpl = img_mask.bytesPerLine();
2500 for (int y = 0; y < h; ++y) {
2501 srow = sptr+((y*sbpl)/4);
2502 drow = dptr+((y*dbpl)/4);
2503 for (int x = 0; x < w; ++x) {
2504 const int diff = (((qRed(*srow)-fillR)*(qRed(*srow)-fillR)) +
2505 ((qGreen(*srow)-fillG)*((qGreen(*srow)-fillG))) +
2506 ((qBlue(*srow)-fillB)*((qBlue(*srow)-fillB))));
2507 (*drow++) = (diff < 100) ? 0xffffffff : 0xff000000;
2511 QBitmap qmask = QBitmap::fromImage(img_mask);
2512 mask->region = QRegion(qmask);
2515 case SH_TitleBar_NoBorder:
2518 case SH_RubberBand_Mask:
2521 case SH_ComboBox_LayoutDirection:
2522 ret = Qt::LeftToRight;
2524 case SH_ItemView_EllipsisLocation:
2525 ret = Qt::AlignHCenter;
2527 case SH_ItemView_ShowDecorationSelected:
2530 case SH_TitleBar_ModifyNotification:
2533 case SH_ScrollBar_RollBetweenButtons:
2536 case SH_WindowFrame_Mask:
2538 if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(hret)) {
2539 mask->region = opt->rect;
2540 mask->region -= QRect(opt->rect.left(), opt->rect.top(), 5, 1);
2541 mask->region -= QRect(opt->rect.left(), opt->rect.top() + 1, 3, 1);
2542 mask->region -= QRect(opt->rect.left(), opt->rect.top() + 2, 2, 1);
2543 mask->region -= QRect(opt->rect.left(), opt->rect.top() + 3, 1, 2);
2545 mask->region -= QRect(opt->rect.right() - 4, opt->rect.top(), 5, 1);
2546 mask->region -= QRect(opt->rect.right() - 2, opt->rect.top() + 1, 3, 1);
2547 mask->region -= QRect(opt->rect.right() - 1, opt->rect.top() + 2, 2, 1);
2548 mask->region -= QRect(opt->rect.right() , opt->rect.top() + 3, 1, 2);
2551 case SH_TabBar_ElideMode:
2552 ret = Qt::ElideRight;
2554 case SH_DialogButtonLayout:
2555 ret = QDialogButtonBox::MacLayout;
2557 case SH_FormLayoutWrapPolicy:
2558 ret = QFormLayout::DontWrapRows;
2560 case SH_FormLayoutFieldGrowthPolicy:
2561 ret = QFormLayout::FieldsStayAtSizeHint;
2563 case SH_FormLayoutFormAlignment:
2564 ret = Qt::AlignHCenter | Qt::AlignTop;
2566 case SH_FormLayoutLabelAlignment:
2567 ret = Qt::AlignRight;
2569 case SH_ComboBox_PopupFrameStyle:
2570 ret = QFrame::NoFrame | QFrame::Plain;
2572 case SH_MessageBox_TextInteractionFlags:
2573 ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard;
2575 case SH_SpellCheckUnderlineStyle:
2576 ret = QTextCharFormat::DashUnderline;
2578 case SH_MessageBox_CenterButtons:
2581 case SH_MenuBar_AltKeyNavigation:
2584 case SH_ItemView_MovementWithoutUpdatingSelection:
2587 case SH_FocusFrame_AboveWidget:
2590 case SH_WizardStyle:
2591 ret = QWizard::MacStyle;
2593 case SH_ItemView_ArrowKeysNavigateIntoChildren:
2596 case SH_Menu_FlashTriggeredItem:
2599 case SH_Menu_FadeOutOnHide:
2604 if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
2606 HIRect menuRect = CGRectMake(opt->rect.x(), opt->rect.y() + 4,
2607 opt->rect.width(), opt->rect.height() - 8);
2608 HIThemeMenuDrawInfo mdi;
2610 if (w && qobject_cast<QMenu *>(w->parentWidget()))
2611 mdi.menuType = kThemeMenuTypeHierarchical;
2613 mdi.menuType = kThemeMenuTypePopUp;
2614 QCFType<HIShapeRef> shape;
2615 HIThemeGetMenuBackgroundShape(&menuRect, &mdi, &shape);
2616 mask->region = QRegion::fromHIShapeRef(shape);
2620 case SH_ItemView_PaintAlternatingRowColorsForEmptyArea:
2623 case SH_TabBar_CloseButtonPosition:
2624 ret = QTabBar::LeftSide;
2626 case SH_DockWidget_ButtonsHaveFrame:
2630 ret = QWindowsStyle::styleHint(sh, opt, w, hret);
2636 QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
2637 const QStyleOption *opt) const
2640 case QIcon::Disabled: {
2641 QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
2642 int imgh = img.height();
2643 int imgw = img.width();
2645 for (int y = 0; y < imgh; ++y) {
2646 for (int x = 0; x < imgw; ++x) {
2647 pixel = img.pixel(x, y);
2648 img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel),
2649 qAlpha(pixel) / 2));
2652 return QPixmap::fromImage(img);
2657 return QWindowsStyle::generatedIconPixmap(iconMode, pixmap, opt);
2661 QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
2662 const QWidget *widget) const
2664 // The default implementation of QStyle::standardIconImplementation() is to call standardPixmap()
2665 // I don't want infinite recursion so if we do get in that situation, just return the Window's
2666 // standard pixmap instead (since there is no mac-specific icon then). This should be fine until
2667 // someone changes how Windows standard
2669 static bool recursionGuard = false;
2672 return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
2674 recursionGuard = true;
2675 QIcon icon = standardIconImplementation(standardPixmap, opt, widget);
2676 recursionGuard = false;
2678 switch (standardPixmap) {
2682 case SP_MessageBoxCritical:
2683 case SP_MessageBoxQuestion:
2684 case SP_MessageBoxInformation:
2685 case SP_MessageBoxWarning:
2689 return icon.pixmap(size, size);
2692 void QMacStyle::setFocusRectPolicy(QWidget *w, FocusRectPolicy policy)
2699 w->setAttribute(Qt::WA_MacShowFocusRect, policy == FocusEnabled);
2704 QMacStyle::FocusRectPolicy QMacStyle::focusRectPolicy(const QWidget *w)
2706 return w->testAttribute(Qt::WA_MacShowFocusRect) ? FocusEnabled : FocusDisabled;
2709 void QMacStyle::setWidgetSizePolicy(const QWidget *widget, WidgetSizePolicy policy)
2711 QWidget *wadget = const_cast<QWidget *>(widget);
2712 wadget->setAttribute(Qt::WA_MacNormalSize, policy == SizeLarge);
2713 wadget->setAttribute(Qt::WA_MacSmallSize, policy == SizeSmall);
2714 wadget->setAttribute(Qt::WA_MacMiniSize, policy == SizeMini);
2717 QMacStyle::WidgetSizePolicy QMacStyle::widgetSizePolicy(const QWidget *widget)
2720 if (widget->testAttribute(Qt::WA_MacMiniSize)) {
2722 } else if (widget->testAttribute(Qt::WA_MacSmallSize)) {
2724 } else if (widget->testAttribute(Qt::WA_MacNormalSize)) {
2727 widget = widget->parentWidget();
2732 void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
2733 const QWidget *w) const
2735 ThemeDrawState tds = d->getDrawState(opt->state);
2736 QMacCGContext cg(p);
2738 case PE_IndicatorArrowUp:
2739 case PE_IndicatorArrowDown:
2740 case PE_IndicatorArrowRight:
2741 case PE_IndicatorArrowLeft: {
2743 p->setRenderHint(QPainter::Antialiasing);
2744 int xOffset = opt->direction == Qt::LeftToRight ? 2 : -1;
2746 matrix.translate(opt->rect.center().x() + xOffset, opt->rect.center().y() + 2);
2750 case PE_IndicatorArrowDown:
2752 case PE_IndicatorArrowUp:
2755 case PE_IndicatorArrowLeft:
2758 case PE_IndicatorArrowRight:
2763 path.lineTo(-4, -3);
2765 p->setMatrix(matrix);
2766 p->setPen(Qt::NoPen);
2767 p->setBrush(QColor(0, 0, 0, 135));
2771 case PE_FrameTabBarBase:
2772 if (const QStyleOptionTabBarBaseV2 *tbb
2773 = qstyleoption_cast<const QStyleOptionTabBarBaseV2 *>(opt)) {
2774 if (tbb->documentMode) {
2776 drawTabBase(p, tbb, w);
2781 QRegion region(tbb->rect);
2782 region -= tbb->tabBarRect;
2784 p->setClipRegion(region);
2785 QStyleOptionTabWidgetFrame twf;
2786 twf.QStyleOption::operator=(*tbb);
2787 twf.shape = tbb->shape;
2788 switch (getTabDirection(twf.shape)) {
2789 case kThemeTabNorth:
2790 twf.rect = twf.rect.adjusted(0, 0, 0, 10);
2792 case kThemeTabSouth:
2793 twf.rect = twf.rect.adjusted(0, -10, 0, 0);
2796 twf.rect = twf.rect.adjusted(0, 0, 10, 0);
2799 twf.rect = twf.rect.adjusted(0, -10, 0, 0);
2802 proxy()->drawPrimitive(PE_FrameTabWidget, &twf, p, w);
2806 case PE_PanelTipLabel:
2807 p->fillRect(opt->rect, opt->palette.brush(QPalette::ToolTipBase));
2809 case PE_FrameGroupBox:
2810 if (const QStyleOptionFrame *groupBox = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
2811 const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(opt);
2812 if (frame2 && frame2->features & QStyleOptionFrameV2::Flat) {
2813 QWindowsStyle::drawPrimitive(pe, groupBox, p, w);
2815 HIThemeGroupBoxDrawInfo gdi;
2816 gdi.version = qt_mac_hitheme_version;
2818 if (w && qobject_cast<QGroupBox *>(w->parentWidget()))
2819 gdi.kind = kHIThemeGroupBoxKindSecondary;
2821 gdi.kind = kHIThemeGroupBoxKindPrimary;
2822 HIRect hirect = qt_hirectForQRect(opt->rect);
2823 HIThemeDrawGroupBox(&hirect, &gdi, cg, kHIThemeOrientationNormal);
2827 case PE_IndicatorToolBarSeparator: {
2829 if (opt->state & State_Horizontal) {
2830 int xpoint = opt->rect.center().x();
2831 path.moveTo(xpoint + 0.5, opt->rect.top() + 1);
2832 path.lineTo(xpoint + 0.5, opt->rect.bottom());
2834 int ypoint = opt->rect.center().y();
2835 path.moveTo(opt->rect.left() + 2 , ypoint + 0.5);
2836 path.lineTo(opt->rect.right() + 1, ypoint + 0.5);
2838 QPainterPathStroker theStroker;
2839 theStroker.setCapStyle(Qt::FlatCap);
2840 theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
2841 path = theStroker.createStroke(path);
2842 p->fillPath(path, QColor(0, 0, 0, 119));
2845 case PE_FrameWindow:
2847 case PE_IndicatorDockWidgetResizeHandle: {
2848 // The docwidget resize handle is drawn as a one-pixel wide line.
2850 if (opt->state & State_Horizontal) {
2851 p->setPen(QColor(160, 160, 160));
2852 p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
2854 p->setPen(QColor(145, 145, 145));
2855 p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
2859 case PE_IndicatorToolBarHandle: {
2862 int x = opt->rect.x() + 6;
2863 int y = opt->rect.y() + 5;
2864 static const int RectHeight = 2;
2865 if (opt->state & State_Horizontal) {
2866 while (y < opt->rect.height() - RectHeight - 6) {
2868 path.addRect(x, y, RectHeight, RectHeight);
2872 while (x < opt->rect.width() - RectHeight - 6) {
2874 path.addRect(x, y, RectHeight, RectHeight);
2878 p->setPen(Qt::NoPen);
2879 QColor dark = opt->palette.dark().color();
2880 dark.setAlphaF(0.75);
2881 QColor light = opt->palette.light().color();
2882 light.setAlphaF(0.6);
2883 p->fillPath(path, light);
2886 p->fillPath(path, dark);
2889 p->fillPath(path, light);
2891 p->fillPath(path, dark);
2896 case PE_IndicatorHeaderArrow:
2897 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
2898 // In HITheme, up is down, down is up and hamburgers eat people.
2899 if (header->sortIndicator != QStyleOptionHeader::None)
2900 proxy()->drawPrimitive(
2901 (header->sortIndicator == QStyleOptionHeader::SortDown) ?
2902 PE_IndicatorArrowUp : PE_IndicatorArrowDown, header, p, w);
2905 case PE_IndicatorMenuCheckMark: {
2906 const int checkw = 8;
2907 const int checkh = 8;
2908 const int xoff = qMax(0, (opt->rect.width() - checkw) / 2);
2909 const int yoff = qMax(0, (opt->rect.width() - checkh) / 2);
2910 const int x1 = xoff + opt->rect.x();
2911 const int y1 = yoff + opt->rect.y() + checkw/2;
2912 const int x2 = xoff + opt->rect.x() + checkw/4;
2913 const int y2 = yoff + opt->rect.y() + checkh;
2914 const int x3 = xoff + opt->rect.x() + checkw;
2915 const int y3 = yoff + opt->rect.y();
2917 QVector<QLineF> a(2);
2918 a << QLineF(x1, y1, x2, y2);
2919 a << QLineF(x2, y2, x3, y3);
2920 if (opt->palette.currentColorGroup() == QPalette::Active) {
2921 if (opt->state & State_On)
2922 p->setPen(QPen(opt->palette.highlightedText().color(), 3));
2924 p->setPen(QPen(opt->palette.text().color(), 3));
2926 p->setPen(QPen(QColor(100, 100, 100), 3));
2929 p->setRenderHint(QPainter::Antialiasing);
2933 case PE_IndicatorViewItemCheck:
2934 case PE_Q3CheckListExclusiveIndicator:
2935 case PE_Q3CheckListIndicator:
2936 case PE_IndicatorRadioButton:
2937 case PE_IndicatorCheckBox: {
2938 bool drawColorless = (!(opt->state & State_Active))
2939 && opt->palette.currentColorGroup() == QPalette::Active;
2940 HIThemeButtonDrawInfo bdi;
2941 bdi.version = qt_mac_hitheme_version;
2943 if (drawColorless && tds == kThemeStateInactive)
2944 bdi.state = kThemeStateActive;
2945 bdi.adornment = kThemeDrawIndicatorOnly;
2946 if (opt->state & State_HasFocus)
2947 bdi.adornment |= kThemeAdornmentFocus;
2948 bool isRadioButton = (pe == PE_Q3CheckListExclusiveIndicator
2949 || pe == PE_IndicatorRadioButton);
2950 switch (d->aquaSizeConstrain(opt, w)) {
2951 case QAquaSizeUnknown:
2952 case QAquaSizeLarge:
2954 bdi.kind = kThemeRadioButton;
2956 bdi.kind = kThemeCheckBox;
2960 bdi.kind = kThemeMiniRadioButton;
2962 bdi.kind = kThemeMiniCheckBox;
2964 case QAquaSizeSmall:
2966 bdi.kind = kThemeSmallRadioButton;
2968 bdi.kind = kThemeSmallCheckBox;
2971 if (opt->state & State_NoChange)
2972 bdi.value = kThemeButtonMixed;
2973 else if (opt->state & State_On)
2974 bdi.value = kThemeButtonOn;
2976 bdi.value = kThemeButtonOff;
2978 if (pe == PE_Q3CheckListExclusiveIndicator || pe == PE_Q3CheckListIndicator)
2979 macRect = qt_hirectForQRect(opt->rect);
2981 macRect = qt_hirectForQRect(opt->rect);
2983 HIThemeDrawButton(&macRect, &bdi, cg, kHIThemeOrientationNormal, 0);
2985 d->drawColorlessButton(macRect, &bdi, p, opt);
2987 case PE_FrameFocusRect:
2988 // Use the our own focus widget stuff.
2990 case PE_IndicatorBranch: {
2991 if (!(opt->state & State_Children))
2993 HIThemeButtonDrawInfo bi;
2994 bi.version = qt_mac_hitheme_version;
2996 if (tds == kThemeStateInactive && opt->palette.currentColorGroup() == QPalette::Active)
2997 bi.state = kThemeStateActive;
2998 if (opt->state & State_Sunken)
2999 bi.state |= kThemeStatePressed;
3000 bi.kind = kThemeDisclosureButton;
3001 if (opt->state & State_Open)
3002 bi.value = kThemeDisclosureDown;
3004 bi.value = opt->direction == Qt::LeftToRight ? kThemeDisclosureRight : kThemeDisclosureLeft;
3005 bi.adornment = kThemeAdornmentNone;
3006 HIRect hirect = qt_hirectForQRect(opt->rect.adjusted(DisclosureOffset,0,-DisclosureOffset,0));
3007 HIThemeDrawButton(&hirect, &bi, cg, kHIThemeOrientationNormal, 0);
3011 QPen oldPen = p->pen();
3012 p->setPen(opt->palette.base().color().darker(140));
3013 p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
3014 p->setPen(opt->palette.base().color().darker(180));
3015 p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
3019 case PE_FrameLineEdit:
3020 if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
3021 if (frame->state & State_Sunken) {
3022 QColor baseColor(frame->palette.background().color());
3023 HIThemeFrameDrawInfo fdi;
3024 fdi.version = qt_mac_hitheme_version;
3027 if (pe == PE_FrameLineEdit) {
3028 fdi.kind = kHIThemeFrameTextFieldSquare;
3029 GetThemeMetric(kThemeMetricEditTextFrameOutset, &frame_size);
3030 if ((frame->state & State_ReadOnly) || !(frame->state & State_Enabled))
3031 fdi.state = kThemeStateInactive;
3033 baseColor = QColor(150, 150, 150); //hardcoded since no query function --Sam
3034 fdi.kind = kHIThemeFrameListBox;
3035 GetThemeMetric(kThemeMetricListBoxFrameOutset, &frame_size);
3037 fdi.isFocused = (frame->state & State_HasFocus);
3038 int lw = frame->lineWidth;
3040 lw = proxy()->pixelMetric(PM_DefaultFrameWidth, frame, w);
3041 { //clear to base color
3043 p->setPen(QPen(baseColor, lw));
3044 p->setBrush(Qt::NoBrush);
3045 p->drawRect(frame->rect);
3048 HIRect hirect = qt_hirectForQRect(frame->rect,
3049 QRect(frame_size, frame_size,
3050 frame_size * 2, frame_size * 2));
3052 HIThemeDrawFrame(&hirect, &fdi, cg, kHIThemeOrientationNormal);
3054 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3058 case PE_PanelLineEdit:
3059 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3060 // Draw the focus frame for widgets other than QLineEdit (e.g. for line edits in Webkit).
3061 // Focus frame is drawn outside the rectangle passed in the option-rect.
3062 if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
3063 if ((opt->state & State_HasFocus) && !qobject_cast<const QLineEdit*>(w)) {
3064 int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin);
3065 int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin);
3066 QStyleOptionFrame focusFrame = *panel;
3067 focusFrame.rect = panel->rect.adjusted(-hmargin, -vmargin, hmargin, vmargin);
3068 drawControl(CE_FocusFrame, &focusFrame, p, w);
3073 case PE_FrameTabWidget:
3074 if (const QStyleOptionTabWidgetFrame *twf
3075 = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
3076 HIRect hirect = qt_hirectForQRect(twf->rect);
3077 HIThemeTabPaneDrawInfo tpdi;
3078 tpdi.version = qt_mac_hitheme_tab_version();
3080 tpdi.direction = getTabDirection(twf->shape);
3081 tpdi.size = kHIThemeTabSizeNormal;
3082 tpdi.kind = kHIThemeTabKindNormal;
3083 tpdi.adornment = kHIThemeTabPaneAdornmentNormal;
3084 HIThemeDrawTabPane(&hirect, &tpdi, cg, kHIThemeOrientationNormal);
3087 case PE_PanelScrollAreaCorner: {
3088 const QBrush brush(opt->palette.brush(QPalette::Base));
3089 p->fillRect(opt->rect, brush);
3090 p->setPen(QPen(QColor(217, 217, 217)));
3091 p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
3092 p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
3094 case PE_FrameStatusBarItem:
3096 case PE_IndicatorTabClose: {
3097 bool hover = (opt->state & State_MouseOver);
3098 bool selected = (opt->state & State_Selected);
3099 bool active = (opt->state & State_Active);
3100 drawTabCloseButton(p, hover, active, selected);
3102 case PE_PanelStatusBar: {
3103 if (QSysInfo::MacintoshVersion <= QSysInfo::MV_10_4) {
3104 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3107 // Use the Leopard style only if the status bar is the status bar for a
3108 // QMainWindow with a unifed toolbar.
3109 if (w == 0 || w->parent() == 0 || qobject_cast<QMainWindow *>(w->parent()) == 0 ||
3110 qobject_cast<QMainWindow *>(w->parent())->unifiedTitleAndToolBarOnMac() == false ) {
3111 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3115 // Fill the status bar with the titlebar gradient.
3116 QLinearGradient linearGrad(0, opt->rect.top(), 0, opt->rect.bottom());
3117 if (opt->state & QStyle::State_Active) {
3118 linearGrad.setColorAt(0, titlebarGradientActiveBegin);
3119 linearGrad.setColorAt(1, titlebarGradientActiveEnd);
3121 linearGrad.setColorAt(0, titlebarGradientInactiveBegin);
3122 linearGrad.setColorAt(1, titlebarGradientInactiveEnd);
3124 p->fillRect(opt->rect, linearGrad);
3126 // Draw the black separator line at the top of the status bar.
3127 if (opt->state & QStyle::State_Active)
3128 p->setPen(titlebarSeparatorLineActive);
3130 p->setPen(titlebarSeparatorLineInactive);
3131 p->drawLine(opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.top());
3137 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3142 static inline QPixmap darkenPixmap(const QPixmap &pixmap)
3144 QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
3145 int imgh = img.height();
3146 int imgw = img.width();
3149 for (int y = 0; y < imgh; ++y) {
3150 for (int x = 0; x < imgw; ++x) {
3151 pixel = img.pixel(x, y);
3153 QColor hsvColor(pixel);
3154 hsvColor.getHsv(&h, &s, &v);
3155 s = qMin(100, s * 2);
3157 hsvColor.setHsv(h, s, v);
3158 pixel = hsvColor.rgb();
3159 img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), a));
3162 return QPixmap::fromImage(img);
3167 void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
3168 const QWidget *w) const
3170 ThemeDrawState tds = d->getDrawState(opt->state);
3171 QMacCGContext cg(p);
3173 case CE_HeaderSection:
3174 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
3175 HIThemeButtonDrawInfo bdi;
3176 bdi.version = qt_mac_hitheme_version;
3177 State flags = header->state;
3178 QRect ir = header->rect;
3179 bdi.kind = kThemeListHeaderButton;
3180 bdi.adornment = kThemeAdornmentNone;
3181 bdi.state = kThemeStateActive;
3183 if (flags & State_On)
3184 bdi.value = kThemeButtonOn;
3186 bdi.value = kThemeButtonOff;
3188 if (header->orientation == Qt::Horizontal){
3189 switch (header->position) {
3190 case QStyleOptionHeader::Beginning:
3191 ir.adjust(-1, -1, 0, 0);
3193 case QStyleOptionHeader::Middle:
3194 ir.adjust(-1, -1, 0, 0);
3196 case QStyleOptionHeader::OnlyOneSection:
3197 case QStyleOptionHeader::End:
3198 ir.adjust(-1, -1, 1, 0);
3204 if (header->position != QStyleOptionHeader::Beginning
3205 && header->position != QStyleOptionHeader::OnlyOneSection) {
3206 bdi.adornment = header->direction == Qt::LeftToRight
3207 ? kThemeAdornmentHeaderButtonLeftNeighborSelected
3208 : kThemeAdornmentHeaderButtonRightNeighborSelected;
3212 if (flags & State_Active) {
3213 if (!(flags & State_Enabled))
3214 bdi.state = kThemeStateUnavailable;
3215 else if (flags & State_Sunken)
3216 bdi.state = kThemeStatePressed;
3218 if (flags & State_Enabled)
3219 bdi.state = kThemeStateInactive;
3221 bdi.state = kThemeStateUnavailableInactive;
3224 if (header->sortIndicator != QStyleOptionHeader::None) {
3225 bdi.value = kThemeButtonOn;
3226 if (header->sortIndicator == QStyleOptionHeader::SortDown)
3227 bdi.adornment = kThemeAdornmentHeaderButtonSortUp;
3229 if (flags & State_HasFocus)
3230 bdi.adornment = kThemeAdornmentFocus;
3232 ir = visualRect(header->direction, header->rect, ir);
3233 HIRect bounds = qt_hirectForQRect(ir);
3235 bool noVerticalHeader = true;
3237 if (const QTableView *table = qobject_cast<const QTableView *>(w->parentWidget()))
3238 noVerticalHeader = !table->verticalHeader()->isVisible();
3240 bool drawTopBorder = header->orientation == Qt::Horizontal;
3241 bool drawLeftBorder = header->orientation == Qt::Vertical
3242 || header->position == QStyleOptionHeader::OnlyOneSection
3243 || (header->position == QStyleOptionHeader::Beginning && noVerticalHeader);
3244 d->drawTableHeader(bounds, drawTopBorder, drawLeftBorder, bdi, p);
3247 case CE_HeaderLabel:
3248 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
3249 QRect textr = header->rect;
3250 if (!header->icon.isNull()) {
3251 QIcon::Mode mode = QIcon::Disabled;
3252 if (opt->state & State_Enabled)
3253 mode = QIcon::Normal;
3254 QPixmap pixmap = header->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize), mode);
3256 QRect pixr = header->rect;
3257 pixr.setY(header->rect.center().y() - (pixmap.height() - 1) / 2);
3258 proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap);
3259 textr.translate(pixmap.width() + 2, 0);
3262 proxy()->drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette,
3263 header->state & State_Enabled, header->text, QPalette::ButtonText);
3266 case CE_ToolButtonLabel:
3267 if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
3268 QStyleOptionToolButton myTb = *tb;
3269 myTb.state &= ~State_AutoRaise;
3270 if (w && qobject_cast<QToolBar *>(w->parentWidget())) {
3271 QRect cr = tb->rect;
3274 bool needText = false;
3276 bool down = tb->state & (State_Sunken | State_On);
3278 shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb, w);
3279 shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, tb, w);
3281 // The down state is special for QToolButtons in a toolbar on the Mac
3282 // The text is a bit bolder and gets a drop shadow and the icons are also darkened.
3283 // This doesn't really fit into any particular case in QIcon, so we
3284 // do the majority of the work ourselves.
3285 if (!(tb->features & QStyleOptionToolButton::Arrow)) {
3286 Qt::ToolButtonStyle tbstyle = tb->toolButtonStyle;
3287 if (tb->icon.isNull() && !tb->text.isEmpty())
3288 tbstyle = Qt::ToolButtonTextOnly;
3291 case Qt::ToolButtonTextOnly: {
3293 alignment = Qt::AlignCenter;
3295 case Qt::ToolButtonIconOnly:
3296 case Qt::ToolButtonTextBesideIcon:
3297 case Qt::ToolButtonTextUnderIcon: {
3299 QIcon::Mode iconMode = (tb->state & State_Enabled) ? QIcon::Normal
3301 QIcon::State iconState = (tb->state & State_On) ? QIcon::On
3303 QPixmap pixmap = tb->icon.pixmap(tb->rect.size().boundedTo(tb->iconSize), iconMode, iconState);
3305 // Draw the text if it's needed.
3306 if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) {
3308 if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
3309 QMainWindow *mw = qobject_cast<QMainWindow *>(w->window());
3310 if (mw && mw->unifiedTitleAndToolBarOnMac()) {
3311 pr.setHeight(pixmap.size().height());
3312 cr.adjust(0, pr.bottom() + 1, 0, 1);
3314 pr.setHeight(pixmap.size().height() + 6);
3315 cr.adjust(0, pr.bottom(), 0, -3);
3317 alignment |= Qt::AlignCenter;
3319 pr.setWidth(pixmap.width() + 8);
3320 cr.adjust(pr.right(), 0, 0, 0);
3321 alignment |= Qt::AlignLeft | Qt::AlignVCenter;
3324 if (opt->state & State_Sunken) {