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';
}
}
}
}
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.
Related
I have a ListView which is initialized with a custom C++ model named rootModel inside Qml.
The model inherits QAbstractListModel and defines a QVector<customType> as a private member to populate the model.
In my ApplicationWindow I have created a Dialog in which I change the model and call the setList() function to update it. This works fine.
I also want to connect the model's size to a ScrollView's int property. This property will define the children of a RowLayout.
The problem is that when I try to bind this property to the model's size, the application crashes.
FYI, all the modifications of the model follow the Qt's rules. rowCount() is Q_INVOKABLE. I have also tried using the onModelChanged handler but this didn't work (I have checked in the documentation that this signal is emitted when modelReset() is emitted, which is taking place inside setList() through endResetModel()
I believe this is a straightforward procedure (have already performed property bindings many times in my project) but doesn't work as expected.
I quote some sample code of my project.
//main.qml
ConfiguredChannels{
id: configuredList
anchors{
left: parent.left
top: devices.bottom
right: tabs.left
bottom: parent.bottom
}
}
TabArea {
id: tabs
y: toolBar.height
x: parent.width / 8
anchors {
top: toolBar.bottom
}
width: 3 * parent.width / 4
height: 3 * parent.height / 4
countPWM: configuredList.model.rowCount() //This is where I want to bind.
}
//ConfiguredChannels.qml
id: confChanView
header: confChanHeader
model: ChannelModel{
id: rootModel
list: channelList
}
//TabArea.qml
Item{
id: tabAreaRoot
property alias channelsPWM: channelsPWM
property int countPWM
ScrollView{
id: scrollPWM
anchors.fill: parent
contentItem: channelsPWM.children
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOn
RowLayout{
id: channelsPWM
spacing: 0
Layout.fillHeight: true
Layout.fillWidth: true
Component.onCompleted: {
var namesPWM = [];
for (var i=0; i<countPWM; i++){
namesPWM.push("Channel"+(i+1));
}
createChannels(countPWM, "PWM", channelsPWM, namesPWM);
}
}
}
[EDIT 1]
After looking closer I realized that with my current implementation, even if I bind properly to the model's size I would still not be able to create the desired amount of RowLayout's children on demand (after I change the model in the Dialog Configuration.qml).
That is because I have put their creation inside RowLayout's Component.onCompleted handler. The content of this handler will be executed once the Configuration.qml will be initialized inside main.qmlfor the first time. After that, any other change to the countPWM will not make a difference since the Component is already Completed! Please correct me if I am wrong at this point.
Based on that, I have followed another implementation. I have created a "wrapper" function of createChannels, named createStrips(countPWM). This way, to update properly the RowLayout's children I have to call this function.
\\Configuration.qml
\\more code
currentModel.setList(newList)
tabs.createStrips(tableModel.count) \\tableModel is used to populate the newList that will be set to the model
newList.clear()
\\more code
\\TabArea.qml
function createStrips(countPWM){
var namesPWM = [];
for (var i=0; i<countPWM; i++){
namesPWM.push("Channel"+(i+1));
}
createChannels(countPWM, "PWM", channelsPWM, namesPWM);
}
function createChannels(counter, channelType, channelParent, channelMapping){
if ( channelParent.children.length !== 0){
console.log("destroying");
for ( var j = channelParent.children.length; j > 0 ; j--){
channelParent.children[j-1].destroy();
}
}
for (var i=0;i<counter;i++){
var component = Qt.createComponent(channelType+".qml");
if( component.status !== Component.Ready )
{
if( component.status === Component.Error )
console.debug("Error:"+ component.errorString() );
return; // or maybe throw
}
var channels =component.createObject(channelParent, { "id": channelType+(i+1), "channelText.text": channelMapping[i]});
}
[EDIT 2]
Although the solution in EDIT 1 works and produces the right children for my ScrollView I don't think it is good enough and I believe the best implementation would be to bind the model's size change with the call to the createStrips(countPWM) function. Something like:
\\main.qml
ConfiguredChannels{
id: configuredList
anchors{
left: parent.left
top: devices.bottom
right: tabs.left
bottom: parent.bottom
}
onModelChanged: tabs.createStrips(model.rowCount) //or an appropriate signal handler defined on C++ side
}
And perhaps even better, make the creation of the children as a custom qml signal handler that will be emitted every time the model's size is changed. (I tried the onModelChanged as above but didn't work. Probably I am missing in which case is this signal emitted)
[SOLUTION]
I followed the instructions of the accepted answer as well as this link.
I added a Q_PROPERTY to my model's definition inside the header file named rowCount with the NOTIFY rowCountChanged as well as the signal void rowCountChanged(); . Also, inside the function setList(newList) which I use to update the model, I added at the end of its implementation the emit rowCountChanged(); . Last I connected this signal with my function createStrips(count) inside QML. Now every time the model's size is changed, my ScrollView will update automatically the strips shown as the RowLayout's children.
\\ChannelModel.h
...
Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged)
...
signals:
void rowCountChanged();
\\ChannelModel.cpp
void ChannelModel::setList(ChannelList *list)
{
beginResetModel();
...
endRestModel();
emit rowCountChanged();
}
\\main.qml
Connections {
target: configuredList.model
onRowCountChanged: tabs.createStrips(configuredList.model.rowCount)
}
Only the q-property allow a binding, in your case the Q_INVOKABLE is not, so you will have to create it, for this we use the signal rowsInserted and rowsRemoved as shown below:
*.h
Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged)
public:
...
signals:
void rowCountChanged();
*.cpp
//constructor
connect(this, &QAbstractListModel::rowsInserted, this, &YourModel::rowCountChanged);
connect(this, &QAbstractListModel::rowsRemoved, this, &YourModel::rowCountChanged);
*.qml
countPWM: configuredList.model.rowCount // without ()
Note:
I'm assuming that when you add an element or remove it, you're using:
beginInsertRows(QModelIndex(), rowCount(), rowCount());
//append data
endInsertRows();
Or:
beginRemoveRows(QModelIndex(), from, to)
// remove
endRemoveRows();
You can't bind to a Q_INVOKABLE since there is no association to a change-signal.
Create a Q_PROPERTY(count READ rowCount NOTIFY rowCountChanged) or something like that. Make sure that the signal rowCountChanged is emitted when rows are inserted or removed.
Of course it would be more canonical if you had a
Q_PROPERTY(count READ count NOTIFY countChanged)
int count() { return rowCount(); }
and make sure to emit countChanged.
Then you can bind to the property count.
Summary
How can I create a component in QML that allows me to specify custom properties with default values, which can be overridden by command-line parameters, but which do NOT initially use the default value?
Background
I've created a QML component that can be used to parse command-line properties easily and bind to their values. Its usage looks like this:
Window {
visibility: appArgs.fullscreen ? "FullScreen" : "Windowed"
width: appArgs.width
height: width*9/16
CommandLineArguments {
id: appArgs
property real width: Screen.width
property bool fullscreen: false
Component.onCompleted: args(
['width', ['-w', '--width'], 'initial window width'],
['fullscreen', ['-f', '--fullscreen'], 'use full-screen mode']
)
}
}
$ ./myapp --help
qml: Command-line arguments supported:
-w, --width : initial window width (default:1920)
-f, --fullscreen : use full-screen mode (default:false)
It works quite well, except...
Problem
All bindings that are created to my component initially use the default value. For example, if I launch my app with -w 800 the window's width initially starts with a value of 1920, and then ~immediately resizes to 800 (when the Component.onCompleted code runs). This problem is unnoticeable 90% of the time, mildly annoying 8% of the time...and unusable in the final 2%.
Sometimes the properties that I want to control can only be set once. For example, a network port to connect to using fragile code that cannot disconnect and reconnect to a new port when it changes. Or a mapping library that loads an enormous set of resources for one visual style, and then throws errors if I attempt to change the style.
So, I need the properties to get the command-line value—if specified—the very first time they are created (and otherwise use the default value). How can I make this happen?
UPDATE: Actually, in that particular case it is actually very easy to avoid the resizing - just set visibility to false, then set the properties to the desired values, and set visibility to true:
Window {
id: main
visible: false
Component.onCompleted: {
main.width = ARG_Width // replace with
main.height = ARG_Width * 9/16 // your stuff
main.visibility = ARG_Fullscreen ? Window.FullScreen : Window.Windowed
main.visible = true
}
}
In this case it is convenient since you can simply hide the window until you set the desired property values. In case you actually need to create the component with the correct initial values, you can do something like this:
Item {
id: main
Component {
id: win
Window {
visible: true
width: ARG_Width
height: width*9/16
visibility: ARG_Fullscreen ? Window.FullScreen : Window.Windowed
}
}
Component.onCompleted: win.createObject(main)
}
In this case the application will start without any window, the desired values will be set on prototype level, so that its creation will be delayed and have the right values right from the start.
It is understandable that this happens, after all you don't read in the arguments until after your application has loaded. Thus it will load up the defaults and then switch to the supplied arguments.
If you want to avoid that, the most straightforward solution would be to read in the arguments and expose them as context properties before the main qml file is loaded, such as this (posting fullly working code since you mentioned you are not a C++ guy):
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
int w = 1920; // initial
bool f = false; // values
QStringList args = app.arguments();
if (args.size() > 1) { // we have arguments
QString a1 = args.at(1);
if (a1 == "-w") w = args.at(2).toInt(); // we have a -w, read in the value
else if (a1 == "-f") f = true; // we have a -f
}
engine.rootContext()->setContextProperty("ARG_Width", w); // expose as context
engine.rootContext()->setContextProperty("ARG_Fullscreen", f); // properties
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); // load main qml
return app.exec();
}
And then in your main.qml file:
Window {
id: main
visible: true
width: ARG_Width
height: width*9/16
visibility: ARG_Fullscreen ? Window.FullScreen : Window.Windowed
}
As the component is created it picks up the correct values right away.
If I completely change the interface to my component, such that the default value is passed to a function that returns a value, then I can achieve my goals.
So, instead of:
property real width: Screen.width
...
Component.onCompleted: args(
['width', ['-w', '--width'], 'initial window width'],
)
I must use something like:
property real width: arg(Screen.width, ['-w', '--width'], 'real', 'initial window width')
This new interface has some disadvantages:
I can no longer specify the order I want arguments to appear in the help, as the properties may invoke arg() in any order.
I can no longer require positional arguments with no flags (e.g. app filename1 filename2) for the same reason.
I have to repeat the type of the property in the descriptor.
It has other benefits, however:
The names of properties do not have to be repeated.
It is fewer lines of code (one line per property instead of 2).
It actually solves my problem stated above.
Example usage:
CommandLineParameters {
id: appArgs
property string message: arg('hi mom', '--message', 'string', 'message to print')
property real width: arg(400, ['-w', '--width'], 'real', 'initial window width')
property bool fullscreen: arg(false, ['-f', '--fullscreen'], 'bool', 'use full screen?')
property var resolution: arg('100x200', '--resolution', getResolution)
function getResolution(str) {
return str.split('x').map(function(s){ return s*1 });
}
}
The Code:
// CommandLineParameters.qml
import QtQml 2.2
QtObject {
property var _argVals
property var _help: []
function arg(value, flags, type, help) {
if (!_argVals) { // Parse the command line once only
_argVals = {};
var key;
for (var i=1,a=Qt.application.arguments;i<a.length;++i){
if (/^--?\S/.test(a[i])) _argVals[key=a[i]] = true;
else if (key) _argVals[key]=a[i], key=0;
else console.log('Unexpected command-line parameter "'+a[i]+'');
}
}
_help.push([flags.join?flags.join(", "):flags, help||'', '(default:'+value+')']);
// Replace the default value with one from command line
if (flags.forEach) flags.forEach(lookForFlag);
else lookForFlag(flags);
// Convert types to appropriate values
if (typeof type==='function') value = type(value);
else if (type=='real' || type=='int') value *= 1;
return value;
function lookForFlag(f) { if (_argVals[f] !== undefined) value=_argVals[f] }
}
Component.onCompleted: {
// Give help, if requested
if (_argVals['-h'] || _argVals['--help']) {
var maxF=Math.max.apply(Math,_help.map(function(a){return a[0].length}));
var maxH=Math.max.apply(Math,_help.map(function(a){return a[1].length}));
var lines=_help.map(function(a){
return pad(a[0],maxF)+" : "+pad(a[1],maxH)+" "+a[2];
});
console.log("Command-line arguments supported:\n"+lines.join("\n"));
Qt.quit(); // Requires connecting the slot in the main application
}
function pad(s,n){ return s+Array(n-s.length+1).join(' ') }
}
}
I am trying to add a qquickwidget along with some other qwidgets in qstackedwidget. But when I am trying to set the current widget to the qquickwidget nothing appears on the window. Is there something else that need to be done? I am also setting the view property of the qquickwidget to true
QQuickWidget* mRoom = new QQuickWidget;
connect(mRoom, SIGNAL(statusChanged(QQuickWidget::Status)), this, SLOT(StatusChanged(QQuickWidget::Status)));
mRoom->setSource(QUrl::fromLocalFile("C:/Users/visjain/Desktop/main_vishwas.qml"));
mRoom->setResizeMode(QQuickWidget::SizeRootObjectToView);
QStackedWidget* mStack = new QStackedWidget(mparent);
mStack->addWidget(mRoom);
mStack->setCurrentWidget(mRoom);
mRoom->show();
qml code -
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
height: 1000
width: 1800
Rectangle{
height: parent.height
width: parent.width
color: "red"
}
}
Did you assign some QML file to the widget?
QQuickWidget *view = new QQuickWidget;
view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
view->show();
Some more source code might be helpful.
For some more detailed reference you might see this.
For putting a QWidget inside a QStackedWidget to the front you should use setCurrentIndex or setCurrentWidget. See this.
We have a fairly big QtQuick application, with a lot of modal dialogs. All of these modals share a consistent look and behaviour, and have leftButtons, rightButtons, a content and additional warning widgets. We use the following base class (PFDialog.qml):
Window {
property alias content: contentLayout.children
ColumnLayout {
id: contentLayout
}
}
and declare dialogs in the following way (main.qml):
Window {
visible: true
property var window: PFDialog {
content: Text { text: "Foobar" }
}
}
The problem is that when the application is closed, a segfault happens in the QQuickItem destructor. This segfault is hard to reproduce, but here is a surefire way of making it happen: with visual studio in debug mode, freed memory is filled with 0xDDDDDDD with triggers the segfault every time.
Full example application can be found here: https://github.com/wesen/testWindowCrash
The crash happens in QQuickItem::~QQuickItem:
for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate();
if (anchor)
anchor->clearItem(this);
}
The reason for this is that the content of our dialog (the Text item in the example above) is a QObject child of the main Window, but a visual child of the dialog window. When closing the application, the dialog window is destroyed first, and at the time the Text item is deleted, the dialog window (still registered as a changeListener) is stale.
Now my question is:
is this a QtQuick bug? Should the dialog deregister itself as a changeListener for its children when it is destroyed (I think it should)
is our property alias content: layout.children pattern correct, or is there a better way to do this? This also happens when declaring a default property alias.
For the sake of completeness, here is how we hotfix it in our application. When content changes, we reparent all the items to the layout item. A of elegance, as you will all agree.
function reparentTo(objects, newParent) {
for (var i = 0; i < objects.length; i++) {
qmlHelpers.qml_SetQObjectParent(objects[i], newParent)
}
}
onContentChanged: reparentTo(content, contentLayout)
I have had this problem lots of times, I don't think it is a bug, more like a design limitation. The more implicit behavior you get, the less control you have, leading to inappropriate orders of object destruction and access to dangling references.
There are numerous situations where this can happen "on its own" as you exceed the bounds of a trivial "by the book" qml application, but in your case it is you who's doing it.
If you want proper ownership, don't use this:
property var window: PFDialog {
content: Text { text: "Foobar" }
}
Instead use this:
property Window window: dlg // if you need to access it externally
PFDialog {
id: dlg
content: Text { text: "Foobar" }
}
Here is a good reason why:
property var item : Item {
Item {
Component.onCompleted: console.log(parent) // qml: QQuickItem(0x4ed720) - OK
}
}
// vs
property var item : Item {
property var i: Item {
Component.onCompleted: console.log(parent) // qml: null - BAD
}
}
A child is not the same as a property. Properties are still collected but they are not parented.
As for achieving the "dynamic content" thingie, I've had good results with ObjectModel:
Window {
property ObjectModel layout
ListView {
width: contentItem.childrenRect.width // expand to content size
height: contentItem.childrenRect.height
model: layout
interactive: false // don't flick
orientation: ListView.Vertical
}
}
Then:
PFDialog {
layout: ObjectModel {
Text { text: "Foobar" }
// other stuff
}
}
Lastly, for the sake of doing explicit cleanups before closing the application, on your main QML file you can implement a handler:
onClosing: {
if (!canExit) doCleanup()
close.accepted = true
}
This ensures the window will not be destroyed without doing the cleanup first.
Finally:
is our property alias content: layout.children pattern correct, or is
there a better way to do this? This also happens when declaring a
default property alias.
It wasn't last time I looked into it, but it was at least couple of years back. It would certainly be nice to have objects declared as children actually becoming children of some other object, but at the time this was not possible, and still may not be. Thus the need for the slightly more verbose solution involving the object model and the list view. If you investigate the matter and find something different, leave a comment to let me know.
I believe that you cannot declare a Window Object in a var. In my tests the SubWindow never open and sometimes broken on startup.
A Window can be declared inside an Item or inside another Window; in that case the inner Window will automatically become "transient for" the outer Window
See: http://doc.qt.io/qt-5/qml-qtquick-window-window.html
Modify your code to this:
Window {
visible: true
PFDialog {
content: Text { text: "Foobar" }
}
}
In QtQuick 2 using the QtQuick Controls you can create complex desktop apps. However it seems to me that the entire UI must be declared and create all at once at the start of the app. Any parts that you don't want to use yet (for example the File->Open dialog) must still be created but they are hidden, like this:
ApplicationWindow {
FileDialog {
id: fileOpenDialog
visible: false
// ...
}
FileDialog {
id: fileSaveDialog
visible: false
// ...
}
// And so on for every window in your app and every piece of UI.
Now, this may be fine for simple apps, but for complex ones or apps with many dialogs surely this is a crazy thing to do? In the traditional QtWidgets model you would dynamically create your dialog when needed.
I know there are some workarounds for this, e.g. you can use a Loader or even create QML objects dynamically directly in javascript, but they are very ugly and you lose all the benefits of the nice QML syntax. Also you can't really "unload" the components. Well Loader claims you can but I tried it and my app crashed.
Is there an elegant solution to this problem? Or do I simply have to bite the bullet and create all the potential UI for my app at once and then hide most of it?
Note: this page has information about using Loaders to get around this, but as you can see it is not a very nice solution.
Edit 1 - Why is Loader suboptimal?
Ok, to show you why Loader is not really that pleasant, consider this example which starts some complex task and waits for a result. Suppose that - unlike all the trivial examples people usually give - the task has many inputs and several outputs.
This is the Loader solution:
Window {
Loader {
id: task
source: "ComplexTask.qml"
active: false
}
TextField {
id: input1
}
TextField {
id: output1
}
Button {
text: "Begin complex task"
onClicked: {
// Show the task.
if (task.active === false)
{
task.active = true;
// Connect completed signal if it hasn't been already.
task.item.taskCompleted.connect(onTaskCompleted)
}
view.item.input1 = input1.text;
// And several more lines of that...
}
}
}
function onTaskCompleted()
{
output1.text = view.item.output1
// And several more lines...
// This actually causes a crash in my code:
// view.active = false;
}
}
If I was doing it without Loader, I could have something like this:
Window {
ComplexTask {
id: task
taskInput1: input1.text
componentLoaded: false
onCompleted: componentLoaded = false
}
TextField {
id: input1
}
TextField {
id: output1
text: task.taskOutput1
}
Button {
text: "Begin complex task"
onClicked: task.componentLoaded = true
}
}
That is obviously way simpler. What I clearly want is some way for the ComplexTask to be loaded and have all its declarative relationships activated when componentLoaded is set to true, and then have the relationships disconnected and unload the component when componentLoaded is set to false. I'm pretty sure there is no way to make something like this in Qt currently.
Creating QML components from JS dynamically is just as ugly as creating widgets from C++ dynamically (if not less so, as it is actually more flexible). There is nothing ugly about it, you can implement your QML components in separate files, use every assistance Creator provides in their creation, and instantiate those components wherever you need them as much as you need them. It is far uglier to have everything hidden from the get go, it is also a lot heavier and it could not possibly anticipate everything that might happen as well dynamic component instantiation can.
Here is a minimalistic self-contained example, it doesn't even use a loader, since the dialog is locally available QML file.
Dialog.qml
Rectangle {
id: dialog
anchors.fill: parent
color: "lightblue"
property var target : null
Column {
TextField {
id: name
text: "new name"
}
Button {
text: "OK"
onClicked: {
if (target) target.text = name.text
dialog.destroy()
}
}
Button {
text: "Cancel"
onClicked: dialog.destroy()
}
}
}
main.qml
ApplicationWindow {
visible: true
width: 200
height: 200
Button {
id: button
text: "rename me"
width: 200
onClicked: {
var component = Qt.createComponent("Dialog.qml")
var obj = component.createObject(overlay)
obj.target = button
}
}
Item {
id: overlay
anchors.fill: parent
}
}
Also, the above example is very barebone and just for the sake of illustration, consider using a stack view, either your own implementation or the available since 5.1 stock StackView.
Here's a slight alternative to ddriver's answer that doesn't call Qt.createComponent() every time you create an instance of that component (which will be quite slow):
// Message dialog box component.
Component {
id: messageBoxFactory
MessageDialog {
}
}
// Create and show a new message box.
function showMessage(text, title, modal)
{
if (typeof modal === 'undefined')
modal = true;
// mainWindow is the parent. We can also specify initial property values.
var messageDialog = messageBoxFactory.createObject(mainWindow, {
text: text,
title: title,
visible: true,
modality: modal ? Qt.ApplicationModal : Qt.NonModal
} );
messageDialog.accepted.connect(messageDialog.destroy);
messageDialog.rejected.connect(messageDialog.destroy);
}
I think loading and unloading elements is not actual any more because every user have more than 2GB RAM.
And do you think your app can take more than even 512 MB ram? I doubt it.
You should load qml elements and don't unload them, no crashes will happens, just store all pointers and manipulate qml frames.
If you just keep all your QML elements in RAM and store their states, it will works faster and looks better.
Example is my project that developed in that way: https://youtube.com/watch?v=UTMOd2s9Vkk
I have made base frame that inherited by all windows. This frame does have methods hide/show and resetState. Base window does contains all child frames, so via signal/slots other frames show/hide next required frame.