Fix async issues related to JS / QML object lifetimes

This commit is contained in:
Brad Davis 2016-03-05 02:49:55 -08:00
parent dbfea6ec82
commit 8bbabf186f
4 changed files with 50 additions and 40 deletions

View file

@ -31,19 +31,18 @@ static const char* const URL_PROPERTY = "source";
// Method called by Qt scripts to create a new web window in the overlay // Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine, return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine,
[&](QObject* object) { return new QmlWebWindowClass(object); }); [&](QObject* object) { return new QmlWebWindowClass(object); });
} }
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) { QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
} }
// FIXME remove.
void QmlWebWindowClass::handleNavigation(const QString& url) {
}
QString QmlWebWindowClass::getURL() const { QString QmlWebWindowClass::getURL() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant();
}
return _qmlWindow->property(URL_PROPERTY); return _qmlWindow->property(URL_PROPERTY);
}); });
return result.toString(); return result.toString();
@ -54,6 +53,8 @@ extern QString fixupHifiUrl(const QString& urlString);
void QmlWebWindowClass::setURL(const QString& urlString) { void QmlWebWindowClass::setURL(const QString& urlString) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString)); if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
}
}); });
} }

View file

@ -26,9 +26,6 @@ public slots:
signals: signals:
void urlChanged(); void urlChanged();
private slots:
void handleNavigation(const QString& url);
}; };
#endif #endif

View file

@ -214,7 +214,7 @@ QmlWindowClass::QmlWindowClass(QObject* qmlWindow)
{ {
qDebug() << "Created window with ID " << _windowId; qDebug() << "Created window with ID " << _windowId;
Q_ASSERT(_qmlWindow); Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow)); Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
// Forward messages received from QML on to the script // Forward messages received from QML on to the script
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection); connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection);
} }
@ -240,7 +240,7 @@ QQuickItem* QmlWindowClass::asQuickItem() const {
if (_toolWindow) { if (_toolWindow) {
return DependencyManager::get<OffscreenUi>()->getToolWindow(); return DependencyManager::get<OffscreenUi>()->getToolWindow();
} }
return dynamic_cast<QQuickItem*>(_qmlWindow); return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data());
} }
void QmlWindowClass::setVisible(bool visible) { void QmlWindowClass::setVisible(bool visible) {
@ -260,32 +260,34 @@ void QmlWindowClass::setVisible(bool visible) {
bool QmlWindowClass::isVisible() const { bool QmlWindowClass::isVisible() const {
// The tool window itself has special logic based on whether any tabs are enabled // The tool window itself has special logic based on whether any tabs are enabled
if (_toolWindow) { return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
auto targetTab = dynamic_cast<QQuickItem*>(_qmlWindow); if (_qmlWindow.isNull()) {
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { return QVariant::fromValue(false);
return QVariant::fromValue(targetTab->isEnabled()); }
}).toBool(); if (_toolWindow) {
} else { return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled());
QQuickItem* targetWindow = asQuickItem(); } else {
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { return QVariant::fromValue(asQuickItem()->isVisible());
return QVariant::fromValue(targetWindow->isVisible()); }
}).toBool(); }).toBool();
}
} }
glm::vec2 QmlWindowClass::getPosition() const { glm::vec2 QmlWindowClass::getPosition() const {
QQuickItem* targetWindow = asQuickItem();
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
return targetWindow->position(); if (_qmlWindow.isNull()) {
return QVariant(QPointF(0, 0));
}
return asQuickItem()->position();
}); });
return toGlm(result.toPointF()); return toGlm(result.toPointF());
} }
void QmlWindowClass::setPosition(const glm::vec2& position) { void QmlWindowClass::setPosition(const glm::vec2& position) {
QQuickItem* targetWindow = asQuickItem();
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetWindow->setPosition(QPointF(position.x, position.y)); if (!_qmlWindow.isNull()) {
asQuickItem()->setPosition(QPointF(position.x, position.y));
}
}); });
} }
@ -299,17 +301,21 @@ glm::vec2 toGlm(const QSizeF& size) {
} }
glm::vec2 QmlWindowClass::getSize() const { glm::vec2 QmlWindowClass::getSize() const {
QQuickItem* targetWindow = asQuickItem();
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant(QSizeF(0, 0));
}
QQuickItem* targetWindow = asQuickItem();
return QSizeF(targetWindow->width(), targetWindow->height()); return QSizeF(targetWindow->width(), targetWindow->height());
}); });
return toGlm(result.toSizeF()); return toGlm(result.toSizeF());
} }
void QmlWindowClass::setSize(const glm::vec2& size) { void QmlWindowClass::setSize(const glm::vec2& size) {
QQuickItem* targetWindow = asQuickItem();
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetWindow->setSize(QSizeF(size.x, size.y)); if (!_qmlWindow.isNull()) {
asQuickItem()->setSize(QSizeF(size.x, size.y));
}
}); });
} }
@ -318,9 +324,10 @@ void QmlWindowClass::setSize(int width, int height) {
} }
void QmlWindowClass::setTitle(const QString& title) { void QmlWindowClass::setTitle(const QString& title) {
QQuickItem* targetWindow = asQuickItem();
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetWindow->setProperty(TITLE_PROPERTY, title); if (!_qmlWindow.isNull()) {
asQuickItem()->setProperty(TITLE_PROPERTY, title);
}
}); });
} }
@ -345,7 +352,12 @@ void QmlWindowClass::hasClosed() {
} }
void QmlWindowClass::raise() { void QmlWindowClass::raise() {
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
}
});
} }
#include "QmlWindowClass.moc" #include "QmlWindowClass.moc"

View file

@ -10,11 +10,13 @@
#define hifi_ui_QmlWindowClass_h #define hifi_ui_QmlWindowClass_h
#include <QtCore/QObject> #include <QtCore/QObject>
#include <GLMHelpers.h> #include <QtCore/QPointer>
#include <QtScript/QScriptValue> #include <QtScript/QScriptValue>
#include <QtQuick/QQuickItem> #include <QtQuick/QQuickItem>
#include <QtWebChannel/QWebChannelAbstractTransport> #include <QtWebChannel/QWebChannelAbstractTransport>
#include <GLMHelpers.h>
class QScriptEngine; class QScriptEngine;
class QScriptContext; class QScriptContext;
class QmlWindowClass; class QmlWindowClass;
@ -38,14 +40,13 @@ private:
const QmlWindowClass* _webWindow { nullptr }; const QmlWindowClass* _webWindow { nullptr };
QWebSocket *_socket { nullptr }; QWebSocket *_socket { nullptr };
}; };
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
class QmlWindowClass : public QObject { class QmlWindowClass : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT) Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
Q_PROPERTY(int windowId READ getWindowId CONSTANT) Q_PROPERTY(int windowId READ getWindowId CONSTANT)
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged)
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize) Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
public: public:
@ -64,11 +65,8 @@ public slots:
glm::vec2 getSize() const; glm::vec2 getSize() const;
void setSize(const glm::vec2& size); void setSize(const glm::vec2& size);
void setSize(int width, int height); void setSize(int width, int height);
void setTitle(const QString& title); void setTitle(const QString& title);
// Ugh.... do not want to do
Q_INVOKABLE void raise(); Q_INVOKABLE void raise();
Q_INVOKABLE void close(); Q_INVOKABLE void close();
Q_INVOKABLE int getWindowId() const { return _windowId; }; Q_INVOKABLE int getWindowId() const { return _windowId; };
@ -79,6 +77,8 @@ public slots:
signals: signals:
void visibilityChanged(bool visible); // Tool window void visibilityChanged(bool visible); // Tool window
void positionChanged();
void sizeChanged();
void moved(glm::vec2 position); void moved(glm::vec2 position);
void resized(QSizeF size); void resized(QSizeF size);
void closed(); void closed();
@ -104,7 +104,7 @@ protected:
// for tool window panes in QML // for tool window panes in QML
bool _toolWindow { false }; bool _toolWindow { false };
const int _windowId; const int _windowId;
QObject* _qmlWindow; QPointer<QObject> _qmlWindow;
QString _source; QString _source;
}; };