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>
This commit is contained in:
parent
c6bf48dcbf
commit
7c7ece9442
@ -1097,17 +1097,67 @@ static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSiz
|
|||||||
}
|
}
|
||||||
#endif
|
#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);
|
qreal pixelRatio = p->device()->devicePixelRatioF();
|
||||||
[NSGraphicsContext saveGraphicsState];
|
static const QString keyFormat = QLatin1String("$qt_focusring%1-%2-%3-%4");
|
||||||
[NSGraphicsContext setCurrentContext:[NSGraphicsContext
|
const QString &key = keyFormat.arg(hMargin).arg(vMargin).arg(radius).arg(pixelRatio);
|
||||||
graphicsContextWithGraphicsPort:(CGContextRef)cg flipped:NO]];
|
QPixmap focusRingPixmap;
|
||||||
NSSetFocusRingStyle(NSFocusRingOnly);
|
const qreal size = radius * 2 + 5;
|
||||||
[focusRingPath setClip]; // Clear clip path to avoid artifacts when rendering the cursor at zero pos
|
|
||||||
[focusRingPath fill];
|
if (!QPixmapCache::find(key, focusRingPixmap)) {
|
||||||
[NSGraphicsContext restoreGraphicsState];
|
focusRingPixmap = QPixmap((QSize(size, size) + 2 * QSize(hMargin, vMargin)) * pixelRatio);
|
||||||
CGContextRestoreGState(cg);
|
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,
|
QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
|
||||||
@ -3947,12 +3997,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSBezierPath *pushButtonFocusRingPath;
|
const qreal radius = bdi.kind == kThemeBevelButton ? 0 : 4;
|
||||||
if (bdi.kind == kThemeBevelButton)
|
const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, btn, w);
|
||||||
pushButtonFocusRingPath = [NSBezierPath bezierPathWithRect:NSRectFromCGRect(focusRect)];
|
const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, btn, w);
|
||||||
else
|
const QRect focusTargetRect(focusRect.origin.x, focusRect.origin.y, focusRect.size.width, focusRect.size.height);
|
||||||
pushButtonFocusRingPath = [NSBezierPath bezierPathWithRoundedRect:NSRectFromCGRect(focusRect) xRadius:4 yRadius:4];
|
d->drawFocusRing(p, focusTargetRect.adjusted(-hMargin, -vMargin, hMargin, vMargin), hMargin, vMargin, radius);
|
||||||
qt_drawFocusRingOnPath(cg, pushButtonFocusRingPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMenu && (!usingYosemiteOrLater || bdi.kind == kThemeBevelButton)) {
|
if (hasMenu && (!usingYosemiteOrLater || bdi.kind == kThemeBevelButton)) {
|
||||||
@ -4391,12 +4440,9 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CE_FocusFrame: {
|
case CE_FocusFrame: {
|
||||||
int xOff = proxy()->pixelMetric(PM_FocusFrameHMargin, opt, w);
|
const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, w);
|
||||||
int yOff = proxy()->pixelMetric(PM_FocusFrameVMargin, opt, w);
|
const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt, w);
|
||||||
NSRect rect = NSMakeRect(xOff+opt->rect.x(), yOff+opt->rect.y(), opt->rect.width() - 2 * xOff,
|
d->drawFocusRing(p, opt->rect, hMargin, vMargin);
|
||||||
opt->rect.height() - 2 * yOff);
|
|
||||||
NSBezierPath *focusFramePath = [NSBezierPath bezierPathWithRect:rect];
|
|
||||||
qt_drawFocusRingOnPath(cg, focusFramePath);
|
|
||||||
break; }
|
break; }
|
||||||
case CE_MenuItem:
|
case CE_MenuItem:
|
||||||
case CE_MenuEmptyArea:
|
case CE_MenuEmptyArea:
|
||||||
|
@ -209,6 +209,8 @@ public:
|
|||||||
void drawNSViewInRect(QCocoaWidget widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, QCocoaDrawRectBlock drawRectBlock = nil) const;
|
void drawNSViewInRect(QCocoaWidget widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, QCocoaDrawRectBlock drawRectBlock = nil) const;
|
||||||
void resolveCurrentNSView(QWindow *window);
|
void resolveCurrentNSView(QWindow *window);
|
||||||
|
|
||||||
|
void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mutable QPointer<QObject> pressedButton;
|
mutable QPointer<QObject> pressedButton;
|
||||||
mutable QPointer<QObject> defaultButton;
|
mutable QPointer<QObject> defaultButton;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user