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

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P2: Important
    • 5.12.0 Alpha
    • 5.11.1
    • Extras: Mac
    • None
    • macOS

    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.

      Attachments

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

        Activity

          People

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

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes