QMacStyle: smooth QMenu corners for real

It's been for years that QMenu's rounded corners in qmacstyle_mac were
done via QWidget::setMask(QRegion). Unfortunately, QRegion mask does not
work well with retina displays and also does not support translucency.
That's why in this change we explicitly make QMenu's background
transparent and then draw a rectangle with rounded corners in
QMacStyle::drawPrimitive(PE_PanelMenu). This not only gives much better
result than the mask-based approach, but also de-HIThemes QMenu.

As a consequence, QComboBoxPrivateContainer doesn't get any mask from
QMacStyle anymore. Therefore, when the mask is empty, we need to paint
PE_PanelMenu before invoking QFrame's paint event handler.

Made-with: Gabriel de Dietrich <gabriel.dedietrich@qt.io>
Change-Id: Ia9236176113f23b86e45507fa6ddf77236084ce3
Reviewed-by: Oleg Yadrov <oleg.yadrov@qt.io>
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@qt.io>
bb10
Oleg Yadrov 2017-10-02 10:27:10 -04:00 committed by Gabriel de Dietrich
parent 08e083e682
commit 5ff9d6a470
4 changed files with 56 additions and 52 deletions

View File

@ -144,7 +144,6 @@ QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes()
}
if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette
|| mac_widget_colors[i].paletteRole == QPlatformTheme::MenuBarPalette) {
pal.setBrush(QPalette::Background, qt_mac_toQColor([NSColor windowBackgroundColor]));
pal.setBrush(QPalette::Highlight, qt_mac_toQColor([NSColor selectedMenuItemColor]));
qc = qt_mac_toQColor([NSColor labelColor]);
pal.setBrush(QPalette::ButtonText, qc);

View File

@ -663,14 +663,6 @@ HIMutableShapeRef qt_mac_toHIMutableShape(const QRegion &region)
return shape;
}
QRegion qt_mac_fromHIShapeRef(HIShapeRef shape)
{
QRegion returnRegion;
//returnRegion.detach();
HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, qt_mac_shape2QRegionHelper, &returnRegion);
return returnRegion;
}
bool qt_macWindowIsTextured(const QWidget *window)
{
if (QWindow *w = window->windowHandle())
@ -2291,24 +2283,7 @@ void QMacStyle::polish(QWidget* w)
|| qobject_cast<QComboBoxPrivateContainer *>(w)
#endif
) {
w->setWindowOpacity(0.985);
if (!w->testAttribute(Qt::WA_SetPalette)) {
QPixmap px(64, 64);
px.fill(Qt::white);
HIThemeMenuDrawInfo mtinfo;
mtinfo.version = qt_mac_hitheme_version;
mtinfo.menuType = kThemeMenuTypePopUp;
// HIRect rect = CGRectMake(0, 0, px.width(), px.height());
// ###
//HIThemeDrawMenuBackground(&rect, &mtinfo, QMacCGContext(&px)),
// kHIThemeOrientationNormal);
QPalette pal = w->palette();
QBrush background(px);
pal.setBrush(QPalette::All, QPalette::Window, background);
pal.setBrush(QPalette::All, QPalette::Button, background);
w->setPalette(pal);
w->setAttribute(Qt::WA_SetPalette, false);
}
w->setAttribute(Qt::WA_TranslucentBackground, true);
}
#endif
@ -3023,7 +2998,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w
ret = Qt::AlignRight;
break;
case SH_ComboBox_PopupFrameStyle:
ret = QFrame::NoFrame | QFrame::Plain;
ret = QFrame::NoFrame;
break;
case SH_MessageBox_TextInteractionFlags:
ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard;
@ -3057,27 +3032,6 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w
case SH_Menu_FadeOutOnHide:
ret = true;
break;
case SH_Menu_Mask:
if (opt) {
if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
ret = true;
CGRect menuRect = CGRectMake(opt->rect.x(), opt->rect.y() + 4,
opt->rect.width(), opt->rect.height() - 8);
HIThemeMenuDrawInfo mdi;
mdi.version = 0;
#if QT_CONFIG(menu)
if (w && qobject_cast<QMenu *>(w->parentWidget()))
mdi.menuType = kThemeMenuTypeHierarchical;
else
#endif
mdi.menuType = kThemeMenuTypePopUp;
QCFType<HIShapeRef> shape;
HIThemeGetMenuBackgroundShape(&menuRect, &mdi, &shape);
mask->region = qt_mac_fromHIShapeRef(shape);
}
}
break;
case SH_ItemView_PaintAlternatingRowColorsForEmptyArea:
ret = true;
break;
@ -3587,6 +3541,40 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
break;
}
case PE_PanelMenu: {
p->save();
p->fillRect(opt->rect, Qt::transparent);
p->setPen(Qt::transparent);
p->setBrush(opt->palette.window());
p->setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
static const qreal CornerPointOffset = 5.5;
static const qreal CornerControlOffset = 2.1;
QRectF r = opt->rect;
// Top-left corner
path.moveTo(r.left(), r.top() + CornerPointOffset);
path.cubicTo(r.left(), r.top() + CornerControlOffset,
r.left() + CornerControlOffset, r.top(),
r.left() + CornerPointOffset, r.top());
// Top-right corner
path.lineTo(r.right() - CornerPointOffset, r.top());
path.cubicTo(r.right() - CornerControlOffset, r.top(),
r.right(), r.top() + CornerControlOffset,
r.right(), r.top() + CornerPointOffset);
// Bottom-right corner
path.lineTo(r.right(), r.bottom() - CornerPointOffset);
path.cubicTo(r.right(), r.bottom() - CornerControlOffset,
r.right() - CornerControlOffset, r.bottom(),
r.right() - CornerPointOffset, r.bottom());
// Bottom-right corner
path.lineTo(r.left() + CornerPointOffset, r.bottom());
path.cubicTo(r.left() + CornerControlOffset, r.bottom(),
r.left(), r.bottom() - CornerControlOffset,
r.left(), r.bottom() - CornerPointOffset);
path.lineTo(r.left(), r.top() + CornerPointOffset);
p->drawPath(path);
p->restore();
} break;
default:
QCommonStyle::drawPrimitive(pe, opt, p, w);
@ -4269,16 +4257,18 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt, w);
d->drawFocusRing(p, opt->rect, hMargin, vMargin);
break; }
case CE_MenuEmptyArea:
// Skip: PE_PanelMenu fills in everything
break;
case CE_MenuItem:
case CE_MenuHMargin:
case CE_MenuVMargin:
case CE_MenuEmptyArea:
case CE_MenuTearoff:
case CE_MenuScroller:
if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
const bool active = mi->state & State_Selected;
const QBrush bg = active ? mi->palette.highlight() : mi->palette.background();
p->fillRect(mi->rect, bg);
if (active)
p->fillRect(mi->rect, mi->palette.highlight());
const QStyleHelper::WidgetSizePolicy widgetSize = d->aquaSizeConstrain(opt, w);

View File

@ -428,6 +428,20 @@ void QComboBoxPrivateContainer::resizeEvent(QResizeEvent *e)
QFrame::resizeEvent(e);
}
void QComboBoxPrivateContainer::paintEvent(QPaintEvent *e)
{
QStyleOptionComboBox cbOpt = comboStyleOption();
if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &cbOpt, combo)
&& mask().isEmpty()) {
QStyleOption opt;
opt.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_PanelMenu, &opt, &p, this);
}
QFrame::paintEvent(e);
}
void QComboBoxPrivateContainer::leaveEvent(QEvent *)
{
// On Mac using the Mac style we want to clear the selection

View File

@ -248,6 +248,7 @@ protected:
void timerEvent(QTimerEvent *timerEvent) override;
void leaveEvent(QEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
QStyleOptionComboBox comboStyleOption() const;
Q_SIGNALS: