macOS: Remove support for surface-backed views
Our deployment target is 10.14, which enables layer-backing by default, and our layer-backing support nowadays is stable enough that we don't need to maintain any of the old code paths for compatibility. The wantsBestResolutionOpenGLSurface property on NSView is only relevant for surface-backed views, so we no longer need to deal with it. Change-Id: I8aef4ac99371113d463ac35eee648a8a2fd1ea72 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
d1111632e2
commit
1fc7ca091b
@ -58,21 +58,6 @@ protected:
|
||||
QCFType<CGColorSpaceRef> colorSpace() const;
|
||||
};
|
||||
|
||||
class QNSWindowBackingStore : public QCocoaBackingStore
|
||||
{
|
||||
public:
|
||||
QNSWindowBackingStore(QWindow *window);
|
||||
~QNSWindowBackingStore();
|
||||
|
||||
void resize(const QSize &size, const QRegion &staticContents) override;
|
||||
void flush(QWindow *, const QRegion &, const QPoint &) override;
|
||||
|
||||
private:
|
||||
bool windowHasUnifiedToolbar() const;
|
||||
QImage::Format format() const override;
|
||||
void redrawRoundedBottomCorners(CGRect) const;
|
||||
};
|
||||
|
||||
class QCALayerBackingStore : public QObject, public QCocoaBackingStore
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -64,276 +64,6 @@ QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
QNSWindowBackingStore::QNSWindowBackingStore(QWindow *window)
|
||||
: QCocoaBackingStore(window)
|
||||
{
|
||||
// Choose an appropriate window depth based on the requested surface format.
|
||||
// On deep color displays the default bit depth is 16-bit, so unless we need
|
||||
// that level of precision we opt out of it (and the expensive RGB32 -> RGB64
|
||||
// conversions that come with it if our backingstore depth does not match).
|
||||
|
||||
NSWindow *nsWindow = static_cast<QCocoaWindow *>(window->handle())->view().window;
|
||||
auto colorSpaceName = NSColorSpaceFromDepth(nsWindow.depthLimit);
|
||||
|
||||
static const int kDefaultBitDepth = 8;
|
||||
auto surfaceFormat = window->requestedFormat();
|
||||
auto bitsPerSample = qMax(kDefaultBitDepth, qMax(surfaceFormat.redBufferSize(),
|
||||
qMax(surfaceFormat.greenBufferSize(), surfaceFormat.blueBufferSize())));
|
||||
|
||||
// NSBestDepth does not seem to guarantee a window depth deep enough for the
|
||||
// given bits per sample, even if documented as such. For example, requesting
|
||||
// 10 bits per sample will not give us a 16-bit format, even if that's what's
|
||||
// available. Work around this by manually bumping the bit depth.
|
||||
bitsPerSample = !(bitsPerSample & (bitsPerSample - 1))
|
||||
? bitsPerSample : qNextPowerOfTwo(bitsPerSample);
|
||||
|
||||
auto bestDepth = NSBestDepth(colorSpaceName, bitsPerSample, 0, NO, nullptr);
|
||||
|
||||
// Disable dynamic depth limit, otherwise our depth limit will be overwritten
|
||||
// by AppKit if the window moves to a screen with a different depth. We call
|
||||
// this before setting the depth limit, as the call will reset the depth to 0.
|
||||
[nsWindow setDynamicDepthLimit:NO];
|
||||
|
||||
qCDebug(lcQpaBackingStore) << "Using" << NSBitsPerSampleFromDepth(bestDepth)
|
||||
<< "bit window depth for" << nsWindow;
|
||||
|
||||
nsWindow.depthLimit = bestDepth;
|
||||
}
|
||||
|
||||
QNSWindowBackingStore::~QNSWindowBackingStore()
|
||||
{
|
||||
}
|
||||
|
||||
bool QNSWindowBackingStore::windowHasUnifiedToolbar() const
|
||||
{
|
||||
Q_ASSERT(window()->handle());
|
||||
return static_cast<QCocoaWindow *>(window()->handle())->m_drawContentBorderGradient;
|
||||
}
|
||||
|
||||
QImage::Format QNSWindowBackingStore::format() const
|
||||
{
|
||||
if (windowHasUnifiedToolbar())
|
||||
return QImage::Format_ARGB32_Premultiplied;
|
||||
|
||||
return QRasterBackingStore::format();
|
||||
}
|
||||
|
||||
void QNSWindowBackingStore::resize(const QSize &size, const QRegion &staticContents)
|
||||
{
|
||||
qCDebug(lcQpaBackingStore) << "Resize requested to" << size;
|
||||
QRasterBackingStore::resize(size, staticContents);
|
||||
|
||||
// The window shadow rendered by AppKit is based on the shape/content of the
|
||||
// NSWindow surface. Technically any flush of the backingstore can result in
|
||||
// a potentially new shape of the window, and would need a shadow invalidation,
|
||||
// but this is likely too expensive to do at every flush for the few cases where
|
||||
// clients change the shape dynamically. One case where we do know that the shadow
|
||||
// likely needs invalidation, if the window has partially transparent content,
|
||||
// is after a resize, where AppKit's default shadow may be based on the previous
|
||||
// window content.
|
||||
QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle());
|
||||
if (cocoaWindow->isContentView() && !cocoaWindow->isOpaque())
|
||||
cocoaWindow->m_needsInvalidateShadow = true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Flushes the given \a region from the specified \a window onto the
|
||||
screen.
|
||||
|
||||
The \a window is the top level window represented by this backingstore,
|
||||
or a non-transient child of that window.
|
||||
|
||||
If the \a window is a child window, the \a region will be in child window
|
||||
coordinates, and the \a offset will be the child window's offset in relation
|
||||
to the backingstore's top level window.
|
||||
*/
|
||||
void QNSWindowBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset)
|
||||
{
|
||||
if (m_image.isNull())
|
||||
return;
|
||||
|
||||
// Use local pool so that any stale image references are cleaned up after flushing
|
||||
QMacAutoReleasePool pool;
|
||||
|
||||
const QWindow *topLevelWindow = this->window();
|
||||
|
||||
Q_ASSERT(topLevelWindow->handle() && window->handle());
|
||||
Q_ASSERT(!topLevelWindow->handle()->isForeignWindow() && !window->handle()->isForeignWindow());
|
||||
|
||||
QNSView *topLevelView = qnsview_cast(static_cast<QCocoaWindow *>(topLevelWindow->handle())->view());
|
||||
QNSView *view = qnsview_cast(static_cast<QCocoaWindow *>(window->handle())->view());
|
||||
|
||||
if (lcQpaBackingStore().isDebugEnabled()) {
|
||||
QString targetViewDescription;
|
||||
if (view != topLevelView) {
|
||||
QDebug targetDebug(&targetViewDescription);
|
||||
targetDebug << "onto" << topLevelView << "at" << offset;
|
||||
}
|
||||
qCDebug(lcQpaBackingStore) << "Flushing" << region << "of" << view << qPrintable(targetViewDescription);
|
||||
}
|
||||
|
||||
// Normally a NSView is drawn via drawRect, as part of the display cycle in the
|
||||
// main runloop, via setNeedsDisplay and friends. AppKit will lock focus on each
|
||||
// individual view, starting with the top level and then traversing any subviews,
|
||||
// calling drawRect for each of them. This pull model results in expose events
|
||||
// sent to Qt, which result in drawing to the backingstore and flushing it.
|
||||
// Qt may also decide to paint and flush the backingstore via e.g. timers,
|
||||
// or other events such as mouse events, in which case we're in a push model.
|
||||
// If there is no focused view, it means we're in the latter case, and need
|
||||
// to manually flush the NSWindow after drawing to its graphic context.
|
||||
const bool drawingOutsideOfDisplayCycle = ![NSView focusView];
|
||||
|
||||
// We also need to ensure the flushed view has focus, so that the graphics
|
||||
// context is set up correctly (coordinate system, clipping, etc). Outside
|
||||
// of the normal display cycle there is no focused view, as explained above,
|
||||
// so we have to handle it manually. There's also a corner case inside the
|
||||
// normal display cycle due to way QWidgetRepaintManager composits native child
|
||||
// widgets, where we'll get a flush of a native child during the drawRect of
|
||||
// its parent/ancestor, and the parent/ancestor being the one locked by AppKit.
|
||||
// In this case we also need to lock and unlock focus manually.
|
||||
const bool shouldHandleViewLockManually = [NSView focusView] != view;
|
||||
if (shouldHandleViewLockManually && !QT_IGNORE_DEPRECATIONS([view lockFocusIfCanDraw])) {
|
||||
qWarning() << "failed to lock focus of" << view;
|
||||
return;
|
||||
}
|
||||
|
||||
const qreal devicePixelRatio = m_image.devicePixelRatio();
|
||||
|
||||
// If the flushed window is a content view, and we're filling the drawn area
|
||||
// completely, or it doesn't have a window background we need to preserve,
|
||||
// we can get away with copying instead of blending the backing store.
|
||||
QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle());
|
||||
const NSCompositingOperation compositingOperation = cocoaWindow->isContentView()
|
||||
&& (cocoaWindow->isOpaque() || view.window.backgroundColor == NSColor.clearColor)
|
||||
? NSCompositingOperationCopy : NSCompositingOperationSourceOver;
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
static bool debugBackingStoreFlush = [[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:@"QtCocoaDebugBackingStoreFlush"];
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// The current contexts is typically a NSWindowGraphicsContext, but can be
|
||||
// NSBitmapGraphicsContext e.g. when debugging the view hierarchy in Xcode.
|
||||
// If we need to distinguish things here in the future, we can use e.g.
|
||||
// [NSGraphicsContext drawingToScreen], or the attributes of the context.
|
||||
NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
|
||||
Q_ASSERT_X(graphicsContext, "QCocoaBackingStore",
|
||||
"Focusing the view should give us a current graphics context");
|
||||
|
||||
// Tag backingstore image with color space based on the window.
|
||||
// Note: This does not copy the underlying image data.
|
||||
QCFType<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace(
|
||||
QCFType<CGImageRef>(m_image.toCGImage()), colorSpace());
|
||||
|
||||
// Create temporary image to use for blitting, without copying image data
|
||||
NSImage *backingStoreImage = [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease];
|
||||
|
||||
QRegion clippedRegion = region;
|
||||
for (QWindow *w = window; w; w = w->parent()) {
|
||||
if (!w->mask().isEmpty()) {
|
||||
clippedRegion &= w == window ? w->mask()
|
||||
: w->mask().translated(window->mapFromGlobal(w->mapToGlobal(QPoint(0, 0))));
|
||||
}
|
||||
}
|
||||
|
||||
for (const QRect &viewLocalRect : clippedRegion) {
|
||||
QPoint backingStoreOffset = viewLocalRect.topLeft() + offset;
|
||||
QRect backingStoreRect(backingStoreOffset * devicePixelRatio, viewLocalRect.size() * devicePixelRatio);
|
||||
if (graphicsContext.flipped) // Flip backingStoreRect to match graphics context
|
||||
backingStoreRect.moveTop(m_image.height() - (backingStoreRect.y() + backingStoreRect.height()));
|
||||
|
||||
CGRect viewRect = viewLocalRect.toCGRect();
|
||||
|
||||
[backingStoreImage drawInRect:viewRect fromRect:backingStoreRect.toCGRect()
|
||||
operation:compositingOperation fraction:1.0 respectFlipped:YES hints:nil];
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
if (Q_UNLIKELY(debugBackingStoreFlush)) {
|
||||
[[NSColor colorWithCalibratedRed:drand48() green:drand48() blue:drand48() alpha:0.3] set];
|
||||
[NSBezierPath fillRect:viewRect];
|
||||
|
||||
if (drawingOutsideOfDisplayCycle) {
|
||||
[[[NSColor magentaColor] colorWithAlphaComponent:0.5] set];
|
||||
[NSBezierPath strokeLineFromPoint:viewLocalRect.topLeft().toCGPoint()
|
||||
toPoint:viewLocalRect.bottomRight().toCGPoint()];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
if (shouldHandleViewLockManually)
|
||||
QT_IGNORE_DEPRECATIONS([view unlockFocus]);
|
||||
|
||||
if (drawingOutsideOfDisplayCycle) {
|
||||
redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]);
|
||||
QT_IGNORE_DEPRECATIONS([view.window flushWindow]);
|
||||
}
|
||||
|
||||
// Done flushing to NSWindow backingstore
|
||||
|
||||
QCocoaWindow *topLevelCocoaWindow = static_cast<QCocoaWindow *>(topLevelWindow->handle());
|
||||
if (Q_UNLIKELY(topLevelCocoaWindow->m_needsInvalidateShadow)) {
|
||||
qCDebug(lcQpaBackingStore) << "Invalidating window shadow for" << topLevelCocoaWindow;
|
||||
[topLevelView.window invalidateShadow];
|
||||
topLevelCocoaWindow->m_needsInvalidateShadow = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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 QNSWindowBackingStore::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
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
QCALayerBackingStore::QCALayerBackingStore(QWindow *window)
|
||||
: QCocoaBackingStore(window)
|
||||
{
|
||||
|
@ -81,7 +81,6 @@ private:
|
||||
static NSOpenGLPixelFormat *pixelFormatForSurfaceFormat(const QSurfaceFormat &format);
|
||||
|
||||
bool setDrawable(QPlatformSurface *surface);
|
||||
void prepareDrawable(QCocoaWindow *platformWindow);
|
||||
void updateSurfaceFormat();
|
||||
|
||||
NSOpenGLContext *m_context = nil;
|
||||
|
@ -393,8 +393,6 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
|
||||
if (view == QT_IGNORE_DEPRECATIONS(m_context.view))
|
||||
return true;
|
||||
|
||||
prepareDrawable(cocoaWindow);
|
||||
|
||||
// Setting the drawable may happen on a separate thread as a result of
|
||||
// a call to makeCurrent, so we need to set up the observers before we
|
||||
// associate the view with the context. That way we will guarantee that
|
||||
@ -410,12 +408,8 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
|
||||
|
||||
m_updateObservers.clear();
|
||||
|
||||
if (view.layer) {
|
||||
m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
|
||||
m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
|
||||
} else {
|
||||
m_updateObservers.append(QMacNotificationObserver(view, QT_IGNORE_DEPRECATIONS(NSViewGlobalFrameDidChangeNotification), updateCallback));
|
||||
}
|
||||
m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
|
||||
m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
|
||||
|
||||
m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
|
||||
NSApplicationDidChangeScreenParametersNotification, updateCallback));
|
||||
@ -437,30 +431,6 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
|
||||
return true;
|
||||
}
|
||||
|
||||
void QCocoaGLContext::prepareDrawable(QCocoaWindow *platformWindow)
|
||||
{
|
||||
// We generally want high-DPI GL surfaces, unless the user has explicitly disabled them
|
||||
bool prefersBestResolutionOpenGLSurface = qt_mac_resolveOption(YES,
|
||||
platformWindow->window(), "_q_mac_wantsBestResolutionOpenGLSurface",
|
||||
"QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
|
||||
|
||||
auto *view = platformWindow->view();
|
||||
|
||||
// The only case we have to opt out ourselves is when using the Apple software renderer
|
||||
// in combination with surface-backed views, as these together do not support high-DPI.
|
||||
if (prefersBestResolutionOpenGLSurface) {
|
||||
int rendererID = 0;
|
||||
[m_context getValues:&rendererID forParameter:NSOpenGLContextParameterCurrentRendererID];
|
||||
bool isSoftwareRenderer = (rendererID & kCGLRendererIDMatchingMask) == kCGLRendererGenericFloatID;
|
||||
if (isSoftwareRenderer && !view.layer) {
|
||||
qCInfo(lcQpaOpenGLContext) << "Disabling high resolution GL surface due to software renderer";
|
||||
prefersBestResolutionOpenGLSurface = false;
|
||||
}
|
||||
}
|
||||
|
||||
QT_IGNORE_DEPRECATIONS(view.wantsBestResolutionOpenGLSurface) = prefersBestResolutionOpenGLSurface;
|
||||
}
|
||||
|
||||
// NSOpenGLContext is not re-entrant. Even when using separate contexts per thread,
|
||||
// view, and window, calls into the API will still deadlock. For more information
|
||||
// see https://openradar.appspot.com/37064579
|
||||
@ -494,19 +464,17 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
|
||||
return;
|
||||
}
|
||||
|
||||
if (QT_IGNORE_DEPRECATIONS(m_context.view).layer) {
|
||||
// Flushing an NSOpenGLContext will hit the screen immediately, ignoring
|
||||
// any Core Animation transactions in place. This may result in major
|
||||
// visual artifacts if the flush happens out of sync with the size
|
||||
// of the layer, view, and window reflected by other parts of the UI,
|
||||
// e.g. if the application flushes in the resize event or a timer during
|
||||
// window resizing, instead of in the expose event.
|
||||
auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
|
||||
if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
|
||||
qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)."
|
||||
<< "Skipping flush to avoid visual artifacts.";
|
||||
return;
|
||||
}
|
||||
// Flushing an NSOpenGLContext will hit the screen immediately, ignoring
|
||||
// any Core Animation transactions in place. This may result in major
|
||||
// visual artifacts if the flush happens out of sync with the size
|
||||
// of the layer, view, and window reflected by other parts of the UI,
|
||||
// e.g. if the application flushes in the resize event or a timer during
|
||||
// window resizing, instead of in the expose event.
|
||||
auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
|
||||
if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
|
||||
qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)."
|
||||
<< "Skipping flush to avoid visual artifacts.";
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&s_reentrancyMutex);
|
||||
|
@ -259,8 +259,8 @@ bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) cons
|
||||
// AppKit expects rendering to happen on the main thread, and we can
|
||||
// easily end up in situations where rendering on secondary threads
|
||||
// will result in visual artifacts, bugs, or even deadlocks, when
|
||||
// building with SDK 10.14 or higher which enbles view layer-backing.
|
||||
return QMacVersion::buildSDK() < QOperatingSystemVersion(QOperatingSystemVersion::MacOSMojave);
|
||||
// layer-backed.
|
||||
return false;
|
||||
case OpenGL:
|
||||
case BufferQueueingOpenGL:
|
||||
#endif
|
||||
@ -333,13 +333,7 @@ QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *wi
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QPlatformBackingStore *backingStore = nullptr;
|
||||
if (platformWindow->view().layer)
|
||||
backingStore = new QCALayerBackingStore(window);
|
||||
else
|
||||
backingStore = new QNSWindowBackingStore(window);
|
||||
|
||||
return backingStore;
|
||||
return new QCALayerBackingStore(window);
|
||||
}
|
||||
|
||||
QAbstractEventDispatcher *QCocoaIntegration::createEventDispatcher() const
|
||||
|
@ -261,8 +261,6 @@ public: // for QNSView
|
||||
bool m_inSetStyleMask;
|
||||
QCocoaMenuBar *m_menubar;
|
||||
|
||||
bool m_needsInvalidateShadow;
|
||||
|
||||
bool m_frameStrutEventsEnabled;
|
||||
QRect m_exposedRect;
|
||||
int m_registerTouchCount;
|
||||
|
@ -145,7 +145,6 @@ QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle)
|
||||
, m_inSetGeometry(false)
|
||||
, m_inSetStyleMask(false)
|
||||
, m_menubar(nullptr)
|
||||
, m_needsInvalidateShadow(false)
|
||||
, m_frameStrutEventsEnabled(false)
|
||||
, m_registerTouchCount(0)
|
||||
, m_resizableTransientParent(false)
|
||||
@ -1056,27 +1055,15 @@ void QCocoaWindow::setMask(const QRegion ®ion)
|
||||
{
|
||||
qCDebug(lcQpaWindow) << "QCocoaWindow::setMask" << window() << region;
|
||||
|
||||
if (m_view.layer) {
|
||||
if (!region.isEmpty()) {
|
||||
QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
|
||||
for (const QRect &r : region)
|
||||
CGPathAddRect(maskPath, nullptr, r.toCGRect());
|
||||
CAShapeLayer *maskLayer = [CAShapeLayer layer];
|
||||
maskLayer.path = maskPath;
|
||||
m_view.layer.mask = maskLayer;
|
||||
} else {
|
||||
m_view.layer.mask = nil;
|
||||
}
|
||||
if (!region.isEmpty()) {
|
||||
QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
|
||||
for (const QRect &r : region)
|
||||
CGPathAddRect(maskPath, nullptr, r.toCGRect());
|
||||
CAShapeLayer *maskLayer = [CAShapeLayer layer];
|
||||
maskLayer.path = maskPath;
|
||||
m_view.layer.mask = maskLayer;
|
||||
} else {
|
||||
if (isContentView()) {
|
||||
// Setting the mask requires invalidating the NSWindow shadow, but that needs
|
||||
// to happen after the backingstore has been redrawn, so that AppKit can pick
|
||||
// up the new window shape based on the backingstore content. Doing a display
|
||||
// directly here is not an option, as the window might not be exposed at this
|
||||
// time, and so would not result in an updated backingstore.
|
||||
m_needsInvalidateShadow = true;
|
||||
[m_view setNeedsDisplay:YES];
|
||||
}
|
||||
m_view.layer.mask = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,13 @@
|
||||
|
||||
- (void)initDrawing
|
||||
{
|
||||
[self updateLayerBacking];
|
||||
if (qt_mac_resolveOption(-1, m_platformWindow->window(),
|
||||
"_q_mac_wantsLayer", "QT_MAC_WANTS_LAYER") != -1) {
|
||||
qCWarning(lcQpaDrawing) << "Layer-backing is always enabled."
|
||||
<< " QT_MAC_WANTS_LAYER/_q_mac_wantsLayer has no effect.";
|
||||
}
|
||||
|
||||
self.wantsLayer = YES;
|
||||
}
|
||||
|
||||
- (BOOL)isOpaque
|
||||
@ -60,40 +66,6 @@
|
||||
|
||||
// ----------------------- Layer setup -----------------------
|
||||
|
||||
- (void)updateLayerBacking
|
||||
{
|
||||
self.wantsLayer = [self layerEnabledByMacOS]
|
||||
|| [self layerExplicitlyRequested]
|
||||
|| [self shouldUseMetalLayer];
|
||||
}
|
||||
|
||||
- (BOOL)layerEnabledByMacOS
|
||||
{
|
||||
// AppKit has its own logic for this, but if we rely on that, our layers are created
|
||||
// by AppKit at a point where we've already set up other parts of the platform plugin
|
||||
// based on the presence of layers or not. Once we've rewritten these parts to support
|
||||
// dynamically picking up layer enablement we can let AppKit do its thing.
|
||||
return QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave
|
||||
&& QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave;
|
||||
}
|
||||
|
||||
- (BOOL)layerExplicitlyRequested
|
||||
{
|
||||
static bool wantsLayer = [&]() {
|
||||
int wantsLayer = qt_mac_resolveOption(-1, m_platformWindow->window(),
|
||||
"_q_mac_wantsLayer", "QT_MAC_WANTS_LAYER");
|
||||
|
||||
if (wantsLayer != -1 && [self layerEnabledByMacOS]) {
|
||||
qCWarning(lcQpaDrawing) << "Layer-backing cannot be explicitly controlled on 10.14 when built against the 10.14 SDK";
|
||||
return true;
|
||||
}
|
||||
|
||||
return wantsLayer == 1;
|
||||
}();
|
||||
|
||||
return wantsLayer;
|
||||
}
|
||||
|
||||
- (BOOL)shouldUseMetalLayer
|
||||
{
|
||||
// MetalSurface needs a layer, and so does VulkanSurface (via MoltenVK)
|
||||
@ -146,8 +118,7 @@
|
||||
{
|
||||
qCDebug(lcQpaDrawing) << "Making" << self
|
||||
<< (self.wantsLayer ? "layer-backed" : "layer-hosted")
|
||||
<< "with" << layer << "due to being" << ([self layerExplicitlyRequested] ? "explicitly requested"
|
||||
: [self shouldUseMetalLayer] ? "needed by surface type" : "enabled by macOS");
|
||||
<< "with" << layer;
|
||||
|
||||
if (layer.delegate && layer.delegate != self) {
|
||||
qCWarning(lcQpaDrawing) << "Layer already has delegate" << layer.delegate
|
||||
@ -244,22 +215,6 @@
|
||||
{
|
||||
Q_ASSERT_X(!self.layer, "QNSView",
|
||||
"The drawRect code path should not be hit when we are layer backed");
|
||||
|
||||
if (!m_platformWindow)
|
||||
return;
|
||||
|
||||
QRegion exposedRegion;
|
||||
const NSRect *dirtyRects;
|
||||
NSInteger numDirtyRects;
|
||||
[self getRectsBeingDrawn:&dirtyRects count:&numDirtyRects];
|
||||
for (int i = 0; i < numDirtyRects; ++i)
|
||||
exposedRegion += QRectF::fromCGRect(dirtyRects[i]).toRect();
|
||||
|
||||
if (exposedRegion.isEmpty())
|
||||
exposedRegion = QRectF::fromCGRect(dirtyBoundingRect).toRect();
|
||||
|
||||
qCDebug(lcQpaDrawing) << "[QNSView drawRect:]" << m_platformWindow->window() << exposedRegion;
|
||||
m_platformWindow->handleExposeEvent(exposedRegion);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user