macOS: Redraw window bottom corners after synchronous backing store flush

Flushing outside of the display cycle does not care about any ordering
between views, including the NSThemeFrame responsible for drawing the
rounded corners of the window.

Since Qt Widgets is doing a lot of synchronous flushing (for now, until
we plumb update() to requestUpdate(), or enable layer-backing), we add
a workaround that explicitly draws the corners after flushing, just like
the logic in [NSView displayIfNeeded]. This is the same workaround used
by WebKit: https://trac.webkit.org/changeset/85376/webkit

Change-Id: I884152cdb2685569704e577b64b5ae278ed82c21
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Tor Arne Vestbø 2017-08-14 14:25:01 +02:00
parent dfdb94410a
commit 52ad5afe1c
2 changed files with 53 additions and 1 deletions

View File

@ -55,6 +55,7 @@ public:
private:
bool windowHasUnifiedToolbar() const;
QImage::Format format() const Q_DECL_OVERRIDE;
void redrawRoundedBottomCorners(CGRect) const;
};
QT_END_NAMESPACE

View File

@ -199,11 +199,62 @@ void QCocoaBackingStore::flush(QWindow *window, const QRegion &region, const QPo
if (shouldHandleViewLockManually)
[view unlockFocus];
if (drawingOutsideOfDisplayCycle)
if (drawingOutsideOfDisplayCycle) {
redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]);
[view.window flushWindow];
}
// FIXME: Tie to changing window flags and/or mask instead
[view invalidateWindowShadowIfNeeded];
}
/*
When drawing outside of the display cycle, which Qt Widget does a lot,
we end up drawing over the NSThemeFrame, losing the rounded corners of
windows in the process.
To work around this, until we've enabled updates via setNeedsDisplay and/or
enabled layer-backed views, we ask the NSWindow to redraw the bottom corners
if they intersect with the flushed region.
This is the same logic used internally by e.g [NSView displayIfNeeded],
[NSRulerView _scrollToMatchContentView], and [NSClipView _immediateScrollToPoint:],
as well as the workaround used by WebKit to fix a similar bug:
https://trac.webkit.org/changeset/85376/webkit
*/
void QCocoaBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const
{
#if !defined(QT_APPLE_NO_PRIVATE_APIS)
Q_ASSERT(this->window()->handle());
NSWindow *window = static_cast<QCocoaWindow *>(this->window()->handle())->nativeWindow();
static SEL intersectBottomCornersWithRect = NSSelectorFromString(
[NSString stringWithFormat:@"_%s%s:", "intersectBottomCorners", "WithRect"]);
if (NSMethodSignature *signature = [window methodSignatureForSelector:intersectBottomCornersWithRect]) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = window;
invocation.selector = intersectBottomCornersWithRect;
[invocation setArgument:&windowRect atIndex:2];
[invocation invoke];
NSRect cornerOverlap = NSZeroRect;
[invocation getReturnValue:&cornerOverlap];
if (!NSIsEmptyRect(cornerOverlap)) {
static SEL maskRoundedBottomCorners = NSSelectorFromString(
[NSString stringWithFormat:@"_%s%s:", "maskRounded", "BottomCorners"]);
if ((signature = [window methodSignatureForSelector:maskRoundedBottomCorners])) {
invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = window;
invocation.selector = maskRoundedBottomCorners;
[invocation setArgument:&cornerOverlap atIndex:2];
[invocation invoke];
}
}
}
#else
Q_UNUSED(windowRect);
#endif
}
QT_END_NAMESPACE