Details
-
Bug
-
Resolution: Done
-
P2: Important
-
5.11.1
-
None
Description
Qt widgets which are children of QMacNativeWidget are stretched on the following conditions:
1) Retina display;
2) QMacNativeWidget::nativeView() is set as content view for NSWindow, like:
QMacNativeWidget * pContainer = new QMacNativeWidget{};
...
[window setContentView: pContainer->nativeView()];
3) For QMacNativeWidget::nativeView() wantsLayer property is set to YES, like:
NSView *rootView = pContainer->nativeView();
rootView.wantsLayer = YES;
Another conditions for which bug is reproduced is when QMacNativeWidget::nativeView() is added as subview to NSWindow contentView, like:
QMacNativeWidget * pContainer = new QMacNativeWidget{};
[[window contentView] addSubview: pContainer->nativeView()];
Here is a code sample which leads to mentioned stretched widgets:
#import "AppDelegate.h" #import <QtWidgets/QMacNativeWidget> #import <QtWidgets/QMacCocoaViewContainer> #import <QtWidgets/QLineEdit> #import <QtWidgets/QLabel> #import <QtWidgets/QHBoxLayout> #import <QtWidgets/QApplication> #import <QDebug> #import <QPainter> #import <QSpacerItem> class RedWidget : public QWidget { public: RedWidget() { } void resizeEvent(QResizeEvent *) { qDebug() << "RedWidget::resize" << size(); } void paintEvent(QPaintEvent *event) { QPainter p(this); Q_UNUSED(event); QRect rect(QPoint(0, 0), size()); qDebug() << "Painting geometry" << rect; p.fillRect(rect, QColor(133, 50, 50)); } }; @implementation MyApplicationDelegate : NSObject - (id)initWithParemsCount: (int) argc params:(char**)argv { if (self = [super init]) { m_argc = argc; m_argv = argv; NSSize InitFrameSize{ 1200, 800 }; NSWindowStyleMask mask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable; window = [[NSWindow alloc] initWithContentRect: NSMakeRect(NSZeroPoint.x, NSZeroPoint.y, InitFrameSize.width, InitFrameSize.height) styleMask: mask backing: NSBackingStoreBuffered defer: false]; } return self; } - (void)applicationWillFinishLaunching:(NSNotification *)notification { m_pQtApp = new QApplication{m_argc, m_argv}; QMacNativeWidget * pContainer = new QMacNativeWidget{}; m_txtField = [[NSTextField alloc] init]; [m_txtField setFrame: NSMakeRect(10, 10, 200, 30)]; QMacCocoaViewContainer * pContainerForNative = new QMacCocoaViewContainer{m_txtField}; pContainerForNative->setGeometry(QRect{10, 10, 200, 30}); QLabel * pLabel = new QLabel{}; pLabel->setText("la la fa"); //pLabel->winId(); RedWidget * pRedWidget = new RedWidget{}; //pRedWidget->winId(); QLineEdit * pEdit = new QLineEdit{}; QVBoxLayout* pLayout = new QVBoxLayout(); pLayout->addSpacerItem(new QSpacerItem(10, 200)); pLayout->setSpacing(10); pLayout->setMargin(10); pLayout->addWidget(pEdit); pLayout->addWidget(pLabel); pLayout->addWidget(pRedWidget); pContainerForNative->setParent(pContainer); pContainerForNative->move(30, 0); pContainer->setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); pContainer->setLayout(pLayout); NSView *rootView = pContainer->nativeView(); rootView.wantsLayer = YES; pContainer->show(); [window setContentView: pContainer->nativeView()]; pContainer->move(0, 0); [window makeKeyAndOrderFront:self]; } - (void)dealloc { } @end
You can find sample project which highlights the issue in attachment.
This issue can be eliminated by providing following fix in qcocoabackingstore.mm from the cocoa plugin.
Changes are highlighted with bold font:
if (view.layer) {
// In layer-backed mode, locking focus on a view does not give the right
// view transformation, and doesn't give us a graphics context to render
// via when drawing outside of the display cycle. Instead we tell AppKit
// that we want to update the layer's content, via [NSView wantsUpdateLayer],
// which result in AppKit not creating a backingstore for each layer, and
// we then directly set the layer's backingstore (content) to our backingstore,
// masked to the part of the subview that is relevant.
// FIXME: Figure out if there's a way to do partial updates
view.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage);
if (view != topLevelView) {
const qreal devicePixelRatio = m_image.devicePixelRatio();
view.layer.contentsRect = CGRectApplyAffineTransform(
[view convertRect:view.bounds toView:topLevelView],
// The contentsRect is in unit coordinate system
CGAffineTransformMakeScale(devicePixelRatio / m_image.width(), devicePixelRatio / m_image.height()));
}
return;
}
BTW, repaint became really slow for the wantsLayer case in comparison with Qt5.9.1. As I understand comment in above chunk of code is related).
One place where I am not able to get rid from wantsLayer=YES is NSPopover.