summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGabriel de Dietrich <gabriel.dedietrich@theqtcompany.com>2016-02-24 17:19:37 -0800
committerGabriel de Dietrich <gabriel.dedietrich@theqtcompany.com>2016-03-17 16:57:31 +0000
commit7c7ece94429d6e12e0543c275d26fd90501193ff (patch)
tree3718c38c165e4484c21e491c02791caaacdb1a4f /src
parentc6bf48dcbfd6b1048cddfd127b95dce27815709f (diff)
QMacStyle: Ensure proper focus ring clipping
By rendering the focus ring directly on the backing NSView, we would ignore the painter's clipping information. It would also require creating a custom CGContext and attached NSGraphicsContext every time. The first step is to render the focus ring on a pixmap and then use the painter to render that pixamp. This ensures the clipping is done properly. The second step is to cache said pixmap and render it as a nine-patch image. Change-Id: I1df1baf7dc490023319f025a16306d4f04e5264c Task-number: QTBUG-50645 Reviewed-by: Morten Johan Sørvig <morten.sorvig@theqtcompany.com>
Diffstat (limited to 'src')
-rw-r--r--src/widgets/styles/qmacstyle_mac.mm90
-rw-r--r--src/widgets/styles/qmacstyle_mac_p_p.h2
2 files changed, 70 insertions, 22 deletions
diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm
index 4f688f6f28..092cf4d543 100644
--- a/src/widgets/styles/qmacstyle_mac.mm
+++ b/src/widgets/styles/qmacstyle_mac.mm
@@ -1097,17 +1097,67 @@ static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSiz
}
#endif
-static void qt_drawFocusRingOnPath(CGContextRef cg, NSBezierPath *focusRingPath)
+void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius) const
{
- CGContextSaveGState(cg);
- [NSGraphicsContext saveGraphicsState];
- [NSGraphicsContext setCurrentContext:[NSGraphicsContext
- graphicsContextWithGraphicsPort:(CGContextRef)cg flipped:NO]];
- NSSetFocusRingStyle(NSFocusRingOnly);
- [focusRingPath setClip]; // Clear clip path to avoid artifacts when rendering the cursor at zero pos
- [focusRingPath fill];
- [NSGraphicsContext restoreGraphicsState];
- CGContextRestoreGState(cg);
+ qreal pixelRatio = p->device()->devicePixelRatioF();
+ static const QString keyFormat = QLatin1String("$qt_focusring%1-%2-%3-%4");
+ const QString &key = keyFormat.arg(hMargin).arg(vMargin).arg(radius).arg(pixelRatio);
+ QPixmap focusRingPixmap;
+ const qreal size = radius * 2 + 5;
+
+ if (!QPixmapCache::find(key, focusRingPixmap)) {
+ focusRingPixmap = QPixmap((QSize(size, size) + 2 * QSize(hMargin, vMargin)) * pixelRatio);
+ focusRingPixmap.fill(Qt::transparent);
+ focusRingPixmap.setDevicePixelRatio(pixelRatio);
+ {
+ QMacAutoReleasePool pool;
+ NSBezierPath *focusRingPath;
+ if (radius > 0)
+ focusRingPath = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(hMargin, vMargin, size, size)
+ xRadius:radius
+ yRadius:radius];
+ else
+ focusRingPath = [NSBezierPath bezierPathWithRect:NSMakeRect(hMargin, vMargin, size, size)];
+ [NSGraphicsContext saveGraphicsState];
+ QMacCGContext gc(&focusRingPixmap);
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:(CGContextRef)gc
+ flipped:NO]];
+ NSSetFocusRingStyle(NSFocusRingOnly);
+ [focusRingPath fill];
+ [NSGraphicsContext restoreGraphicsState];
+ }
+ QPixmapCache::insert(key, focusRingPixmap);
+ }
+
+ // Add 2 for the actual ring tickness going inwards
+ const qreal hCornerSize = 2 + hMargin + radius;
+ const qreal vCornerSize = 2 + vMargin + radius;
+ const qreal shCornerSize = hCornerSize * pixelRatio;
+ const qreal svCornerSize = vCornerSize * pixelRatio;
+ // top-left corner
+ p->drawPixmap(QPointF(targetRect.left(), targetRect.top()), focusRingPixmap,
+ QRectF(0, 0, shCornerSize, svCornerSize));
+ // top-right corner
+ p->drawPixmap(QPointF(targetRect.right() - hCornerSize + 1, targetRect.top()), focusRingPixmap,
+ QRectF(focusRingPixmap.width() - shCornerSize, 0, shCornerSize, svCornerSize));
+ // bottom-left corner
+ p->drawPixmap(QPointF(targetRect.left(), targetRect.bottom() - vCornerSize + 1), focusRingPixmap,
+ QRectF(0, focusRingPixmap.height() - svCornerSize, shCornerSize, svCornerSize));
+ // bottom-right corner
+ p->drawPixmap(QPointF(targetRect.right() - hCornerSize + 1, targetRect.bottom() - vCornerSize + 1), focusRingPixmap,
+ QRect(focusRingPixmap.width() - shCornerSize, focusRingPixmap.height() - svCornerSize, shCornerSize, svCornerSize));
+ // top edge
+ p->drawPixmap(QRectF(targetRect.left() + hCornerSize, targetRect.top(), targetRect.width() - 2 * hCornerSize, vCornerSize), focusRingPixmap,
+ QRect(shCornerSize, 0, focusRingPixmap.width() - 2 * shCornerSize, svCornerSize));
+ // bottom edge
+ p->drawPixmap(QRectF(targetRect.left() + hCornerSize, targetRect.bottom() - vCornerSize + 1, targetRect.width() - 2 * hCornerSize, vCornerSize), focusRingPixmap,
+ QRect(shCornerSize, focusRingPixmap.height() - svCornerSize, focusRingPixmap.width() - 2 * shCornerSize, svCornerSize));
+ // left edge
+ p->drawPixmap(QRectF(targetRect.left(), targetRect.top() + vCornerSize, hCornerSize, targetRect.height() - 2 * vCornerSize), focusRingPixmap,
+ QRect(0, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize));
+ // right edge
+ p->drawPixmap(QRectF(targetRect.right() - hCornerSize + 1, targetRect.top() + vCornerSize, hCornerSize, targetRect.height() - 2 * vCornerSize), focusRingPixmap,
+ QRect(focusRingPixmap.width() - shCornerSize, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize));
}
QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
@@ -3947,12 +3997,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
}
}
- NSBezierPath *pushButtonFocusRingPath;
- if (bdi.kind == kThemeBevelButton)
- pushButtonFocusRingPath = [NSBezierPath bezierPathWithRect:NSRectFromCGRect(focusRect)];
- else
- pushButtonFocusRingPath = [NSBezierPath bezierPathWithRoundedRect:NSRectFromCGRect(focusRect) xRadius:4 yRadius:4];
- qt_drawFocusRingOnPath(cg, pushButtonFocusRingPath);
+ const qreal radius = bdi.kind == kThemeBevelButton ? 0 : 4;
+ const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, btn, w);
+ const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, btn, w);
+ const QRect focusTargetRect(focusRect.origin.x, focusRect.origin.y, focusRect.size.width, focusRect.size.height);
+ d->drawFocusRing(p, focusTargetRect.adjusted(-hMargin, -vMargin, hMargin, vMargin), hMargin, vMargin, radius);
}
if (hasMenu && (!usingYosemiteOrLater || bdi.kind == kThemeBevelButton)) {
@@ -4391,12 +4440,9 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
}
break;
case CE_FocusFrame: {
- int xOff = proxy()->pixelMetric(PM_FocusFrameHMargin, opt, w);
- int yOff = proxy()->pixelMetric(PM_FocusFrameVMargin, opt, w);
- NSRect rect = NSMakeRect(xOff+opt->rect.x(), yOff+opt->rect.y(), opt->rect.width() - 2 * xOff,
- opt->rect.height() - 2 * yOff);
- NSBezierPath *focusFramePath = [NSBezierPath bezierPathWithRect:rect];
- qt_drawFocusRingOnPath(cg, focusFramePath);
+ const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, w);
+ const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt, w);
+ d->drawFocusRing(p, opt->rect, hMargin, vMargin);
break; }
case CE_MenuItem:
case CE_MenuEmptyArea:
diff --git a/src/widgets/styles/qmacstyle_mac_p_p.h b/src/widgets/styles/qmacstyle_mac_p_p.h
index 33818568ec..b2c76c2d76 100644
--- a/src/widgets/styles/qmacstyle_mac_p_p.h
+++ b/src/widgets/styles/qmacstyle_mac_p_p.h
@@ -209,6 +209,8 @@ public:
void drawNSViewInRect(QCocoaWidget widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, QCocoaDrawRectBlock drawRectBlock = nil) const;
void resolveCurrentNSView(QWindow *window);
+ void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const;
+
public:
mutable QPointer<QObject> pressedButton;
mutable QPointer<QObject> defaultButton;