9

I'm having a problem trying to insert a QML view into a native OSX window. I know it's possible, but I don't know what I'm doing wrong.

Basically, my goal is, given a native NSView* to then embed a QML based widget. The problem is that I get it to point where it does indeed renderer the qml inside the view but it creates an extra transparent window to the side and it doesn't seem to redraw the QML view properly.

Here's the code that I'm using (please disregard all the memory leaks):

@interface AppDelegate ()
-(void)processEvents;

@property(nonatomic) NSTimer* timer;
@property(nonatomic) QApplication* qt;
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    NSWindow* window = [[[NSApplication sharedApplication] windows] objectAtIndex:0];
    NSView *view = [window contentView];
    assert(view);

    char* test[0];
    int count = 0;

    QApplication::instance()->setAttribute(Qt::AA_MacPluginApplication);
    _qt = new QApplication(count, test);

    QMacNativeWidget* native = new QMacNativeWidget(view);
    assert(native);

    QQuickWidget* qml = new QQuickWidget(native);
    qml->setSource(QUrl(QStringLiteral("main.qml")));

    QVBoxLayout* layout = new QVBoxLayout();
    layout->addWidget(qml);

    native->setLayout(layout);

    qml->show();
    native->show();


    NSView* qmlView = (NSView*)native->winId();
    [view addSubview:qmlView];

    _timer = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:@selector(processEvents) userInfo:nil repeats:YES];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
    // Insert code here to tear down your application
    [_timer invalidate];
    _qt->quit();

}

-(void)processEvents
{
    _qt->processEvents();
    _qt->sendPostedEvents(0,-1);
}

@end

And here's the simple qml:

import QtQuick 2.7

Item
{
    visible: true
    x: 0;
    y: 0;
    width: 100
    height: 100
    Rectangle
    {
        anchors.fill: parent
        color: 'blue'
        MouseArea
        {
            anchors.fill: parent
            onClicked:
            {
                console.log(parent.color);
                if(parent.color == '#0000ff')
                    parent.color = 'green';
                else
                    parent.color = 'blue';
            }
        }
    }
}
bitwise
  • 541
  • 6
  • 16

1 Answers1

1

QQuickWidget composities its content with other widget content in a slightly complex way, involving a framebuffer and offscreen window, which I suspect might explain the odd results you see - I would not expect it to work in a plugin situation.

The easiest option would be use a QQuickWindow (or QQuickView) with createWindowContainer to turn the QWindow into a QWidget you can parent to your QMacNativeWidget.

However, I think the most robust approach would be to ignore widgets and windows entirely, and integrate at the framebuffer level, using QQuickRenderControl and an NSOpenGLView. This is more work to code up but keeps the NSView hierarchy straightforward, and should give the best possible performance. You can either render directly into the native OpenGL view (which requires creating a QOpenGLContext from the native context, possible since Qt 5.4), or into a framebuffer using a texture shared between the QtQuick and NSOpenGLContext.

James Turner
  • 2,425
  • 2
  • 19
  • 24
  • Thank you so much for taking the time to answer. I will research these options and see if they work. In the meantime, would you be able to point me to an example implemented somewhere? – bitwise Aug 19 '16 at 14:29
  • For `createWindowContainer` simply look at the docs - it will take a `QQuickWindow` and wrap it in a widget. This will work better in a plugin scenario than QQuickWidget, due to different implementation choices. – James Turner Aug 26 '16 at 21:49
  • For QQuickRenderControl, see the `QQuickRenderControl` example that ships with Qt - you need to replace the `WindowSingleThreaded` in that example with a `NSOpenGLView`, and wrap the associated `openGLContext` in a `QOpenGLContext` to use it with the render control. – James Turner Aug 26 '16 at 21:52
  • Thanks James, I'll give those a try and post an example here when I get a chance – bitwise Aug 26 '16 at 21:54