From 556569819dd59956e026c4358d949cfccb911805 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 9 Aug 2017 14:00:19 +0200 Subject: [PATCH] 1st stage of moving to async dialogs --- interface/src/Application.cpp | 165 +++++++++-------- interface/src/Application.h | 1 - interface/src/ModelPackager.cpp | 6 +- interface/src/ModelPropertiesDialog.cpp | 2 +- interface/src/assets/ATPAssetMigrator.cpp | 172 +++++++++--------- .../scripting/WindowScriptingInterface.cpp | 4 +- .../trackers/src/trackers/EyeTracker.cpp | 8 +- libraries/ui/src/OffscreenUi.cpp | 33 +++- libraries/ui/src/OffscreenUi.h | 23 ++- 9 files changed, 244 insertions(+), 170 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e5fb9949db..a72faad851 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -894,7 +894,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(scriptEngines, &ScriptEngines::scriptLoadError, scriptEngines, [](const QString& filename, const QString& error){ - OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load."); + OffscreenUi::asyncWarning(nullptr, "Error Loading Script", filename + " failed to load."); }, Qt::QueuedConnection); #ifdef _WIN32 @@ -1689,7 +1689,7 @@ void Application::domainConnectionRefused(const QString& reasonMessage, int reas case DomainHandler::ConnectionRefusedReason::Unknown: { QString message = "Unable to connect to the location you are visiting.\n"; message += reasonMessage; - OffscreenUi::warning("", message); + OffscreenUi::asyncWarning("", message); break; } default: @@ -3626,7 +3626,7 @@ bool Application::acceptSnapshot(const QString& urlString) { DependencyManager::get()->handleLookupString(snapshotData->getURL().toString()); } } else { - OffscreenUi::warning("", "No location details were found in the file\n" + + OffscreenUi::asyncWarning("", "No location details were found in the file\n" + snapshotPath + "\nTry dragging in an authentic Hifi snapshot."); } return true; @@ -5997,7 +5997,7 @@ void Application::setSessionUUID(const QUuid& sessionUUID) const { bool Application::askToSetAvatarUrl(const QString& url) { QUrl realUrl(url); if (realUrl.isLocalFile()) { - OffscreenUi::warning("", "You can not use local files for avatar components."); + OffscreenUi::asyncWarning("", "You can not use local files for avatar components."); return false; } @@ -6009,41 +6009,61 @@ bool Application::askToSetAvatarUrl(const QString& url) { QString modelName = fstMapping["name"].toString(); QString modelLicense = fstMapping["license"].toString(); - bool agreeToLicence = true; // assume true + bool agreeToLicense = true; // assume true + //create set avatar callback + auto setAvatar = [=] (QString url, QString modelName) { + auto offscreenUi = DependencyManager::get(); + + QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=] (QMessageBox::StandardButton answer) { + auto offscreenUi = DependencyManager::get(); + QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); + + bool ok = (QMessageBox::Ok == answer); + if (ok) { + getMyAvatar()->useFullAvatarURL(url, modelName); + emit fullAvatarURLChanged(url, modelName); + } else { + qCDebug(interfaceapp) << "Declined to use the avatar: " << url; + } + }); + OffscreenUi::asyncQuestion("Set Avatar", + "Would you like to use '" + modelName + "' for your avatar?", + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok); + }; + if (!modelLicense.isEmpty()) { - // word wrap the licence text to fit in a reasonable shaped message box. + // word wrap the license text to fit in a reasonable shaped message box. const int MAX_CHARACTERS_PER_LINE = 90; modelLicense = simpleWordWrap(modelLicense, MAX_CHARACTERS_PER_LINE); - agreeToLicence = QMessageBox::Yes == OffscreenUi::question("Avatar Usage License", + auto offscreenUi = DependencyManager::get(); + QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=, &agreeToLicense] (QMessageBox::StandardButton answer) { + auto offscreenUi = DependencyManager::get(); + QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); + + agreeToLicense = (answer == QMessageBox::Yes); + if (agreeToLicense) { + switch (modelType) { + case FSTReader::HEAD_AND_BODY_MODEL: { + setAvatar(url, modelName); + break; + } + default: + OffscreenUi::asyncWarning("", modelName + "Does not support a head and body as required."); + break; + } + } else { + qCDebug(interfaceapp) << "Declined to agree to avatar license: " << url; + } + + //auto offscreenUi = DependencyManager::get(); + }); + + OffscreenUi::asyncQuestion("Avatar Usage License", modelLicense + "\nDo you agree to these terms?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - } - - bool ok = false; - - if (!agreeToLicence) { - qCDebug(interfaceapp) << "Declined to agree to avatar license: " << url; } else { - switch (modelType) { - - case FSTReader::HEAD_AND_BODY_MODEL: - ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar", - "Would you like to use '" + modelName + "' for your avatar?", - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok); - break; - - default: - OffscreenUi::warning("", modelName + "Does not support a head and body as required."); - break; - } - } - - if (ok) { - getMyAvatar()->useFullAvatarURL(url, modelName); - emit fullAvatarURLChanged(url, modelName); - } else { - qCDebug(interfaceapp) << "Declined to use the avatar: " << url; + setAvatar(url, modelName); } return true; @@ -6051,8 +6071,6 @@ bool Application::askToSetAvatarUrl(const QString& url) { bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { - QMessageBox::StandardButton reply; - QString shortName = scriptFilenameOrURL; QUrl scriptURL { scriptFilenameOrURL }; @@ -6063,16 +6081,23 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { shortName = shortName.mid(startIndex, endIndex - startIndex); } + auto offscreenUi = DependencyManager::get(); + QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=] (QMessageBox::StandardButton answer) { + const QString& fileName = scriptFilenameOrURL; + if (answer == QMessageBox::Yes) { + qCDebug(interfaceapp) << "Chose to run the script: " << fileName; + DependencyManager::get()->loadScript(fileName); + } else { + qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL; + } + auto offscreenUi = DependencyManager::get(); + QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); + }); + QString message = "Would you like to run this script:\n" + shortName; - reply = OffscreenUi::question(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No); + OffscreenUi::asyncQuestion(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No); - if (reply == QMessageBox::Yes) { - qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL; - DependencyManager::get()->loadScript(scriptFilenameOrURL); - } else { - qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL; - } return true; } @@ -6109,22 +6134,29 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) { name = nameValue.toString(); } - // display confirmation dialog - if (displayAvatarAttachmentConfirmationDialog(name)) { - - // add attachment to avatar - auto myAvatar = getMyAvatar(); - assert(myAvatar); - auto attachmentDataVec = myAvatar->getAttachmentData(); - AttachmentData attachmentData; - attachmentData.fromJson(jsonObject); - attachmentDataVec.push_back(attachmentData); - myAvatar->setAttachmentData(attachmentDataVec); - - } else { - qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url; - } + auto offscreenUi = DependencyManager::get(); + QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=] (QMessageBox::StandardButton answer) { + auto offscreenUi = DependencyManager::get(); + QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); + if (answer == QMessageBox::Yes) { + // add attachment to avatar + auto myAvatar = getMyAvatar(); + assert(myAvatar); + auto attachmentDataVec = myAvatar->getAttachmentData(); + AttachmentData attachmentData; + attachmentData.fromJson(jsonObject); + attachmentDataVec.push_back(attachmentData); + myAvatar->setAttachmentData(attachmentDataVec); + } else { + qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url; + } + }); + auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation"); + auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name); + OffscreenUi::asyncQuestion(avatarAttachmentConfirmationTitle, + avatarAttachmentConfirmationMessage, + QMessageBox::Ok | QMessageBox::Cancel); } else { // json parse error auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\""); @@ -6142,20 +6174,7 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) { void Application::displayAvatarAttachmentWarning(const QString& message) const { auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure"); - OffscreenUi::warning(avatarAttachmentWarningTitle, message); -} - -bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) const { - auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation"); - auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name); - auto reply = OffscreenUi::question(avatarAttachmentConfirmationTitle, - avatarAttachmentConfirmationMessage, - QMessageBox::Ok | QMessageBox::Cancel); - if (QMessageBox::Ok == reply) { - return true; - } else { - return false; - } + OffscreenUi::asyncWarning(avatarAttachmentWarningTitle, message); } void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const { @@ -6722,7 +6741,7 @@ void Application::setPreviousScriptLocation(const QString& location) { void Application::loadScriptURLDialog() const { QString newScript = OffscreenUi::getText(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL"); if (QUrl(newScript).scheme() == "atp") { - OffscreenUi::warning("Error Loading Script", "Cannot load client script over ATP"); + OffscreenUi::asyncWarning("Error Loading Script", "Cannot load client script over ATP"); } else if (!newScript.isEmpty()) { DependencyManager::get()->loadScript(newScript.trimmed()); } @@ -6836,7 +6855,7 @@ void Application::notifyPacketVersionMismatch() { QString message = "The location you are visiting is running an incompatible server version.\n"; message += "Content may not display properly."; - OffscreenUi::warning("", message); + OffscreenUi::asyncWarning("", message); } } @@ -6845,7 +6864,7 @@ void Application::checkSkeleton() const { qCDebug(interfaceapp) << "MyAvatar model has no skeleton"; QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; - OffscreenUi::warning("", message); + OffscreenUi::asyncWarning("", message); getMyAvatar()->useFullAvatarURL(AvatarData::defaultFullAvatarModelUrl(), DEFAULT_FULL_AVATAR_MODEL_NAME); } else { diff --git a/interface/src/Application.h b/interface/src/Application.h index f8eb393f9e..21ccfd30f0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -428,7 +428,6 @@ private slots: bool askToWearAvatarAttachmentUrl(const QString& url); void displayAvatarAttachmentWarning(const QString& message) const; - bool displayAvatarAttachmentConfirmationDialog(const QString& name) const; void setSessionUUID(const QUuid& sessionUUID) const; diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 44768db93c..5f4c7526e0 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -79,7 +79,7 @@ bool ModelPackager::loadModel() { if (_modelFile.completeSuffix().contains("fst")) { QFile fst(_modelFile.filePath()); if (!fst.open(QFile::ReadOnly | QFile::Text)) { - OffscreenUi::warning(NULL, + OffscreenUi::asyncWarning(NULL, QString("ModelPackager::loadModel()"), QString("Could not open FST file %1").arg(_modelFile.filePath()), QMessageBox::Ok); @@ -98,7 +98,7 @@ bool ModelPackager::loadModel() { // open the fbx file QFile fbx(_fbxInfo.filePath()); if (!_fbxInfo.exists() || !_fbxInfo.isFile() || !fbx.open(QIODevice::ReadOnly)) { - OffscreenUi::warning(NULL, + OffscreenUi::asyncWarning(NULL, QString("ModelPackager::loadModel()"), QString("Could not open FBX file %1").arg(_fbxInfo.filePath()), QMessageBox::Ok); @@ -408,7 +408,7 @@ bool ModelPackager::copyTextures(const QString& oldDir, const QDir& newDir) { } if (!errors.isEmpty()) { - OffscreenUi::warning(nullptr, "ModelPackager::copyTextures()", + OffscreenUi::asyncWarning(nullptr, "ModelPackager::copyTextures()", "Missing textures:" + errors); qCDebug(interfaceapp) << "ModelPackager::copyTextures():" << errors; return false; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index d41a913c95..ae352974ae 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -201,7 +201,7 @@ void ModelPropertiesDialog::chooseTextureDirectory() { return; } if (!directory.startsWith(_basePath)) { - OffscreenUi::warning(NULL, "Invalid texture directory", "Texture directory must be child of base path."); + OffscreenUi::asyncWarning(NULL, "Invalid texture directory", "Texture directory must be child of base path."); return; } _textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1)); diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 667c2587b0..63147c8798 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -53,105 +53,109 @@ void ATPAssetMigrator::loadEntityServerFile() { " continue?\n\nMake sure you are connected to the right domain." }; - auto button = OffscreenUi::question(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - - if (button == QMessageBox::No) { - return; - } - - // try to open the file at the given filename - QFile modelsFile { filename }; - - if (modelsFile.open(QIODevice::ReadOnly)) { - QByteArray compressedJsonData = modelsFile.readAll(); - QByteArray jsonData; - - if (!gunzip(compressedJsonData, jsonData)) { - OffscreenUi::warning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format."); - } - - QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData); - _entitiesArray = modelsJSON.object()["Entities"].toArray(); - - for (auto jsonValue : _entitiesArray) { - QJsonObject entityObject = jsonValue.toObject(); - QString modelURLString = entityObject.value(MODEL_URL_KEY).toString(); - QString compoundURLString = entityObject.value(COMPOUND_SHAPE_URL_KEY).toString(); + auto offscreenUi = DependencyManager::get(); - for (int i = 0; i < 2; ++i) { - bool isModelURL = (i == 0); - quint8 replacementType = i; - auto migrationURLString = (isModelURL) ? modelURLString : compoundURLString; + QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=] (QMessageBox::StandardButton answer) { + auto offscreenUi = DependencyManager::get(); + QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); - if (!migrationURLString.isEmpty()) { - QUrl migrationURL = QUrl(migrationURLString); + if (QMessageBox::Yes == answer) { + // try to open the file at the given filename + QFile modelsFile { filename }; - if (!_ignoredUrls.contains(migrationURL) - && (migrationURL.scheme() == URL_SCHEME_HTTP || migrationURL.scheme() == URL_SCHEME_HTTPS - || migrationURL.scheme() == URL_SCHEME_FILE || migrationURL.scheme() == URL_SCHEME_FTP)) { + if (modelsFile.open(QIODevice::ReadOnly)) { + QByteArray compressedJsonData = modelsFile.readAll(); + QByteArray jsonData; - if (_pendingReplacements.contains(migrationURL)) { - // we already have a request out for this asset, just store the QJsonValueRef - // so we can do the hash replacement when the request comes back - _pendingReplacements.insert(migrationURL, { jsonValue, replacementType }); - } else if (_uploadedAssets.contains(migrationURL)) { - // we already have a hash for this asset - // so just do the replacement immediately - if (isModelURL) { - entityObject[MODEL_URL_KEY] = _uploadedAssets.value(migrationURL).toString(); - } else { - entityObject[COMPOUND_SHAPE_URL_KEY] = _uploadedAssets.value(migrationURL).toString(); - } + if (!gunzip(compressedJsonData, jsonData)) { + OffscreenUi::asyncWarning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format."); + } - jsonValue = entityObject; - } else if (wantsToMigrateResource(migrationURL)) { - auto request = - DependencyManager::get()->createResourceRequest(this, migrationURL); + QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData); + _entitiesArray = modelsJSON.object()["Entities"].toArray(); - if (request) { - qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration"; + for (auto jsonValue : _entitiesArray) { + QJsonObject entityObject = jsonValue.toObject(); + QString modelURLString = entityObject.value(MODEL_URL_KEY).toString(); + QString compoundURLString = entityObject.value(COMPOUND_SHAPE_URL_KEY).toString(); - // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL - // to an ATP one once ready - _pendingReplacements.insert(migrationURL, { jsonValue, (isModelURL ? 0 : 1)}); + for (int i = 0; i < 2; ++i) { + bool isModelURL = (i == 0); + quint8 replacementType = i; + auto migrationURLString = (isModelURL) ? modelURLString : compoundURLString; - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - migrateResource(request); + if (!migrationURLString.isEmpty()) { + QUrl migrationURL = QUrl(migrationURLString); + + if (!_ignoredUrls.contains(migrationURL) + && (migrationURL.scheme() == URL_SCHEME_HTTP || migrationURL.scheme() == URL_SCHEME_HTTPS + || migrationURL.scheme() == URL_SCHEME_FILE || migrationURL.scheme() == URL_SCHEME_FTP)) { + + if (_pendingReplacements.contains(migrationURL)) { + // we already have a request out for this asset, just store the QJsonValueRef + // so we can do the hash replacement when the request comes back + _pendingReplacements.insert(migrationURL, { jsonValue, replacementType }); + } else if (_uploadedAssets.contains(migrationURL)) { + // we already have a hash for this asset + // so just do the replacement immediately + if (isModelURL) { + entityObject[MODEL_URL_KEY] = _uploadedAssets.value(migrationURL).toString(); + } else { + entityObject[COMPOUND_SHAPE_URL_KEY] = _uploadedAssets.value(migrationURL).toString(); + } + + jsonValue = entityObject; + } else if (wantsToMigrateResource(migrationURL)) { + auto request = + DependencyManager::get()->createResourceRequest(this, migrationURL); + + if (request) { + qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration"; + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(migrationURL, { jsonValue, (isModelURL ? 0 : 1)}); + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + ++_errorCount; + _pendingReplacements.remove(migrationURL); + qWarning() << "Could not retrieve asset at" << migrationURL.toString(); + + checkIfFinished(); + } + request->deleteLater(); + }); + + request->send(); } else { ++_errorCount; - _pendingReplacements.remove(migrationURL); - qWarning() << "Could not retrieve asset at" << migrationURL.toString(); - - checkIfFinished(); + qWarning() << "Count not create request for asset at" << migrationURL.toString(); } - request->deleteLater(); - }); - request->send(); - } else { - ++_errorCount; - qWarning() << "Count not create request for asset at" << migrationURL.toString(); + } else { + _ignoredUrls.insert(migrationURL); + } } - - } else { - _ignoredUrls.insert(migrationURL); - } } + } + } + + _doneReading = true; + + checkIfFinished(); + + } else { + OffscreenUi::asyncWarning(_dialogParent, "Error", + "There was a problem loading that entity-server file for ATP asset migration. Please try again"); } - } - - _doneReading = true; - - checkIfFinished(); - - } else { - OffscreenUi::warning(_dialogParent, "Error", - "There was a problem loading that entity-server file for ATP asset migration. Please try again"); - } + }); + OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); } } @@ -314,11 +318,11 @@ void ATPAssetMigrator::saveEntityServerFile() { OffscreenUi::information(_dialogParent, "Success", infoMessage); } else { - OffscreenUi::warning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); + OffscreenUi::asyncWarning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); } } else { - OffscreenUi::warning(_dialogParent, "Error", + OffscreenUi::asyncWarning(_dialogParent, "Error", QString("Could not open file at %1 to write new entities file to.").arg(saveName)); } } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 84f4cbbbd8..36381b3626 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -59,7 +59,7 @@ WindowScriptingInterface::WindowScriptingInterface() { QUrl url(urlString); emit svoImportRequested(url.url()); } else { - OffscreenUi::warning("Import SVO Error", "You need to be running edit.js to import entities."); + OffscreenUi::asyncWarning("Import SVO Error", "You need to be running edit.js to import entities."); } }); @@ -103,7 +103,7 @@ void WindowScriptingInterface::raiseMainWindow() { /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue void WindowScriptingInterface::alert(const QString& message) { - OffscreenUi::warning("", message); + OffscreenUi::asyncWarning("", message); } /// Display a confirmation box with the options 'Yes' and 'No' diff --git a/libraries/trackers/src/trackers/EyeTracker.cpp b/libraries/trackers/src/trackers/EyeTracker.cpp index 8733461dbb..e641abc630 100644 --- a/libraries/trackers/src/trackers/EyeTracker.cpp +++ b/libraries/trackers/src/trackers/EyeTracker.cpp @@ -136,7 +136,7 @@ void EyeTracker::onStreamStarted() { qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result); // Display error dialog unless SMI SDK has already displayed an error message. if (result != SMI_ERROR_HMD_NOT_SUPPORTED) { - OffscreenUi::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); } } else { qCDebug(interfaceapp) << "Eye Tracker: Started streaming"; @@ -149,7 +149,7 @@ void EyeTracker::onStreamStarted() { result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); - OffscreenUi::warning(nullptr, "Eye Tracker Error", "Error loading calibration" + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", "Error loading calibration" + smiReturnValueToString(result)); } else { qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; @@ -165,7 +165,7 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { int result = smi_setCallback(eyeTrackerCallback); if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result); - OffscreenUi::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); } else { _isInitialized = true; } @@ -270,7 +270,7 @@ void EyeTracker::calibrate(int points) { } if (result != SMI_RET_SUCCESS) { - OffscreenUi::warning(nullptr, "Eye Tracker Error", "Calibration error: " + smiReturnValueToString(result)); + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", "Calibration error: " + smiReturnValueToString(result)); } } #endif diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 135729653e..75f13b0dbd 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -91,6 +91,12 @@ QObject* OffscreenUi::getFlags() { return offscreenFlags; } +void OffscreenUi::removeModalDialog(QObject* modal) { + if (modal) { + _modalDialogListeners.removeOne(modal); + } +} + void OffscreenUi::create(QOpenGLContext* context) { OffscreenQmlSurface::create(context); auto myContext = getSurfaceContext(); @@ -204,6 +210,9 @@ private slots: void onSelected(int button) { _result = button; _finished = true; + auto offscreenUi = DependencyManager::get(); + emit offscreenUi->response(static_cast(_result.toInt())); + offscreenUi->removeModalDialog(qobject_cast(this)); disconnect(_dialog); } }; @@ -263,6 +272,21 @@ QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& ti return static_cast(waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, defaultButton))); } +void OffscreenUi::asyncMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "asyncMessageBox", + Q_ARG(Icon, icon), + Q_ARG(QString, title), + Q_ARG(QString, text), + Q_ARG(QMessageBox::StandardButtons, buttons), + Q_ARG(QMessageBox::StandardButton, defaultButton)); + } + + MessageBoxListener* messageBoxListener = new MessageBoxListener(createMessageBox(icon, title, text, buttons, defaultButton)); + QObject* modalDialog = qobject_cast(messageBoxListener); + _modalDialogListeners.push_back(modalDialog); +} + QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton); @@ -275,11 +299,18 @@ QMessageBox::StandardButton OffscreenUi::question(const QString& title, const QS QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_QUESTION, title, text, buttons, defaultButton); } +void OffscreenUi::asyncQuestion(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_QUESTION, title, text, buttons, defaultButton); +} QMessageBox::StandardButton OffscreenUi::warning(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_WARNING, title, text, buttons, defaultButton); } - +void OffscreenUi::asyncWarning(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_WARNING, title, text, buttons, defaultButton); +} class InputDialogListener : public ModalDialogListener { diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index f228f28d5e..726b7897a0 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -71,6 +71,7 @@ public: // Message box compatibility Q_INVOKABLE QMessageBox::StandardButton messageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); + Q_INVOKABLE void asyncMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); // Must be called from the main thread QQuickItem* createMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); // Must be called from the main thread @@ -94,12 +95,23 @@ public: QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return question(title, text, buttons, defaultButton); } + + static void asyncQuestion(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncQuestion(title, text, buttons, defaultButton); + } /// Same design as QMessageBox::warning(), will block, returns result static QMessageBox::StandardButton warning(void* ignored, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return warning(title, text, buttons, defaultButton); } + static void asyncWarning(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncWarning(title, text, buttons, defaultButton); + } static QMessageBox::StandardButton critical(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, @@ -110,9 +122,15 @@ public: static QMessageBox::StandardButton question(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static void asyncQuestion (const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton warning(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static void asyncWarning(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); @@ -153,9 +171,11 @@ public: static QVariant getCustomInfo(const Icon icon, const QString& title, const QVariantMap& config, bool* ok = 0); unsigned int getMenuUserDataId() const; - signals: void showDesktop(); + void response(QMessageBox::StandardButton response); + public slots: + void removeModalDialog(QObject* modal); private: QString fileDialog(const QVariantMap& properties); @@ -163,6 +183,7 @@ private: QQuickItem* _desktop { nullptr }; QQuickItem* _toolWindow { nullptr }; + QList _modalDialogListeners; std::unordered_map _pressedKeys; VrMenu* _vrMenu { nullptr }; QQueue> _queuedMenuInitializers;