Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-70340

Qt widgets are stretched on Retina display for QMacNativeWidget on native NSWindow with wantsLayer=YES



    • Type: Bug
    • Status: Closed
    • Priority: P2: Important
    • Resolution: Done
    • Affects Version/s: 5.11.1
    • Fix Version/s: 5.12.0 Alpha
    • Component/s: Extras: Mac
    • Labels:
    • Platform/s:


      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
      RedWidget() {
      void resizeEvent(QResizeEvent *)
      qDebug() << "RedWidget::resize" << size();
      void paintEvent(QPaintEvent *event)
      QPainter p(this);
      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");
      RedWidget * pRedWidget = new RedWidget{};
      QLineEdit * pEdit = new QLineEdit{};
      QVBoxLayout* pLayout = new QVBoxLayout();
      pLayout->addSpacerItem(new QSpacerItem(10, 200));
      pContainerForNative->move(30, 0);
      pContainer->setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint);
      NSView *rootView = pContainer->nativeView();
      rootView.wantsLayer = YES;
      [window setContentView: pContainer->nativeView()];
      pContainer->move(0, 0);
      [window makeKeyAndOrderFront:self];
      - (void)dealloc {


       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()));

      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.


        No reviews matched the request. Check your Options in the drop-down menu of this sections header.



            • Assignee:
              vestbo Tor Arne Vestbø
              alexander_vershynin Alexaner Vershynin
            • Votes:
              0 Vote for this issue
              2 Start watching this issue


              • Created:

                Gerrit Reviews

                There are no open Gerrit changes