From 556569819dd59956e026c4358d949cfccb911805 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 9 Aug 2017 14:00:19 +0200 Subject: [PATCH 01/19] 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; From 616f15864a8059c9223189670dd6ae4f1f5bbac7 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 9 Aug 2017 14:49:15 +0200 Subject: [PATCH 02/19] Ported an some currently not used code --- interface/src/assets/ATPAssetMigrator.cpp | 138 ++++++++++++++-------- 1 file changed, 88 insertions(+), 50 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 63147c8798..b3711a2707 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -47,6 +47,37 @@ void ATPAssetMigrator::loadEntityServerFile() { if (!filename.isEmpty()) { qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename; + auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) { + 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; + qWarning() << "Count not create request for asset at" << migrationURL.toString(); + } + + }; static const QString MIGRATION_CONFIRMATION_TEXT { "The ATP Asset Migration process will scan the selected entity-server file,\nupload discovered resources to the"\ " current asset-server\nand then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\ @@ -54,7 +85,6 @@ void ATPAssetMigrator::loadEntityServerFile() { }; 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); @@ -88,60 +118,68 @@ void ATPAssetMigrator::loadEntityServerFile() { 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; - qWarning() << "Count not create request for asset at" << migrationURL.toString(); - } + && (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 { - _ignoredUrls.insert(migrationURL); + entityObject[COMPOUND_SHAPE_URL_KEY] = _uploadedAssets.value(migrationURL).toString(); + } + + jsonValue = entityObject; + } else { + + static bool hasAskedForCompleteMigration { false }; + static bool wantsCompleteMigration { false }; + + if (!hasAskedForCompleteMigration) { + // this is the first resource migration - ask the user if they just want to migrate everything + static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n"\ + "Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n"\ + "Select \"No\" to be prompted for each discovered asset." + }; + 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) { + wantsCompleteMigration = true; + migrateResources(migrationURL, jsonValue, isModelURL); + } else { + 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) { + migrateResources(migrationURL, jsonValue, isModelURL); + } else { + _ignoredUrls.insert(migrationURL); + } + }); + OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, + "Would you like to migrate the following resource?\n" + migrationURL.toString(), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + } + }); + OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + hasAskedForCompleteMigration = true; + } + if (wantsCompleteMigration) { + migrateResources(migrationURL, jsonValue, isModelURL); } } + } } } - } _doneReading = true; @@ -150,7 +188,7 @@ void ATPAssetMigrator::loadEntityServerFile() { } else { OffscreenUi::asyncWarning(_dialogParent, "Error", - "There was a problem loading that entity-server file for ATP asset migration. Please try again"); + "There was a problem loading that entity-server file for ATP asset migration. Please try again"); } } }); From 68e9d335cdf29b1c65e77514a7222d80e5a3e7e5 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 10 Aug 2017 10:45:11 +0200 Subject: [PATCH 03/19] Added async information and critical dialogs --- interface/src/assets/ATPAssetMigrator.cpp | 2 +- interface/src/ui/AddressBarDialog.cpp | 4 ++-- libraries/ui/src/OffscreenUi.cpp | 13 ++++++++++++- libraries/ui/src/OffscreenUi.h | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index b3711a2707..45459683e8 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -354,7 +354,7 @@ void ATPAssetMigrator::saveEntityServerFile() { infoMessage += "You can re-attempt migration on those models\nby restarting this process with the newly saved file."; } - OffscreenUi::information(_dialogParent, "Success", infoMessage); + OffscreenUi::asyncInformation(_dialogParent, "Success", infoMessage); } else { OffscreenUi::asyncWarning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); } diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 8aaaac1a57..d7b59ba912 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -73,11 +73,11 @@ void AddressBarDialog::loadForward() { } void AddressBarDialog::displayAddressOfflineMessage() { - OffscreenUi::critical("", "That user or place is currently offline"); + OffscreenUi::asyncCritical("", "That user or place is currently offline"); } void AddressBarDialog::displayAddressNotFoundMessage() { - OffscreenUi::critical("", "There is no address information for that user or place"); + OffscreenUi::asyncCritical("", "There is no address information for that user or place"); } void AddressBarDialog::observeShownChanged(bool visible) { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 75f13b0dbd..fd557d74c6 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -172,7 +172,7 @@ protected: virtual QVariant waitForResult() { while (!_finished) { - QCoreApplication::processEvents(); + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); } return _result; } @@ -295,6 +295,17 @@ QMessageBox::StandardButton OffscreenUi::information(const QString& title, const QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton); } + +void OffscreenUi::asyncCritical(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton); +} + +void OffscreenUi::asyncInformation(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton); +} + QMessageBox::StandardButton OffscreenUi::question(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_QUESTION, title, text, buttons, defaultButton); diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 726b7897a0..09f5d0b863 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -89,6 +89,16 @@ public: QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return information(title, text, buttons, defaultButton); } + static void asyncCritical(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncCritical(title, text, buttons, defaultButton); + } + static void asyncInformation(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncInformation(title, text, buttons, defaultButton); + } /// Same design as QMessageBox::question(), will block, returns result static QMessageBox::StandardButton question(void* ignored, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, @@ -119,6 +129,12 @@ public: static QMessageBox::StandardButton information(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static void asyncCritical(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static void asyncInformation(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton question(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); From b604b1bbac75f8833dbf8d82d12143839cbf6f55 Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 13 Aug 2017 11:02:29 +0200 Subject: [PATCH 04/19] Async file dialogs #1 --- libraries/ui/src/OffscreenUi.cpp | 47 ++++++++++++++++++++++++++++++++ libraries/ui/src/OffscreenUi.h | 5 +++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index fd557d74c6..4027c4c5b6 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -680,6 +680,31 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) { return result.toUrl().toLocalFile(); } +QQuickItem *OffscreenUi::fileDialogAsync(const QVariantMap& properties) { + QVariant buildDialogResult; + bool invokeResult; + auto tabletScriptingInterface = DependencyManager::get(); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + if (tablet->getToolbarMode()) { + invokeResult = QMetaObject::invokeMethod(_desktop, "fileDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + } else { + QQuickItem* tabletRoot = tablet->getTabletRoot(); + invokeResult = QMetaObject::invokeMethod(tabletRoot, "fileDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + emit tabletScriptingInterface->tabletNotification(); + } + + if (!invokeResult) { + qWarning() << "Failed to create file open dialog"; + return nullptr; + } + + return qvariant_cast(buildDialogResult); +} + QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; @@ -702,6 +727,28 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, return fileDialog(map); } +QString OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + QString result; + BLOCKING_INVOKE_METHOD(this, "fileOpenDialogAsync", + Q_RETURN_ARG(QString, result), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return result; + } + + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + return fileDialog(map); +} + QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 09f5d0b863..90902dc417 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -149,6 +149,7 @@ public: 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 fileOpenDialogAsync(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); Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); @@ -190,11 +191,13 @@ public: signals: void showDesktop(); void response(QMessageBox::StandardButton response); - public slots: + void fileDialogResponse(QString response); +public slots: void removeModalDialog(QObject* modal); private: QString fileDialog(const QVariantMap& properties); + QQuickItem* fileDialogAsync(const QVariantMap &properties); QString assetDialog(const QVariantMap& properties); QQuickItem* _desktop { nullptr }; From ac7aa609f292abc38773ad4451609add290a00e8 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 14 Aug 2017 16:23:53 +0200 Subject: [PATCH 05/19] Open file async implemented --- interface/src/Application.cpp | 15 +++++++++------ libraries/ui/src/OffscreenUi.cpp | 23 +++++++++++++++++------ libraries/ui/src/OffscreenUi.h | 8 +++++--- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 408e4e5bd1..966cead203 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6774,12 +6774,15 @@ void Application::openUrl(const QUrl& url) const { void Application::loadDialog() { auto scriptEngines = DependencyManager::get(); - QString fileNameString = OffscreenUi::getOpenFileName( - _glWidget, tr("Open Script"), getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); - if (!fileNameString.isEmpty() && QFile(fileNameString).exists()) { - setPreviousScriptLocation(QFileInfo(fileNameString).absolutePath()); - DependencyManager::get()->loadScript(fileNameString, true, false, false, true); // Don't load from cache - } + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, [=] (QString response) { + if (!response.isEmpty() && QFile(response).exists()) { + setPreviousScriptLocation(QFileInfo(response).absolutePath()); + DependencyManager::get()->loadScript(response, true, false, false, true); // Don't load from cache + } + }); + OffscreenUi::getOpenFileNameAsync(_glWidget, tr("Open Script"), getPreviousScriptLocation(), + tr("JavaScript Files (*.js)")); } QString Application::getPreviousScriptLocation() { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 4027c4c5b6..8ea92d2b1e 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -645,6 +645,9 @@ private slots: void onSelectedFile(QVariant file) { _result = file; _finished = true; + auto offscreenUi = DependencyManager::get(); + emit offscreenUi->fileDialogResponse(_result.toUrl().toLocalFile()); + offscreenUi->removeModalDialog(qobject_cast(this)); disconnect(_dialog); } }; @@ -680,7 +683,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) { return result.toUrl().toLocalFile(); } -QQuickItem *OffscreenUi::fileDialogAsync(const QVariantMap& properties) { +void OffscreenUi::fileDialogAsync(const QVariantMap& properties) { QVariant buildDialogResult; bool invokeResult; auto tabletScriptingInterface = DependencyManager::get(); @@ -699,10 +702,14 @@ QQuickItem *OffscreenUi::fileDialogAsync(const QVariantMap& properties) { if (!invokeResult) { qWarning() << "Failed to create file open dialog"; - return nullptr; + return; } - return qvariant_cast(buildDialogResult); + FileDialogListener* fileDialogListener = new FileDialogListener(qvariant_cast(buildDialogResult)); + QObject* fileModalDialog = qobject_cast(fileDialogListener); + _modalDialogListeners.push_back(fileModalDialog); + + return; } QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { @@ -727,7 +734,7 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, return fileDialog(map); } -QString OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { +void OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; BLOCKING_INVOKE_METHOD(this, "fileOpenDialogAsync", @@ -737,7 +744,7 @@ QString OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& Q_ARG(QString, filter), Q_ARG(QString*, selectedFilter), Q_ARG(QFileDialog::Options, options)); - return result; + return; } // FIXME support returning the selected filter... somehow? @@ -746,7 +753,7 @@ QString OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& map.insert("dir", QUrl::fromLocalFile(dir)); map.insert("filter", filter); map.insert("options", static_cast(options)); - return fileDialog(map); + fileDialogAsync(map); } QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { @@ -799,6 +806,10 @@ QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, cons return DependencyManager::get()->fileOpenDialog(caption, dir, filter, selectedFilter, options); } +void OffscreenUi::getOpenFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + return DependencyManager::get()->fileOpenDialogAsync(caption, dir, filter, selectedFilter, options); +} + QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { return DependencyManager::get()->fileSaveDialog(caption, dir, filter, selectedFilter, options); } diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 90902dc417..335645ce06 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -149,14 +149,16 @@ public: 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 fileOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE void fileOpenDialogAsync(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); Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getOpenFileName - static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static void getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + // Compatibility with QFileDialog::getSaveFileName static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getExistingDirectory @@ -197,7 +199,7 @@ public slots: private: QString fileDialog(const QVariantMap& properties); - QQuickItem* fileDialogAsync(const QVariantMap &properties); + void fileDialogAsync(const QVariantMap &properties); QString assetDialog(const QVariantMap& properties); QQuickItem* _desktop { nullptr }; From 757af1e2e51903c41cb8d65c58a80a6fd2021fbb Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 15 Aug 2017 14:44:01 +0200 Subject: [PATCH 06/19] Added Save, Directory and Assets async methods --- interface/src/Application.cpp | 2 + interface/src/assets/ATPAssetMigrator.cpp | 266 +++++++++++----------- libraries/ui/src/OffscreenUi.cpp | 110 ++++++++- libraries/ui/src/OffscreenUi.h | 8 + 4 files changed, 254 insertions(+), 132 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 966cead203..130ef49dd0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6776,6 +6776,8 @@ void Application::loadDialog() { auto scriptEngines = DependencyManager::get(); auto offscreenUi = DependencyManager::get(); connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, [=] (QString response) { + auto offscreenUi = DependencyManager::get(); + disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, nullptr); if (!response.isEmpty() && QFile(response).exists()) { setPreviousScriptLocation(QFileInfo(response).absolutePath()); DependencyManager::get()->loadScript(response, true, false, false, true); // Don't load from cache diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 45459683e8..a86c012a55 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -42,159 +42,165 @@ static const QString COMPOUND_SHAPE_URL_KEY = "compoundShapeURL"; static const QString MESSAGE_BOX_TITLE = "ATP Asset Migration"; void ATPAssetMigrator::loadEntityServerFile() { - auto filename = OffscreenUi::getOpenFileName(_dialogParent, tr("Select an entity-server content file to migrate"), QString(), tr("Entity-Server Content (*.gz)")); - - if (!filename.isEmpty()) { - qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename; - - auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) { - 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; - qWarning() << "Count not create request for asset at" << migrationURL.toString(); - } - - }; - static const QString MIGRATION_CONFIRMATION_TEXT { - "The ATP Asset Migration process will scan the selected entity-server file,\nupload discovered resources to the"\ - " current asset-server\nand then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\ - " continue?\n\nMake sure you are connected to the right domain." - }; - + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, [=] (QString filename) { auto offscreenUi = DependencyManager::get(); - QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=] (QMessageBox::StandardButton answer) { + disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, nullptr); + if (!filename.isEmpty()) { + qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename; + + auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) { + 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; + qWarning() << "Count not create request for asset at" << migrationURL.toString(); + } + + }; + static const QString MIGRATION_CONFIRMATION_TEXT { + "The ATP Asset Migration process will scan the selected entity-server file,\nupload discovered resources to the"\ + " current asset-server\nand then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\ + " continue?\n\nMake sure you are connected to the right domain." + }; + auto offscreenUi = DependencyManager::get(); - QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); + QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=] (QMessageBox::StandardButton answer) { + auto offscreenUi = DependencyManager::get(); + QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); - if (QMessageBox::Yes == answer) { - // try to open the file at the given filename - QFile modelsFile { filename }; + if (QMessageBox::Yes == answer) { + // try to open the file at the given filename + QFile modelsFile { filename }; - if (modelsFile.open(QIODevice::ReadOnly)) { - QByteArray compressedJsonData = modelsFile.readAll(); - QByteArray jsonData; + if (modelsFile.open(QIODevice::ReadOnly)) { + QByteArray compressedJsonData = modelsFile.readAll(); + QByteArray jsonData; - if (!gunzip(compressedJsonData, jsonData)) { - OffscreenUi::asyncWarning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format."); - } + if (!gunzip(compressedJsonData, jsonData)) { + OffscreenUi::asyncWarning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format."); + } - QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData); - _entitiesArray = modelsJSON.object()["Entities"].toArray(); + 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(); + 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(); - for (int i = 0; i < 2; ++i) { - bool isModelURL = (i == 0); - quint8 replacementType = i; - auto migrationURLString = (isModelURL) ? modelURLString : compoundURLString; + for (int i = 0; i < 2; ++i) { + bool isModelURL = (i == 0); + quint8 replacementType = i; + auto migrationURLString = (isModelURL) ? modelURLString : compoundURLString; - if (!migrationURLString.isEmpty()) { - QUrl migrationURL = QUrl(migrationURLString); + 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 (!_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(); + 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 { - entityObject[COMPOUND_SHAPE_URL_KEY] = _uploadedAssets.value(migrationURL).toString(); - } - jsonValue = entityObject; - } else { + static bool hasAskedForCompleteMigration { false }; + static bool wantsCompleteMigration { false }; - static bool hasAskedForCompleteMigration { false }; - static bool wantsCompleteMigration { false }; - - if (!hasAskedForCompleteMigration) { - // this is the first resource migration - ask the user if they just want to migrate everything - static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n"\ - "Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n"\ - "Select \"No\" to be prompted for each discovered asset." - }; - auto offscreenUi = DependencyManager::get(); - QObject::connect(offscreenUi.data(), &OffscreenUi::response, this, [=] (QMessageBox::StandardButton answer) { + if (!hasAskedForCompleteMigration) { + // this is the first resource migration - ask the user if they just want to migrate everything + static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n"\ + "Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n"\ + "Select \"No\" to be prompted for each discovered asset." + }; auto offscreenUi = DependencyManager::get(); - QObject::disconnect(offscreenUi.data(), &OffscreenUi::response, this, nullptr); - if (answer == QMessageBox::Yes) { - wantsCompleteMigration = true; - migrateResources(migrationURL, jsonValue, isModelURL); - } else { - 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) { - migrateResources(migrationURL, jsonValue, isModelURL); - } else { - _ignoredUrls.insert(migrationURL); - } - }); - OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, - "Would you like to migrate the following resource?\n" + migrationURL.toString(), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + 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) { + wantsCompleteMigration = true; + migrateResources(migrationURL, jsonValue, isModelURL); + } else { + 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) { + migrateResources(migrationURL, jsonValue, isModelURL); + } else { + _ignoredUrls.insert(migrationURL); + } + }); + OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, + "Would you like to migrate the following resource?\n" + migrationURL.toString(), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - } - }); - OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - hasAskedForCompleteMigration = true; - } - if (wantsCompleteMigration) { - migrateResources(migrationURL, jsonValue, isModelURL); + } + }); + OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + hasAskedForCompleteMigration = true; + } + if (wantsCompleteMigration) { + migrateResources(migrationURL, jsonValue, isModelURL); + } } } } } } + + _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::asyncWarning(_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); - } + }); + OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + } + }); + OffscreenUi::getOpenFileNameAsync(_dialogParent, tr("Select an entity-server content file to migrate"), + QString(), tr("Entity-Server Content (*.gz)")); } void ATPAssetMigrator::migrateResource(ResourceRequest* request) { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 8ea92d2b1e..470603d0d0 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -736,9 +736,7 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, void OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { - QString result; BLOCKING_INVOKE_METHOD(this, "fileOpenDialogAsync", - Q_RETURN_ARG(QString, result), Q_ARG(QString, caption), Q_ARG(QString, dir), Q_ARG(QString, filter), @@ -780,6 +778,28 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, return fileDialog(map); } +void OffscreenUi::fileSaveDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "fileSaveDialogAsync", + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return; + } + + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + map.insert("saveDialog", true); + + return fileDialogAsync(map); +} + QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; @@ -802,6 +822,26 @@ QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QStri return fileDialog(map); } +void OffscreenUi::existingDirectoryDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialogAsync", + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return; + } + + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + map.insert("selectDirectory", true); + return fileDialogAsync(map); +} + QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { return DependencyManager::get()->fileOpenDialog(caption, dir, filter, selectedFilter, options); } @@ -814,10 +854,18 @@ QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, cons return DependencyManager::get()->fileSaveDialog(caption, dir, filter, selectedFilter, options); } +void OffscreenUi::getSaveFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + return DependencyManager::get()->fileSaveDialogAsync(caption, dir, filter, selectedFilter, options); +} + QString OffscreenUi::getExistingDirectory(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { return DependencyManager::get()->existingDirectoryDialog(caption, dir, filter, selectedFilter, options); } +void OffscreenUi::getExistingDirectoryAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + return DependencyManager::get()->existingDirectoryDialogAsync(caption, dir, filter, selectedFilter, options); +} + class AssetDialogListener : public ModalDialogListener { // ATP equivalent of FileDialogListener. Q_OBJECT @@ -833,6 +881,9 @@ class AssetDialogListener : public ModalDialogListener { private slots: void onSelectedAsset(QVariant asset) { _result = asset; + auto offscreenUi = DependencyManager::get(); + emit offscreenUi->assetDialogResponse(_result.toUrl().toLocalFile()); + offscreenUi->removeModalDialog(qobject_cast(this)); _finished = true; disconnect(_dialog); } @@ -870,6 +921,35 @@ QString OffscreenUi::assetDialog(const QVariantMap& properties) { return result.toUrl().toString(); } +void OffscreenUi::assetDialogAsync(const QVariantMap& properties) { + // ATP equivalent of fileDialog(). + QVariant buildDialogResult; + bool invokeResult; + auto tabletScriptingInterface = DependencyManager::get(); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + if (tablet->getToolbarMode()) { + invokeResult = QMetaObject::invokeMethod(_desktop, "assetDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + } else { + QQuickItem* tabletRoot = tablet->getTabletRoot(); + invokeResult = QMetaObject::invokeMethod(tabletRoot, "assetDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + emit tabletScriptingInterface->tabletNotification(); + } + + if (!invokeResult) { + qWarning() << "Failed to create asset open dialog"; + return; + } + + AssetDialogListener* assetDialogListener = new AssetDialogListener(qvariant_cast(buildDialogResult)); + QObject* assetModalDialog = qobject_cast(assetDialogListener); + _modalDialogListeners.push_back(assetModalDialog); + return; +} + QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { // ATP equivalent of fileOpenDialog(). if (QThread::currentThread() != thread()) { @@ -893,11 +973,37 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir, return assetDialog(map); } +void OffscreenUi::assetOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + // ATP equivalent of fileOpenDialog(). + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "assetOpenDialogAsync", + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return; + } + + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", dir); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + return assetDialogAsync(map); +} + QString OffscreenUi::getOpenAssetName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { // ATP equivalent of getOpenFileName(). return DependencyManager::get()->assetOpenDialog(caption, dir, filter, selectedFilter, options); } +void OffscreenUi::getOpenAssetNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + // ATP equivalent of getOpenFileName(). + return DependencyManager::get()->assetOpenDialogAsync(caption, dir, filter, selectedFilter, options); +} + bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { if (!filterEnabled(originalDestination, event)) { return false; diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 335645ce06..a3529da89c 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -151,9 +151,12 @@ public: 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 void fileOpenDialogAsync(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); + Q_INVOKABLE void fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE void existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE void assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getOpenFileName static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); @@ -161,10 +164,13 @@ public: // Compatibility with QFileDialog::getSaveFileName static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static void getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getExistingDirectory static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static void getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); static QString getOpenAssetName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static void getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QVariant inputDialog(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); Q_INVOKABLE QVariant customInputDialog(const Icon icon, const QString& title, const QVariantMap& config); @@ -194,6 +200,7 @@ signals: void showDesktop(); void response(QMessageBox::StandardButton response); void fileDialogResponse(QString response); + void assetDialogResponse(QString response); public slots: void removeModalDialog(QObject* modal); @@ -201,6 +208,7 @@ private: QString fileDialog(const QVariantMap& properties); void fileDialogAsync(const QVariantMap &properties); QString assetDialog(const QVariantMap& properties); + void assetDialogAsync(const QVariantMap& properties); QQuickItem* _desktop { nullptr }; QQuickItem* _toolWindow { nullptr }; From e9ed05c3baf2296fbd0389f9f199867081aecb78 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 15 Aug 2017 15:55:36 +0200 Subject: [PATCH 07/19] Reimplement snapshot dir browser for async --- .../scripting/WindowScriptingInterface.cpp | 21 ++++++++++++------ .../src/scripting/WindowScriptingInterface.h | 4 ++-- scripts/system/snapshot.js | 22 +++++++++++-------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 36381b3626..2e6867c30b 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -174,8 +174,7 @@ void WindowScriptingInterface::ensureReticleVisible() const { /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QString& directory) { +void WindowScriptingInterface::browseDir(const QString& title, const QString& directory) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -184,11 +183,19 @@ QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QSt #ifndef Q_OS_WIN path = fixupPathForMac(directory); #endif - QString result = OffscreenUi::getExistingDirectory(nullptr, title, path); - if (!result.isEmpty()) { - setPreviousBrowseLocation(QFileInfo(result).absolutePath()); - } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, + this, [=] (QString result) { + auto offscreenUi = DependencyManager::get(); + disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, + this, nullptr); + if (!result.isEmpty()) { + setPreviousBrowseLocation(QFileInfo(result).absolutePath()); + } + emit browseDirChanged(result); + }); + + OffscreenUi::getExistingDirectoryAsync(nullptr, title, path); } /// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index f8ed20f42f..3de5ec43a3 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -53,7 +53,7 @@ public slots: QScriptValue confirm(const QString& message = ""); QScriptValue prompt(const QString& message = "", const QString& defaultText = ""); CustomPromptResult customPrompt(const QVariant& config); - QScriptValue browseDir(const QString& title = "", const QString& directory = ""); + void browseDir(const QString& title = "", const QString& directory = ""); QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); @@ -87,7 +87,7 @@ signals: void announcement(const QString& message); void messageBoxClosed(int id, int button); - + void browseDirChanged(QString browseDir); // triggered when window size or position changes void geometryChanged(QRect geometry); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 37618253ee..08614c2030 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -120,15 +120,8 @@ function onMessage(message) { openLoginWindow(); break; case 'chooseSnapshotLocation': - var snapshotPath = Window.browseDir("Choose Snapshots Directory", "", ""); - - if (snapshotPath) { // not cancelled - Snapshot.setSnapshotsLocation(snapshotPath); - tablet.emitScriptEvent(JSON.stringify({ - type: "snapshot", - action: "snapshotLocationChosen" - })); - } + Window.browseDirChanged.connect(snapshotDirChanged); + Window.browseDir("Choose Snapshots Directory", "", ""); break; case 'openSettings': if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false)) @@ -579,6 +572,17 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { }); } +function snapshotDirChanged(snapshotPath) { + Window.browseDirChanged.disconnect(snapshotDirChanged); + if (snapshotPath !== "") { // not cancelled + Snapshot.setSnapshotsLocation(snapshotPath); + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "snapshotLocationChosen" + })); + } +} + function processingGifStarted(pathStillSnapshot) { Window.processingGifStarted.disconnect(processingGifStarted); Window.processingGifCompleted.connect(processingGifCompleted); From 51a5c3035390672374c906cd23e89ef259b90b6d Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 15 Aug 2017 17:05:55 +0200 Subject: [PATCH 08/19] Reimplement asset browsing for async --- .../scripting/WindowScriptingInterface.cpp | 22 +++++++++++++------ .../src/scripting/WindowScriptingInterface.h | 3 ++- .../marketplace/record/record.js | 17 +++++++++----- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 2e6867c30b..ad33c5177c 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -247,8 +247,7 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString& /// \param const QString& title title of the window /// \param const QString& directory directory to start the asset browser at /// \param const QString& nameFilter filter to filter asset names by - see `QFileDialog` -/// \return QScriptValue asset path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) { +void WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -260,11 +259,20 @@ QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const if (path.right(1) != "/") { path = path + "/"; } - QString result = OffscreenUi::getOpenAssetName(nullptr, title, path, nameFilter); - if (!result.isEmpty()) { - setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); - } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::assetDialogResponse, + this, [=] (QString result) { + auto offscreenUi = DependencyManager::get(); + disconnect(offscreenUi.data(), &OffscreenUi::assetDialogResponse, + this, nullptr); + if (!result.isEmpty()) { + setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); + } + emit assetsDirChanged(result); + }); + + OffscreenUi::getOpenAssetNameAsync(nullptr, title, path, nameFilter); } void WindowScriptingInterface::showAssetServer(const QString& upload) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 3de5ec43a3..1338743c20 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -56,7 +56,7 @@ public slots: void browseDir(const QString& title = "", const QString& directory = ""); QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); - QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + void browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void showAssetServer(const QString& upload = ""); void copyToClipboard(const QString& text); void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f); @@ -88,6 +88,7 @@ signals: void messageBoxClosed(int id, int button); void browseDirChanged(QString browseDir); + void assetsDirChanged(QString assetsDir); // triggered when window size or position changes void geometryChanged(QRect geometry); diff --git a/unpublishedScripts/marketplace/record/record.js b/unpublishedScripts/marketplace/record/record.js index 5439d68c9a..84f631f307 100644 --- a/unpublishedScripts/marketplace/record/record.js +++ b/unpublishedScripts/marketplace/record/record.js @@ -486,6 +486,15 @@ return isFinishOnOpen; } + function onAssetsDirChanged(recording) { + Window.assetsDirChanged.disconnect(onAssetsDirChanged); + if (recording !== "") { + log("Load recording " + recording); + UserActivityLogger.logAction("record_load_recording", logDetails()); + Player.playRecording("atp:" + recording, MyAvatar.position, MyAvatar.orientation); + } + } + function onWebEventReceived(data) { var message, recording; @@ -520,12 +529,8 @@ break; case LOAD_RECORDING_ACTION: // User wants to select an ATP recording to play. - recording = Window.browseAssets("Select Recording to Play", "recordings", "*.hfr"); - if (recording) { - log("Load recording " + recording); - UserActivityLogger.logAction("record_load_recording", logDetails()); - Player.playRecording("atp:" + recording, MyAvatar.position, MyAvatar.orientation); - } + Window.assetsDirChanged.connect(onAssetsDirChanged); + Window.browseAssets("Select Recording to Play", "recordings", "*.hfr"); break; case START_RECORDING_ACTION: // Start making a recording. From 8afdb27c1b39cacc03d3ce872364122542757ed5 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 15 Aug 2017 22:55:44 +0200 Subject: [PATCH 09/19] Reworked WindowScriptingInterface save method for async methods --- interface/src/assets/ATPAssetMigrator.cpp | 61 ++++++++++--------- .../scripting/WindowScriptingInterface.cpp | 21 ++++--- .../src/scripting/WindowScriptingInterface.h | 4 +- scripts/system/edit.js | 19 +++--- scripts/system/libraries/entityList.js | 19 +++--- 5 files changed, 73 insertions(+), 51 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index a86c012a55..4f79d32734 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -296,9 +296,6 @@ void ATPAssetMigrator::checkIfFinished() { // are we out of pending replacements? if so it is time to save the entity-server file if (_doneReading && _pendingReplacements.empty()) { saveEntityServerFile(); - - // reset after the attempted save, success or fail - reset(); } } @@ -336,39 +333,45 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { void ATPAssetMigrator::saveEntityServerFile() { // show a dialog to ask the user where they want to save the file - QString saveName = OffscreenUi::getSaveFileName(_dialogParent, "Save Migrated Entities File"); - - QFile saveFile { saveName }; - - if (saveFile.open(QIODevice::WriteOnly)) { - QJsonObject rootObject; - rootObject[ENTITIES_OBJECT_KEY] = _entitiesArray; - - QJsonDocument newDocument { rootObject }; - QByteArray jsonDataForFile; - - if (gzip(newDocument.toJson(), jsonDataForFile, -1)) { - - saveFile.write(jsonDataForFile); - saveFile.close(); + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, [=] (QString saveName) { + QFile saveFile { saveName }; - QString infoMessage = QString("Your new entities file has been saved at\n%1.").arg(saveName); + if (saveFile.open(QIODevice::WriteOnly)) { + QJsonObject rootObject; + rootObject[ENTITIES_OBJECT_KEY] = _entitiesArray; - if (_errorCount > 0) { - infoMessage += QString("\nThere were %1 models that could not be migrated.\n").arg(_errorCount); - infoMessage += "Check the warnings in your log for details.\n"; - infoMessage += "You can re-attempt migration on those models\nby restarting this process with the newly saved file."; + QJsonDocument newDocument { rootObject }; + QByteArray jsonDataForFile; + + if (gzip(newDocument.toJson(), jsonDataForFile, -1)) { + + saveFile.write(jsonDataForFile); + saveFile.close(); + + QString infoMessage = QString("Your new entities file has been saved at\n%1.").arg(saveName); + + if (_errorCount > 0) { + infoMessage += QString("\nThere were %1 models that could not be migrated.\n").arg(_errorCount); + infoMessage += "Check the warnings in your log for details.\n"; + infoMessage += "You can re-attempt migration on those models\nby restarting this process with the newly saved file."; + } + + OffscreenUi::asyncInformation(_dialogParent, "Success", infoMessage); + } else { + OffscreenUi::asyncWarning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); } - OffscreenUi::asyncInformation(_dialogParent, "Success", infoMessage); } else { - OffscreenUi::asyncWarning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); + OffscreenUi::asyncWarning(_dialogParent, "Error", + QString("Could not open file at %1 to write new entities file to.").arg(saveName)); } + // reset after the attempted save, success or fail + reset(); + }); + + OffscreenUi::getSaveFileNameAsync(_dialogParent, "Save Migrated Entities File"); - } else { - OffscreenUi::asyncWarning(_dialogParent, "Error", - QString("Could not open file at %1 to write new entities file to.").arg(saveName)); - } } void ATPAssetMigrator::reset() { diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index ad33c5177c..dc8031b98b 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -225,8 +225,7 @@ QScriptValue WindowScriptingInterface::browse(const QString& title, const QStrin /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { +void WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -235,11 +234,19 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString& #ifndef Q_OS_WIN path = fixupPathForMac(directory); #endif - QString result = OffscreenUi::getSaveFileName(nullptr, title, path, nameFilter); - if (!result.isEmpty()) { - setPreviousBrowseLocation(QFileInfo(result).absolutePath()); - } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, + this, [=] (QString result) { + auto offscreenUi = DependencyManager::get(); + disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, + this, nullptr); + if (!result.isEmpty()) { + setPreviousBrowseLocation(QFileInfo(result).absolutePath()); + } + emit saveFileChanged(result); + }); + + OffscreenUi::getSaveFileNameAsync(nullptr, title, path, nameFilter); } /// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 1338743c20..6b7f480d67 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -55,7 +55,7 @@ public slots: CustomPromptResult customPrompt(const QVariant& config); void browseDir(const QString& title = "", const QString& directory = ""); QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); - QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + void save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void showAssetServer(const QString& upload = ""); void copyToClipboard(const QString& text); @@ -89,6 +89,8 @@ signals: void messageBoxClosed(int id, int button); void browseDirChanged(QString browseDir); void assetsDirChanged(QString assetsDir); + void saveFileChanged(QString saveFile); + // triggered when window size or position changes void geometryChanged(QRect geometry); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index c141c7cd52..74a74ddb7f 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1458,6 +1458,16 @@ function toggleSelectedEntitiesVisible() { } } +function onFileSaveChanged(filename) { + Window.saveFileChanged.disconnect(onFileSaveChanged); + if (filename !== "") { + var success = Clipboard.exportEntities(filename, selectionManager.selections); + if (!success) { + Window.notifyEditError("Export failed."); + } + } +} + function handeMenuEvent(menuItem) { if (menuItem === "Allow Selecting of Small Models") { allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); @@ -1475,13 +1485,8 @@ function handeMenuEvent(menuItem) { if (!selectionManager.hasSelection()) { Window.notifyEditError("No entities have been selected."); } else { - var filename = Window.save("Select Where to Save", "", "*.json"); - if (filename) { - var success = Clipboard.exportEntities(filename, selectionManager.selections); - if (!success) { - Window.notifyEditError("Export failed."); - } - } + Window.saveFileChanged.connect(onFileSaveChanged); + Window.save("Select Where to Save", "", "*.json"); } } else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") { var importURL = null; diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 64a05fcebf..d2d815f9e3 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -108,6 +108,16 @@ EntityListTool = function(opts) { webView.emitScriptEvent(JSON.stringify(data)); }; + function onFileSaveChanged(filename) { + Window.saveFileChanged.disconnect(onFileSaveChanged); + if (filename !== "") { + var success = Clipboard.exportEntities(filename, selectionManager.selections); + if (!success) { + Window.notifyEditError("Export failed."); + } + } + } + webView.webEventReceived.connect(function(data) { try { data = JSON.parse(data); @@ -139,13 +149,8 @@ EntityListTool = function(opts) { if (!selectionManager.hasSelection()) { Window.notifyEditError("No entities have been selected."); } else { - var filename = Window.save("Select Where to Save", "", "*.json"); - if (filename) { - var success = Clipboard.exportEntities(filename, selectionManager.selections); - if (!success) { - Window.notifyEditError("Export failed."); - } - } + Window.saveFileChanged.connect(onFileSaveChanged); + Window.save("Select Where to Save", "", "*.json"); } } else if (data.type == "pal") { var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates. From 273261ee5705314b7b6f7ae31d201225abf7f99e Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 15 Aug 2017 23:12:35 +0200 Subject: [PATCH 10/19] Reworked WindowScriptingInterface browse method for async methods --- .../scripting/WindowScriptingInterface.cpp | 21 +++++--- .../src/scripting/WindowScriptingInterface.h | 5 +- scripts/system/edit.js | 48 ++++++++++--------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index dc8031b98b..512d79adb2 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -203,8 +203,7 @@ void WindowScriptingInterface::browseDir(const QString& title, const QString& di /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { +void WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -213,11 +212,19 @@ QScriptValue WindowScriptingInterface::browse(const QString& title, const QStrin #ifndef Q_OS_WIN path = fixupPathForMac(directory); #endif - QString result = OffscreenUi::getOpenFileName(nullptr, title, path, nameFilter); - if (!result.isEmpty()) { - setPreviousBrowseLocation(QFileInfo(result).absolutePath()); - } - return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, + this, [=] (QString result) { + auto offscreenUi = DependencyManager::get(); + disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, + this, nullptr); + if (!result.isEmpty()) { + setPreviousBrowseLocation(QFileInfo(result).absolutePath()); + } + emit openFileChanged(result); + }); + + OffscreenUi::getOpenFileNameAsync(nullptr, title, path, nameFilter); } /// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 6b7f480d67..8ae315d05c 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -54,7 +54,7 @@ public slots: QScriptValue prompt(const QString& message = "", const QString& defaultText = ""); CustomPromptResult customPrompt(const QVariant& config); void browseDir(const QString& title = "", const QString& directory = ""); - QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + void browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void showAssetServer(const QString& upload = ""); @@ -89,7 +89,8 @@ signals: void messageBoxClosed(int id, int button); void browseDirChanged(QString browseDir); void assetsDirChanged(QString assetsDir); - void saveFileChanged(QString saveFile); + void saveFileChanged(QString filename); + void openFileChanged(QString filename); // triggered when window size or position changes void geometryChanged(QRect geometry); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 74a74ddb7f..8d57c37bf6 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -431,17 +431,8 @@ var toolBar = (function () { }); addButton("importEntitiesButton", "assets-01.svg", function() { - var importURL = null; - var fullPath = Window.browse("Select Model to Import", "", "*.json"); - if (fullPath) { - importURL = "file:///" + fullPath; - } - if (importURL) { - if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { - toolBar.toggle(); - } - importSVO(importURL); - } + Window.openFileChanged.connect(onFileOpenChanged); + Window.browse("Select Model to Import", "", "*.json"); }); addButton("openAssetBrowserButton", "assets-01.svg", function() { @@ -1468,6 +1459,20 @@ function onFileSaveChanged(filename) { } } +function onFileOpenChanged(filename) { + Window.openFileChanged.disconnect(onFileOpenChanged); + var importURL = null; + if (filename !== "") { + importURL = "file:///" + filename; + } + if (importURL) { + if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { + toolBar.toggle(); + } + importSVO(importURL); + } +} + function handeMenuEvent(menuItem) { if (menuItem === "Allow Selecting of Small Models") { allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); @@ -1489,21 +1494,18 @@ function handeMenuEvent(menuItem) { Window.save("Select Where to Save", "", "*.json"); } } else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") { - var importURL = null; if (menuItem === "Import Entities") { - var fullPath = Window.browse("Select Model to Import", "", "*.json"); - if (fullPath) { - importURL = "file:///" + fullPath; - } + Window.openFileChanged.connect(onFileOpenChanged); + Window.browse("Select Model to Import", "", "*.json"); } else { - importURL = Window.prompt("URL of SVO to import", ""); - } - - if (importURL) { - if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { - toolBar.toggle(); + var importURL = Window.prompt("URL of SVO to import", ""); + if (importURL) { + if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { + toolBar.toggle(); + } + importSVO(importURL); } - importSVO(importURL); + } } else if (menuItem === "Entity List...") { entityListTool.toggleVisible(); From a87a93d991c2667dd6613a525c97b7c094d84de9 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 16 Aug 2017 15:35:51 +0200 Subject: [PATCH 11/19] Implemented input dialog async functions --- libraries/ui/src/OffscreenUi.cpp | 76 +++++++++++++++++++++++++++++++- libraries/ui/src/OffscreenUi.h | 21 +++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 470603d0d0..3a5edb6844 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -328,16 +328,29 @@ class InputDialogListener : public ModalDialogListener { Q_OBJECT friend class OffscreenUi; - InputDialogListener(QQuickItem* queryBox) : ModalDialogListener(queryBox) { + InputDialogListener(QQuickItem* queryBox, bool custom = false) : ModalDialogListener(queryBox), _custom(custom) { if (_finished) { return; } connect(_dialog, SIGNAL(selected(QVariant)), this, SLOT(onSelected(const QVariant&))); } +private: + bool _custom { false }; private slots: void onSelected(const QVariant& result) { - _result = result; + if (_custom) { + if (result.isValid()) { + // We get a JSON encoded result, so we unpack it into a QVariant wrapping a QVariantMap + _result = QVariant(QJsonDocument::fromJson(result.toString().toUtf8()).object().toVariantMap()); + } + } else { + _result = result; + } + + auto offscreenUi = DependencyManager::get(); + emit offscreenUi->inputDialogResponse(_result); + offscreenUi->removeModalDialog(qobject_cast(this)); _finished = true; disconnect(_dialog); } @@ -391,6 +404,31 @@ QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const return result; } +void OffscreenUi::getTextAsync(const Icon icon, const QString& title, const QString& label, const QString& text) { + DependencyManager::get()->inputDialogAsync(icon, title, label, text); +} + +void OffscreenUi::getItemAsync(const Icon icon, const QString& title, const QString& label, const QStringList& items, + int current, bool editable) { + + auto offscreenUi = DependencyManager::get(); + auto inputDialog = offscreenUi->createInputDialog(icon, title, label, current); + if (!inputDialog) { + return; + } + inputDialog->setProperty("items", items); + inputDialog->setProperty("editable", editable); + + InputDialogListener* inputDialogListener = new InputDialogListener(inputDialog); + offscreenUi->getModalDialogListeners().push_back(qobject_cast(inputDialogListener)); + + return; +} + +void OffscreenUi::getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config) { + DependencyManager::get()->customInputDialog(icon, title, config); +} + QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) { if (QThread::currentThread() != thread()) { QVariant result; @@ -406,6 +444,21 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q return waitForInputDialogResult(createInputDialog(icon, title, label, current)); } +void OffscreenUi::inputDialogAsync(const Icon icon, const QString& title, const QString& label, const QVariant& current) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "inputDialogAsync", + Q_ARG(Icon, icon), + Q_ARG(QString, title), + Q_ARG(QString, label), + Q_ARG(QVariant, current)); + return; + } + + InputDialogListener* inputDialogListener = new InputDialogListener(createInputDialog(icon, title, label, current)); + QObject* inputDialog = qobject_cast(inputDialogListener); + _modalDialogListeners.push_back(inputDialog); +} + QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, const QVariantMap& config) { if (QThread::currentThread() != thread()) { QVariant result; @@ -426,6 +479,21 @@ QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, c return result; } +void OffscreenUi::customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "customInputDialogAsync", + Q_ARG(Icon, icon), + Q_ARG(QString, title), + Q_ARG(QVariantMap, config)); + return; + } + + InputDialogListener* inputDialogListener = new InputDialogListener(createCustomInputDialog(icon, title, config), true); + QObject* inputDialog = qobject_cast(inputDialogListener); + _modalDialogListeners.push_back(inputDialog); + return; +} + void OffscreenUi::togglePinned() { bool invokeResult = QMetaObject::invokeMethod(_desktop, "togglePinned"); if (!invokeResult) { @@ -950,6 +1018,10 @@ void OffscreenUi::assetDialogAsync(const QVariantMap& properties) { return; } +QList &OffscreenUi::getModalDialogListeners() { + return _modalDialogListeners; +} + QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { // ATP equivalent of fileOpenDialog(). if (QThread::currentThread() != thread()) { diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index a3529da89c..716e598291 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -173,7 +173,9 @@ public: static void getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QVariant inputDialog(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); + Q_INVOKABLE void inputDialogAsync(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); Q_INVOKABLE QVariant customInputDialog(const Icon icon, const QString& title, const QVariantMap& config); + Q_INVOKABLE void customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config); QQuickItem* createInputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current); QQuickItem* createCustomInputDialog(const Icon icon, const QString& title, const QVariantMap& config); QVariant waitForInputDialogResult(QQuickItem* inputDialog); @@ -191,16 +193,35 @@ public: return getItem(OffscreenUi::ICON_NONE, title, label, items, current, editable, ok); } + // Compatibility with QInputDialog::getText + static void getTextAsync(void* ignored, const QString & title, const QString & label, + QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0, + Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) { + return getTextAsync(OffscreenUi::ICON_NONE, title, label, text); + } + // Compatibility with QInputDialog::getItem + static void getItemAsync(void *ignored, const QString & title, const QString & label, const QStringList & items, + int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, + Qt::InputMethodHints inputMethodHints = Qt::ImhNone) { + return getItemAsync(OffscreenUi::ICON_NONE, title, label, items, current, editable); + } + static QString getText(const Icon icon, const QString & title, const QString & label, const QString & text = QString(), bool * ok = 0); static QString getItem(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0); static QVariant getCustomInfo(const Icon icon, const QString& title, const QVariantMap& config, bool* ok = 0); + static void getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString()); + static void getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true); + static void getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config); unsigned int getMenuUserDataId() const; + QList &getModalDialogListeners(); + signals: void showDesktop(); void response(QMessageBox::StandardButton response); void fileDialogResponse(QString response); void assetDialogResponse(QString response); + void inputDialogResponse(QVariant response); public slots: void removeModalDialog(QObject* modal); From 4e5c650621ad31aad88a410734f9e46769a9b30d Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 16 Aug 2017 21:41:18 +0200 Subject: [PATCH 12/19] Modified some usecases for async dialogs --- interface/src/Application.cpp | 18 +++++--- interface/src/AvatarBookmarks.cpp | 41 ++++++++++--------- interface/src/LocationBookmarks.cpp | 29 +++++++------ .../scripting/WindowScriptingInterface.cpp | 16 +++++--- .../src/scripting/WindowScriptingInterface.h | 3 +- scripts/system/edit.js | 20 +++++---- 6 files changed, 74 insertions(+), 53 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 936a0bac6b..effbb35ece 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6849,12 +6849,18 @@ 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::asyncWarning("Error Loading Script", "Cannot load client script over ATP"); - } else if (!newScript.isEmpty()) { - DependencyManager::get()->loadScript(newScript.trimmed()); - } + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, [=] (QVariant response) { + disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, nullptr); + auto offscreenUi = DependencyManager::get(); + const QString& newScript = response.toString(); + if (QUrl(newScript).scheme() == "atp") { + OffscreenUi::asyncWarning("Error Loading Script", "Cannot load client script over ATP"); + } else if (!newScript.isEmpty()) { + DependencyManager::get()->loadScript(newScript.trimmed()); + } + }); + OffscreenUi::getTextAsync(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL"); } void Application::loadLODToolsDialog() { diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 73192b0bef..7c42effbc2 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -106,30 +106,31 @@ void AvatarBookmarks::changeToBookmarkedAvatar() { } void AvatarBookmarks::addBookmark() { - bool ok = false; - auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString(), &ok); - if (!ok) { - return; - } + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, [=] (QVariant response) { + disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, nullptr); + auto offscreenUi = DependencyManager::get(); + auto bookmarkName = response.toString(); + bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); + if (bookmarkName.length() == 0) { + return; + } - bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); - if (bookmarkName.length() == 0) { - return; - } + auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString(); + const QVariant& avatarScale = myAvatar->getAvatarScale(); - const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString(); - const QVariant& avatarScale = myAvatar->getAvatarScale(); + // If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION + QVariantMap *bookmark = new QVariantMap; + bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); + bookmark->insert(ENTRY_AVATAR_URL, avatarUrl); + bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale); + bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); - // If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION - QVariantMap *bookmark = new QVariantMap; - bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); - bookmark->insert(ENTRY_AVATAR_URL, avatarUrl); - bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale); - bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); - - Bookmarks::addBookmarkToFile(bookmarkName, *bookmark); + Bookmarks::addBookmarkToFile(bookmarkName, *bookmark); + }); + OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString()); } void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) { diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp index eee6cdf3c8..ee8e546b16 100644 --- a/interface/src/LocationBookmarks.cpp +++ b/interface/src/LocationBookmarks.cpp @@ -70,20 +70,23 @@ void LocationBookmarks::teleportToBookmark() { } void LocationBookmarks::addBookmark() { - bool ok = false; - auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString(), &ok); - if (!ok) { - return; - } + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, [=] (QVariant response) { + disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, nullptr); + auto offscreenUi = DependencyManager::get(); + auto bookmarkName = response.toString(); - bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); - if (bookmarkName.length() == 0) { - return; - } + bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); + if (bookmarkName.length() == 0) { + return; + } - auto addressManager = DependencyManager::get(); - QString bookmarkAddress = addressManager->currentAddress().toString(); - Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress); + auto addressManager = DependencyManager::get(); + QString bookmarkAddress = addressManager->currentAddress().toString(); + Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress); + }); + + OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString()); } void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) { @@ -97,4 +100,4 @@ void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, co menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole); Bookmarks::sortActions(menubar, _bookmarksMenu); } -} \ No newline at end of file +} diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 512d79adb2..d5c645d176 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -116,11 +116,17 @@ QScriptValue WindowScriptingInterface::confirm(const QString& message) { /// Display a prompt with a text box /// \param const QString& message message to display /// \param const QString& defaultText default text in the text box -/// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise. -QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { - bool ok = false; - QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText, &ok); - return ok ? QScriptValue(result) : QScriptValue::NullValue; +void WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, + this, [=] (QVariant result) { + auto offscreenUi = DependencyManager::get(); + disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, + this, nullptr); + emit promptTextChanged(result.toString()); + }); + + OffscreenUi::getTextAsync(nullptr, "", message, QLineEdit::Normal, defaultText); } CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 8ae315d05c..401f47bcdd 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -51,7 +51,7 @@ public slots: void raiseMainWindow(); void alert(const QString& message = ""); QScriptValue confirm(const QString& message = ""); - QScriptValue prompt(const QString& message = "", const QString& defaultText = ""); + void prompt(const QString& message = "", const QString& defaultText = ""); CustomPromptResult customPrompt(const QVariant& config); void browseDir(const QString& title = "", const QString& directory = ""); void browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); @@ -91,6 +91,7 @@ signals: void assetsDirChanged(QString assetsDir); void saveFileChanged(QString filename); void openFileChanged(QString filename); + void promptTextChanged(QString text); // triggered when window size or position changes void geometryChanged(QRect geometry); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 8d57c37bf6..1828146182 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1473,6 +1473,16 @@ function onFileOpenChanged(filename) { } } +function onPromptTextChanged(prompt) { + Window.promptTextChanged.disconnect(onPromptTextChanged); + if (prompt !== "") { + if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { + toolBar.toggle(); + } + importSVO(prompt); + } +} + function handeMenuEvent(menuItem) { if (menuItem === "Allow Selecting of Small Models") { allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); @@ -1498,14 +1508,8 @@ function handeMenuEvent(menuItem) { Window.openFileChanged.connect(onFileOpenChanged); Window.browse("Select Model to Import", "", "*.json"); } else { - var importURL = Window.prompt("URL of SVO to import", ""); - if (importURL) { - if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { - toolBar.toggle(); - } - importSVO(importURL); - } - + Window.promptTextChanged.connect(onFileOpenChanged); + Window.prompt("URL of SVO to import", ""); } } else if (menuItem === "Entity List...") { entityListTool.toggleVisible(); From 15f8bc0141f993ffec8f651bc74f9956938c1eb8 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 16 Aug 2017 22:17:34 +0200 Subject: [PATCH 13/19] Keep old function names for compatibility --- .../scripting/WindowScriptingInterface.cpp | 109 +++++++++++++++++- .../src/scripting/WindowScriptingInterface.h | 15 ++- scripts/system/edit.js | 8 +- scripts/system/libraries/entityList.js | 2 +- scripts/system/snapshot.js | 2 +- .../marketplace/record/record.js | 2 +- 6 files changed, 121 insertions(+), 17 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index d5c645d176..698e6ebb29 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -116,7 +116,17 @@ QScriptValue WindowScriptingInterface::confirm(const QString& message) { /// Display a prompt with a text box /// \param const QString& message message to display /// \param const QString& defaultText default text in the text box -void WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { +/// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise. +QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { + bool ok = false; + QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText, &ok); + return ok ? QScriptValue(result) : QScriptValue::NullValue; +} + +/// Display a prompt with a text box +/// \param const QString& message message to display +/// \param const QString& defaultText default text in the text box +void WindowScriptingInterface::promptAsync(const QString& message, const QString& defaultText) { auto offscreenUi = DependencyManager::get(); connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, [=] (QVariant result) { @@ -180,7 +190,29 @@ void WindowScriptingInterface::ensureReticleVisible() const { /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -void WindowScriptingInterface::browseDir(const QString& title, const QString& directory) { +/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` +QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QString& directory) { + ensureReticleVisible(); + QString path = directory; + if (path.isEmpty()) { + path = getPreviousBrowseLocation(); + } +#ifndef Q_OS_WIN + path = fixupPathForMac(directory); +#endif + QString result = OffscreenUi::getExistingDirectory(nullptr, title, path); + if (!result.isEmpty()) { + setPreviousBrowseLocation(QFileInfo(result).absolutePath()); + } + return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); +} + +/// Display a "browse to directory" dialog. If `directory` is an invalid file or directory the browser will start at the current +/// working directory. +/// \param const QString& title title of the window +/// \param const QString& directory directory to start the file browser at +/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` +void WindowScriptingInterface::browseDirAsync(const QString& title, const QString& directory) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -204,12 +236,32 @@ void WindowScriptingInterface::browseDir(const QString& title, const QString& di OffscreenUi::getExistingDirectoryAsync(nullptr, title, path); } +/// \param const QString& title title of the window +/// \param const QString& directory directory to start the file browser at +/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` +/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` +QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { + ensureReticleVisible(); + QString path = directory; + if (path.isEmpty()) { + path = getPreviousBrowseLocation(); + } +#ifndef Q_OS_WIN + path = fixupPathForMac(directory); +#endif + QString result = OffscreenUi::getOpenFileName(nullptr, title, path, nameFilter); + if (!result.isEmpty()) { + setPreviousBrowseLocation(QFileInfo(result).absolutePath()); + } + return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); +} + /// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current /// working directory. /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -void WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { +void WindowScriptingInterface::browseAsync(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -238,7 +290,29 @@ void WindowScriptingInterface::browse(const QString& title, const QString& direc /// \param const QString& title title of the window /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -void WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { +/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` +QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { + ensureReticleVisible(); + QString path = directory; + if (path.isEmpty()) { + path = getPreviousBrowseLocation(); + } +#ifndef Q_OS_WIN + path = fixupPathForMac(directory); +#endif + QString result = OffscreenUi::getSaveFileName(nullptr, title, path, nameFilter); + if (!result.isEmpty()) { + setPreviousBrowseLocation(QFileInfo(result).absolutePath()); + } + return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); +} + +/// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current +/// working directory. +/// \param const QString& title title of the window +/// \param const QString& directory directory to start the file browser at +/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` +void WindowScriptingInterface::saveAsync(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { @@ -262,12 +336,37 @@ void WindowScriptingInterface::save(const QString& title, const QString& directo OffscreenUi::getSaveFileNameAsync(nullptr, title, path, nameFilter); } +/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid +/// directory the browser will start at the root directory. +/// \param const QString& title title of the window +/// \param const QString& directory directory to start the asset browser at +/// \param const QString& nameFilter filter to filter asset names by - see `QFileDialog` +/// \return QScriptValue asset path as a string if one was selected, otherwise `QScriptValue::NullValue` +QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) { + ensureReticleVisible(); + QString path = directory; + if (path.isEmpty()) { + path = getPreviousBrowseAssetLocation(); + } + if (path.left(1) != "/") { + path = "/" + path; + } + if (path.right(1) != "/") { + path = path + "/"; + } + QString result = OffscreenUi::getOpenAssetName(nullptr, title, path, nameFilter); + if (!result.isEmpty()) { + setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); + } + return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); +} + /// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid /// directory the browser will start at the root directory. /// \param const QString& title title of the window /// \param const QString& directory directory to start the asset browser at /// \param const QString& nameFilter filter to filter asset names by - see `QFileDialog` -void WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) { +void WindowScriptingInterface::browseAssetsAsync(const QString& title, const QString& directory, const QString& nameFilter) { ensureReticleVisible(); QString path = directory; if (path.isEmpty()) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 401f47bcdd..3304aed4ee 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -51,12 +51,17 @@ public slots: void raiseMainWindow(); void alert(const QString& message = ""); QScriptValue confirm(const QString& message = ""); - void prompt(const QString& message = "", const QString& defaultText = ""); + QScriptValue prompt(const QString& message, const QString& defaultText); + void promptAsync(const QString& message = "", const QString& defaultText = ""); CustomPromptResult customPrompt(const QVariant& config); - void browseDir(const QString& title = "", const QString& directory = ""); - void browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); - void save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); - void browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + QScriptValue browseDir(const QString& title = "", const QString& directory = ""); + void browseDirAsync(const QString& title = "", const QString& directory = ""); + QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + void browseAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + void saveAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + void browseAssetsAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void showAssetServer(const QString& upload = ""); void copyToClipboard(const QString& text); void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 1828146182..75510a31b3 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -432,7 +432,7 @@ var toolBar = (function () { addButton("importEntitiesButton", "assets-01.svg", function() { Window.openFileChanged.connect(onFileOpenChanged); - Window.browse("Select Model to Import", "", "*.json"); + Window.browseAsync("Select Model to Import", "", "*.json"); }); addButton("openAssetBrowserButton", "assets-01.svg", function() { @@ -1501,15 +1501,15 @@ function handeMenuEvent(menuItem) { Window.notifyEditError("No entities have been selected."); } else { Window.saveFileChanged.connect(onFileSaveChanged); - Window.save("Select Where to Save", "", "*.json"); + Window.saveAsync("Select Where to Save", "", "*.json"); } } else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") { if (menuItem === "Import Entities") { Window.openFileChanged.connect(onFileOpenChanged); - Window.browse("Select Model to Import", "", "*.json"); + Window.browseAsync("Select Model to Import", "", "*.json"); } else { Window.promptTextChanged.connect(onFileOpenChanged); - Window.prompt("URL of SVO to import", ""); + Window.promptAsync("URL of SVO to import", ""); } } else if (menuItem === "Entity List...") { entityListTool.toggleVisible(); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index d2d815f9e3..9d9689000e 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -150,7 +150,7 @@ EntityListTool = function(opts) { Window.notifyEditError("No entities have been selected."); } else { Window.saveFileChanged.connect(onFileSaveChanged); - Window.save("Select Where to Save", "", "*.json"); + Window.saveAsync("Select Where to Save", "", "*.json"); } } else if (data.type == "pal") { var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates. diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 08614c2030..bf0ab69789 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -121,7 +121,7 @@ function onMessage(message) { break; case 'chooseSnapshotLocation': Window.browseDirChanged.connect(snapshotDirChanged); - Window.browseDir("Choose Snapshots Directory", "", ""); + Window.browseDirAsync("Choose Snapshots Directory", "", ""); break; case 'openSettings': if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false)) diff --git a/unpublishedScripts/marketplace/record/record.js b/unpublishedScripts/marketplace/record/record.js index 84f631f307..a3f9d6ca0d 100644 --- a/unpublishedScripts/marketplace/record/record.js +++ b/unpublishedScripts/marketplace/record/record.js @@ -530,7 +530,7 @@ case LOAD_RECORDING_ACTION: // User wants to select an ATP recording to play. Window.assetsDirChanged.connect(onAssetsDirChanged); - Window.browseAssets("Select Recording to Play", "recordings", "*.hfr"); + Window.browseAssetsAsync("Select Recording to Play", "recordings", "*.hfr"); break; case START_RECORDING_ACTION: // Start making a recording. From 3ceeb0d83edfc079b1131d9bb73fbdbc85f45e0b Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 16 Sep 2017 22:04:53 +0200 Subject: [PATCH 14/19] Rework all c++ calls --- interface/src/Application.cpp | 85 ++++---- interface/src/AvatarBookmarks.cpp | 9 +- interface/src/LocationBookmarks.cpp | 10 +- interface/src/assets/ATPAssetMigrator.cpp | 64 +++--- .../scripting/WindowScriptingInterface.cpp | 59 ++--- libraries/ui/src/OffscreenUi.cpp | 202 ++++++++++-------- libraries/ui/src/OffscreenUi.h | 83 ++++--- 7 files changed, 257 insertions(+), 255 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a2178faa99..496cbc93e4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6146,13 +6146,13 @@ bool Application::askToSetAvatarUrl(const QString& url) { bool agreeToLicense = true; // assume true //create set avatar callback auto setAvatar = [=] (QString url, QString modelName) { - auto offscreenUi = DependencyManager::get(); + ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Set Avatar", + "Would you like to use '" + modelName + "' for your avatar?", + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok); + QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); - 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); + bool ok = (QMessageBox::Ok == static_cast(answer.toInt())); if (ok) { getMyAvatar()->useFullAvatarURL(url, modelName); emit fullAvatarURLChanged(url, modelName); @@ -6160,9 +6160,6 @@ bool Application::askToSetAvatarUrl(const QString& url) { 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()) { @@ -6170,12 +6167,13 @@ bool Application::askToSetAvatarUrl(const QString& url) { const int MAX_CHARACTERS_PER_LINE = 90; modelLicense = simpleWordWrap(modelLicense, MAX_CHARACTERS_PER_LINE); - 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); + ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Avatar Usage License", + modelLicense + "\nDo you agree to these terms?", + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + QObject::connect(dlg, &ModalDialogListener::response, this, [=, &agreeToLicense] (QVariant answer) { + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); - agreeToLicense = (answer == QMessageBox::Yes); + agreeToLicense = (static_cast(answer.toInt()) == QMessageBox::Yes); if (agreeToLicense) { switch (modelType) { case FSTReader::HEAD_AND_BODY_MODEL: { @@ -6192,10 +6190,6 @@ bool Application::askToSetAvatarUrl(const QString& url) { //auto offscreenUi = DependencyManager::get(); }); - - OffscreenUi::asyncQuestion("Avatar Usage License", - modelLicense + "\nDo you agree to these terms?", - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); } else { setAvatar(url, modelName); } @@ -6215,23 +6209,21 @@ 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) { + QString message = "Would you like to run this script:\n" + shortName; + ModalDialogListener* dlg = OffscreenUi::asyncQuestion(getWindow(), "Run Script", message, + QMessageBox::Yes | QMessageBox::No); + + QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { const QString& fileName = scriptFilenameOrURL; - if (answer == QMessageBox::Yes) { + if (static_cast(answer.toInt()) == 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); + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); }); - QString message = "Would you like to run this script:\n" + shortName; - - OffscreenUi::asyncQuestion(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No); - return true; } @@ -6268,11 +6260,14 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) { name = nameValue.toString(); } - 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) { + auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation"); + auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name); + ModalDialogListener* dlg = OffscreenUi::asyncQuestion(avatarAttachmentConfirmationTitle, + avatarAttachmentConfirmationMessage, + QMessageBox::Ok | QMessageBox::Cancel); + QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); + if (static_cast(answer.toInt()) == QMessageBox::Yes) { // add attachment to avatar auto myAvatar = getMyAvatar(); assert(myAvatar); @@ -6285,12 +6280,6 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) { 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\""); @@ -6933,17 +6922,17 @@ void Application::openUrl(const QUrl& url) const { void Application::loadDialog() { auto scriptEngines = DependencyManager::get(); - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, [=] (QString response) { - auto offscreenUi = DependencyManager::get(); - disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, nullptr); + ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_glWidget, tr("Open Script"), + getPreviousScriptLocation(), + tr("JavaScript Files (*.js)")); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { + disconnect(dlg, &ModalDialogListener::response, this, nullptr); + const QString& response = answer.toString(); if (!response.isEmpty() && QFile(response).exists()) { setPreviousScriptLocation(QFileInfo(response).absolutePath()); DependencyManager::get()->loadScript(response, true, false, false, true); // Don't load from cache } }); - OffscreenUi::getOpenFileNameAsync(_glWidget, tr("Open Script"), getPreviousScriptLocation(), - tr("JavaScript Files (*.js)")); } QString Application::getPreviousScriptLocation() { @@ -6956,10 +6945,9 @@ void Application::setPreviousScriptLocation(const QString& location) { } void Application::loadScriptURLDialog() const { - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, [=] (QVariant response) { - disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, nullptr); - auto offscreenUi = DependencyManager::get(); + ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL"); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + disconnect(dlg, &ModalDialogListener::response, this, nullptr); const QString& newScript = response.toString(); if (QUrl(newScript).scheme() == "atp") { OffscreenUi::asyncWarning("Error Loading Script", "Cannot load client script over ATP"); @@ -6967,7 +6955,6 @@ void Application::loadScriptURLDialog() const { DependencyManager::get()->loadScript(newScript.trimmed()); } }); - OffscreenUi::getTextAsync(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL"); } void Application::loadLODToolsDialog() { diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 7c42effbc2..5b9c45eef5 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -106,10 +106,9 @@ void AvatarBookmarks::changeToBookmarkedAvatar() { } void AvatarBookmarks::addBookmark() { - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, [=] (QVariant response) { - disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, nullptr); - auto offscreenUi = DependencyManager::get(); + ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString()); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + disconnect(dlg, &ModalDialogListener::response, this, nullptr); auto bookmarkName = response.toString(); bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); if (bookmarkName.length() == 0) { @@ -130,7 +129,7 @@ void AvatarBookmarks::addBookmark() { Bookmarks::addBookmarkToFile(bookmarkName, *bookmark); }); - OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString()); + } void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) { diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp index d1e5595c5a..285f533a7f 100644 --- a/interface/src/LocationBookmarks.cpp +++ b/interface/src/LocationBookmarks.cpp @@ -74,10 +74,10 @@ void LocationBookmarks::teleportToBookmark() { } void LocationBookmarks::addBookmark() { - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, [=] (QVariant response) { - disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, this, nullptr); - auto offscreenUi = DependencyManager::get(); + ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString()); + + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + disconnect(dlg, &ModalDialogListener::response, this, nullptr); auto bookmarkName = response.toString(); bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); @@ -89,8 +89,6 @@ void LocationBookmarks::addBookmark() { QString bookmarkAddress = addressManager->currentAddress().toString(); Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress); }); - - OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString()); } void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) { diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 4f79d32734..8de40865b7 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -42,10 +42,12 @@ static const QString COMPOUND_SHAPE_URL_KEY = "compoundShapeURL"; static const QString MESSAGE_BOX_TITLE = "ATP Asset Migration"; void ATPAssetMigrator::loadEntityServerFile() { - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, [=] (QString filename) { - auto offscreenUi = DependencyManager::get(); - disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, nullptr); + ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_dialogParent, tr("Select an entity-server content file to migrate"), + QString(), tr("Entity-Server Content (*.gz)")); + + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + const QString& filename = response.toString(); + disconnect(dlg, &ModalDialogListener::response, this, nullptr); if (!filename.isEmpty()) { qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename; @@ -85,13 +87,12 @@ void ATPAssetMigrator::loadEntityServerFile() { " current asset-server\nand then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\ " continue?\n\nMake sure you are connected to the right domain." }; + ModalDialogListener* migrationConfirmDialog = OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + QObject::connect(migrationConfirmDialog, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(migrationConfirmDialog, &ModalDialogListener::response, this, nullptr); - 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 (QMessageBox::Yes == answer) { + if (QMessageBox::Yes == static_cast(answer.toInt())) { // try to open the file at the given filename QFile modelsFile { filename }; @@ -148,31 +149,31 @@ void ATPAssetMigrator::loadEntityServerFile() { "Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n"\ "Select \"No\" to be prompted for each discovered asset." }; - 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) { + ModalDialogListener* migrationConfirmDialog1 = OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, + "Would you like to migrate the following resource?\n" + migrationURL.toString(), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + QObject::connect(migrationConfirmDialog1, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(migrationConfirmDialog1, &ModalDialogListener::response, this, nullptr); + if (static_cast(answer.toInt()) == + QMessageBox::Yes) { wantsCompleteMigration = true; migrateResources(migrationURL, jsonValue, isModelURL); } else { - 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) { + ModalDialogListener* migrationConfirmDialog2 = OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + QObject::connect(migrationConfirmDialog2, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(migrationConfirmDialog2, &ModalDialogListener::response, this, nullptr); + if (static_cast(answer.toInt()) == + QMessageBox::Yes) { migrateResources(migrationURL, jsonValue, isModelURL); } else { _ignoredUrls.insert(migrationURL); } }); - OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, - "Would you like to migrate the following resource?\n" + migrationURL.toString(), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - } }); - OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); hasAskedForCompleteMigration = true; } if (wantsCompleteMigration) { @@ -194,13 +195,8 @@ void ATPAssetMigrator::loadEntityServerFile() { } } }); - OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - } }); - OffscreenUi::getOpenFileNameAsync(_dialogParent, tr("Select an entity-server content file to migrate"), - QString(), tr("Entity-Server Content (*.gz)")); } void ATPAssetMigrator::migrateResource(ResourceRequest* request) { @@ -333,8 +329,9 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { void ATPAssetMigrator::saveEntityServerFile() { // show a dialog to ask the user where they want to save the file - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, this, [=] (QString saveName) { + ModalDialogListener* dlg = OffscreenUi::getSaveFileNameAsync(_dialogParent, "Save Migrated Entities File"); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + const QString& saveName = response.toString(); QFile saveFile { saveName }; if (saveFile.open(QIODevice::WriteOnly)) { @@ -369,9 +366,6 @@ void ATPAssetMigrator::saveEntityServerFile() { // reset after the attempted save, success or fail reset(); }); - - OffscreenUi::getSaveFileNameAsync(_dialogParent, "Save Migrated Entities File"); - } void ATPAssetMigrator::reset() { diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index bdd99c75c2..8dacfe5569 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -127,16 +127,13 @@ QScriptValue WindowScriptingInterface::prompt(const QString& message, const QStr /// \param const QString& message message to display /// \param const QString& defaultText default text in the text box void WindowScriptingInterface::promptAsync(const QString& message, const QString& defaultText) { - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, - this, [=] (QVariant result) { - auto offscreenUi = DependencyManager::get(); - disconnect(offscreenUi.data(), &OffscreenUi::inputDialogResponse, - this, nullptr); + ModalDialogListener* dlg = OffscreenUi::getTextAsync(nullptr, "", message, QLineEdit::Normal, defaultText); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant result) { + disconnect(dlg, &ModalDialogListener::response, this, nullptr); emit promptTextChanged(result.toString()); }); - OffscreenUi::getTextAsync(nullptr, "", message, QLineEdit::Normal, defaultText); + } CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) { @@ -221,19 +218,15 @@ void WindowScriptingInterface::browseDirAsync(const QString& title, const QStrin #ifndef Q_OS_WIN path = fixupPathForMac(directory); #endif - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, - this, [=] (QString result) { - auto offscreenUi = DependencyManager::get(); - disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, - this, nullptr); + ModalDialogListener* dlg = OffscreenUi::getExistingDirectoryAsync(nullptr, title, path); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + const QString& result = response.toString(); + disconnect(dlg, &ModalDialogListener::response, this, nullptr); if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } emit browseDirChanged(result); }); - - OffscreenUi::getExistingDirectoryAsync(nullptr, title, path); } /// \param const QString& title title of the window @@ -270,19 +263,17 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString& #ifndef Q_OS_WIN path = fixupPathForMac(directory); #endif - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, - this, [=] (QString result) { - auto offscreenUi = DependencyManager::get(); - disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, - this, nullptr); + ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(nullptr, title, path, nameFilter); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + const QString& result = response.toString(); + disconnect(dlg, &ModalDialogListener::response, this, nullptr); if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } emit openFileChanged(result); }); - OffscreenUi::getOpenFileNameAsync(nullptr, title, path, nameFilter); + } /// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current @@ -321,19 +312,17 @@ void WindowScriptingInterface::saveAsync(const QString& title, const QString& di #ifndef Q_OS_WIN path = fixupPathForMac(directory); #endif - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, - this, [=] (QString result) { - auto offscreenUi = DependencyManager::get(); - disconnect(offscreenUi.data(), &OffscreenUi::fileDialogResponse, - this, nullptr); + ModalDialogListener* dlg = OffscreenUi::getSaveFileNameAsync(nullptr, title, path, nameFilter); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + const QString& result = response.toString(); + disconnect(dlg, &ModalDialogListener::response, this, nullptr); if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } emit saveFileChanged(result); }); - OffscreenUi::getSaveFileNameAsync(nullptr, title, path, nameFilter); + } /// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid @@ -379,19 +368,15 @@ void WindowScriptingInterface::browseAssetsAsync(const QString& title, const QSt path = path + "/"; } - auto offscreenUi = DependencyManager::get(); - connect(offscreenUi.data(), &OffscreenUi::assetDialogResponse, - this, [=] (QString result) { - auto offscreenUi = DependencyManager::get(); - disconnect(offscreenUi.data(), &OffscreenUi::assetDialogResponse, - this, nullptr); + ModalDialogListener* dlg = OffscreenUi::getOpenAssetNameAsync(nullptr, title, path, nameFilter); + connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { + const QString& result = response.toString(); + disconnect(dlg, &ModalDialogListener::response, this, nullptr); if (!result.isEmpty()) { setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath()); } emit assetsDirChanged(result); }); - - OffscreenUi::getOpenAssetNameAsync(nullptr, title, path, nameFilter); } void WindowScriptingInterface::showAssetServer(const QString& upload) { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 2048bd547f..da12d8ac31 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -151,45 +151,6 @@ bool OffscreenUi::isVisible(const QString& name) { } } -class ModalDialogListener : public QObject { - Q_OBJECT - friend class OffscreenUi; - -protected: - ModalDialogListener(QQuickItem* dialog) : _dialog(dialog) { - if (!dialog) { - _finished = true; - return; - } - connect(_dialog, SIGNAL(destroyed()), this, SLOT(onDestroyed())); - } - - ~ModalDialogListener() { - if (_dialog) { - disconnect(_dialog); - } - } - - virtual QVariant waitForResult() { - while (!_finished) { - QCoreApplication::processEvents(); - } - return _result; - } - -protected slots: - void onDestroyed() { - _finished = true; - disconnect(_dialog); - _dialog = nullptr; - } - -protected: - QQuickItem* _dialog; - bool _finished { false }; - QVariant _result; -}; - class MessageBoxListener : public ModalDialogListener { Q_OBJECT @@ -211,7 +172,7 @@ private slots: _result = button; _finished = true; auto offscreenUi = DependencyManager::get(); - emit offscreenUi->response(static_cast(_result.toInt())); + emit response(_result); offscreenUi->removeModalDialog(qobject_cast(this)); disconnect(_dialog); } @@ -272,19 +233,23 @@ 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) { +ModalDialogListener* OffscreenUi::asyncMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "asyncMessageBox", + Q_RETURN_ARG(ModalDialogListener*, ret), Q_ARG(Icon, icon), Q_ARG(QString, title), Q_ARG(QString, text), Q_ARG(QMessageBox::StandardButtons, buttons), Q_ARG(QMessageBox::StandardButton, defaultButton)); + return ret; } MessageBoxListener* messageBoxListener = new MessageBoxListener(createMessageBox(icon, title, text, buttons, defaultButton)); QObject* modalDialog = qobject_cast(messageBoxListener); _modalDialogListeners.push_back(modalDialog); + return messageBoxListener; } QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text, @@ -296,31 +261,34 @@ QMessageBox::StandardButton OffscreenUi::information(const QString& title, const return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton); } -void OffscreenUi::asyncCritical(const QString& title, const QString& text, +ModalDialogListener* OffscreenUi::asyncCritical(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton); + return DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton); } -void OffscreenUi::asyncInformation(const QString& title, const QString& text, +ModalDialogListener* OffscreenUi::asyncInformation(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton); + return DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton); } QMessageBox::StandardButton OffscreenUi::question(const QString& title, const QString& text, 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, + +ModalDialogListener *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); + return 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, + +ModalDialogListener* 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); + return DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_WARNING, title, text, buttons, defaultButton); } @@ -338,8 +306,8 @@ class InputDialogListener : public ModalDialogListener { private slots: void onSelected(const QVariant& result) { _result = result; - auto offscreenUi = DependencyManager::get(); - emit offscreenUi->inputDialogResponse(_result); + auto offscreenUi = DependencyManager::get(); + emit response(_result); offscreenUi->removeModalDialog(qobject_cast(this)); _finished = true; disconnect(_dialog); @@ -394,17 +362,17 @@ QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const return result; } -void OffscreenUi::getTextAsync(const Icon icon, const QString& title, const QString& label, const QString& text) { - DependencyManager::get()->inputDialogAsync(icon, title, label, text); +ModalDialogListener* OffscreenUi::getTextAsync(const Icon icon, const QString& title, const QString& label, const QString& text) { + return DependencyManager::get()->inputDialogAsync(icon, title, label, text); } -void OffscreenUi::getItemAsync(const Icon icon, const QString& title, const QString& label, const QStringList& items, +ModalDialogListener* OffscreenUi::getItemAsync(const Icon icon, const QString& title, const QString& label, const QStringList& items, int current, bool editable) { auto offscreenUi = DependencyManager::get(); auto inputDialog = offscreenUi->createInputDialog(icon, title, label, current); if (!inputDialog) { - return; + return nullptr; } inputDialog->setProperty("items", items); inputDialog->setProperty("editable", editable); @@ -412,11 +380,11 @@ void OffscreenUi::getItemAsync(const Icon icon, const QString& title, const QStr InputDialogListener* inputDialogListener = new InputDialogListener(inputDialog); offscreenUi->getModalDialogListeners().push_back(qobject_cast(inputDialogListener)); - return; + return inputDialogListener; } -void OffscreenUi::getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config) { - DependencyManager::get()->customInputDialog(icon, title, config); +ModalDialogListener* OffscreenUi::getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config) { + return DependencyManager::get()->customInputDialogAsync(icon, title, config); } QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) { @@ -434,19 +402,22 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q return waitForInputDialogResult(createInputDialog(icon, title, label, current)); } -void OffscreenUi::inputDialogAsync(const Icon icon, const QString& title, const QString& label, const QVariant& current) { +ModalDialogListener* OffscreenUi::inputDialogAsync(const Icon icon, const QString& title, const QString& label, const QVariant& current) { if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "inputDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), Q_ARG(Icon, icon), Q_ARG(QString, title), Q_ARG(QString, label), Q_ARG(QVariant, current)); - return; + return ret; } InputDialogListener* inputDialogListener = new InputDialogListener(createInputDialog(icon, title, label, current)); QObject* inputDialog = qobject_cast(inputDialogListener); _modalDialogListeners.push_back(inputDialog); + return inputDialogListener; } QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, const QVariantMap& config) { @@ -469,19 +440,21 @@ QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, c return result; } -void OffscreenUi::customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config) { +ModalDialogListener* OffscreenUi::customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config) { if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "customInputDialogAsync", - Q_ARG(Icon, icon), - Q_ARG(QString, title), - Q_ARG(QVariantMap, config)); - return; + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(Icon, icon), + Q_ARG(QString, title), + Q_ARG(QVariantMap, config)); + return ret; } InputDialogListener* inputDialogListener = new InputDialogListener(createCustomInputDialog(icon, title, config)); QObject* inputDialog = qobject_cast(inputDialogListener); _modalDialogListeners.push_back(inputDialog); - return; + return inputDialogListener; } void OffscreenUi::togglePinned() { @@ -655,6 +628,7 @@ void OffscreenUi::createDesktop(const QUrl& url) { #endif load(url, [=](QQmlContext* context, QObject* newObject) { + Q_UNUSED(context) _desktop = static_cast(newObject); getSurfaceContext()->setContextProperty("desktop", _desktop); _toolWindow = _desktop->findChild("ToolWindow"); @@ -703,7 +677,7 @@ private slots: _result = file; _finished = true; auto offscreenUi = DependencyManager::get(); - emit offscreenUi->fileDialogResponse(_result.toUrl().toLocalFile()); + emit response(_result); offscreenUi->removeModalDialog(qobject_cast(this)); disconnect(_dialog); } @@ -740,7 +714,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) { return result.toUrl().toLocalFile(); } -void OffscreenUi::fileDialogAsync(const QVariantMap& properties) { +ModalDialogListener* OffscreenUi::fileDialogAsync(const QVariantMap& properties) { QVariant buildDialogResult; bool invokeResult; auto tabletScriptingInterface = DependencyManager::get(); @@ -759,14 +733,14 @@ void OffscreenUi::fileDialogAsync(const QVariantMap& properties) { if (!invokeResult) { qWarning() << "Failed to create file open dialog"; - return; + return nullptr; } FileDialogListener* fileDialogListener = new FileDialogListener(qvariant_cast(buildDialogResult)); QObject* fileModalDialog = qobject_cast(fileDialogListener); _modalDialogListeners.push_back(fileModalDialog); - return; + return fileDialogListener; } QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { @@ -791,15 +765,17 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, return fileDialog(map); } -void OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "fileOpenDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), Q_ARG(QString, caption), Q_ARG(QString, dir), Q_ARG(QString, filter), Q_ARG(QString*, selectedFilter), Q_ARG(QFileDialog::Options, options)); - return; + return ret; } // FIXME support returning the selected filter... somehow? @@ -808,7 +784,7 @@ void OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir map.insert("dir", QUrl::fromLocalFile(dir)); map.insert("filter", filter); map.insert("options", static_cast(options)); - fileDialogAsync(map); + return fileDialogAsync(map); } QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { @@ -835,15 +811,17 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, return fileDialog(map); } -void OffscreenUi::fileSaveDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::fileSaveDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "fileSaveDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), Q_ARG(QString, caption), Q_ARG(QString, dir), Q_ARG(QString, filter), Q_ARG(QString*, selectedFilter), Q_ARG(QFileDialog::Options, options)); - return; + return ret; } // FIXME support returning the selected filter... somehow? @@ -879,15 +857,17 @@ QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QStri return fileDialog(map); } -void OffscreenUi::existingDirectoryDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::existingDirectoryDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialogAsync", - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); - return; + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return ret; } QVariantMap map; @@ -900,26 +880,32 @@ void OffscreenUi::existingDirectoryDialogAsync(const QString& caption, const QSt } QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->fileOpenDialog(caption, dir, filter, selectedFilter, options); } -void OffscreenUi::getOpenFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::getOpenFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->fileOpenDialogAsync(caption, dir, filter, selectedFilter, options); } QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->fileSaveDialog(caption, dir, filter, selectedFilter, options); } -void OffscreenUi::getSaveFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::getSaveFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->fileSaveDialogAsync(caption, dir, filter, selectedFilter, options); } QString OffscreenUi::getExistingDirectory(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->existingDirectoryDialog(caption, dir, filter, selectedFilter, options); } -void OffscreenUi::getExistingDirectoryAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::getExistingDirectoryAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->existingDirectoryDialogAsync(caption, dir, filter, selectedFilter, options); } @@ -939,7 +925,7 @@ class AssetDialogListener : public ModalDialogListener { void onSelectedAsset(QVariant asset) { _result = asset; auto offscreenUi = DependencyManager::get(); - emit offscreenUi->assetDialogResponse(_result.toUrl().toLocalFile()); + emit response(_result); offscreenUi->removeModalDialog(qobject_cast(this)); _finished = true; disconnect(_dialog); @@ -978,7 +964,7 @@ QString OffscreenUi::assetDialog(const QVariantMap& properties) { return result.toUrl().toString(); } -void OffscreenUi::assetDialogAsync(const QVariantMap& properties) { +ModalDialogListener *OffscreenUi::assetDialogAsync(const QVariantMap& properties) { // ATP equivalent of fileDialog(). QVariant buildDialogResult; bool invokeResult; @@ -998,13 +984,13 @@ void OffscreenUi::assetDialogAsync(const QVariantMap& properties) { if (!invokeResult) { qWarning() << "Failed to create asset open dialog"; - return; + return nullptr; } AssetDialogListener* assetDialogListener = new AssetDialogListener(qvariant_cast(buildDialogResult)); QObject* assetModalDialog = qobject_cast(assetDialogListener); _modalDialogListeners.push_back(assetModalDialog); - return; + return assetDialogListener; } QList &OffscreenUi::getModalDialogListeners() { @@ -1034,16 +1020,18 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir, return assetDialog(map); } -void OffscreenUi::assetOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::assetOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { // ATP equivalent of fileOpenDialog(). if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "assetOpenDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), Q_ARG(QString, caption), Q_ARG(QString, dir), Q_ARG(QString, filter), Q_ARG(QString*, selectedFilter), Q_ARG(QFileDialog::Options, options)); - return; + return ret; } // FIXME support returning the selected filter... somehow? @@ -1057,11 +1045,13 @@ void OffscreenUi::assetOpenDialogAsync(const QString& caption, const QString& di QString OffscreenUi::getOpenAssetName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { // ATP equivalent of getOpenFileName(). + Q_UNUSED(ignored) return DependencyManager::get()->assetOpenDialog(caption, dir, filter, selectedFilter, options); } -void OffscreenUi::getOpenAssetNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { +ModalDialogListener* OffscreenUi::getOpenAssetNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { // ATP equivalent of getOpenFileName(). + Q_UNUSED(ignored) return DependencyManager::get()->assetOpenDialogAsync(caption, dir, filter, selectedFilter, options); } @@ -1104,5 +1094,31 @@ unsigned int OffscreenUi::getMenuUserDataId() const { return _vrMenu->_userDataId; } -#include "OffscreenUi.moc" +ModalDialogListener::ModalDialogListener(QQuickItem *dialog) : _dialog(dialog) { + if (!dialog) { + _finished = true; + return; + } + connect(_dialog, SIGNAL(destroyed()), this, SLOT(onDestroyed())); +} +ModalDialogListener::~ModalDialogListener() { + if (_dialog) { + disconnect(_dialog); + } +} + +QVariant ModalDialogListener::waitForResult() { + while (!_finished) { + QCoreApplication::processEvents(); + } + return _result; +} + +void ModalDialogListener::onDestroyed() { + _finished = true; + disconnect(_dialog); + _dialog = nullptr; +} + +#include "OffscreenUi.moc" diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 9613cbc3f4..391d7da6c7 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -30,6 +30,27 @@ class VrMenu; #define OFFSCREEN_VISIBILITY_PROPERTY "shown" +class ModalDialogListener : public QObject { + Q_OBJECT + friend class OffscreenUi; + +protected: + ModalDialogListener(QQuickItem* dialog); + virtual ~ModalDialogListener(); + virtual QVariant waitForResult(); + +signals: + void response(const QVariant& value); + +protected slots: + void onDestroyed(); + +protected: + QQuickItem* _dialog; + bool _finished { false }; + QVariant _result; +}; + class OffscreenUi : public OffscreenQmlSurface, public Dependency { Q_OBJECT @@ -71,7 +92,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); + Q_INVOKABLE ModalDialogListener* 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 @@ -89,12 +110,12 @@ public: QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return information(title, text, buttons, defaultButton); } - static void asyncCritical(void* ignored, const QString& title, const QString& text, + static ModalDialogListener* asyncCritical(void* ignored, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return asyncCritical(title, text, buttons, defaultButton); } - static void asyncInformation(void* ignored, const QString& title, const QString& text, + static ModalDialogListener* asyncInformation(void* ignored, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return asyncInformation(title, text, buttons, defaultButton); @@ -106,7 +127,7 @@ public: return question(title, text, buttons, defaultButton); } - static void asyncQuestion(void* ignored, const QString& title, const QString& text, + static ModalDialogListener* 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); @@ -117,7 +138,7 @@ public: QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return warning(title, text, buttons, defaultButton); } - static void asyncWarning(void* ignored, const QString& title, const QString& text, + static ModalDialogListener* 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); @@ -129,53 +150,55 @@ public: static QMessageBox::StandardButton information(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); - static void asyncCritical(const QString& title, const QString& text, + static ModalDialogListener* asyncCritical(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); - static void asyncInformation(const QString& title, const QString& text, + static ModalDialogListener *asyncInformation(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 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, + static ModalDialogListener* 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, + static ModalDialogListener *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 void fileOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* fileOpenDialogAsync(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); - Q_INVOKABLE void fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); - Q_INVOKABLE void existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); - Q_INVOKABLE void assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getOpenFileName static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); - static void getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getSaveFileName static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); - static void getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getExistingDirectory static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); - static void getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); static QString getOpenAssetName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); - static void getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QVariant inputDialog(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); - Q_INVOKABLE void inputDialogAsync(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); + Q_INVOKABLE ModalDialogListener* inputDialogAsync(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); Q_INVOKABLE QVariant customInputDialog(const Icon icon, const QString& title, const QVariantMap& config); - Q_INVOKABLE void customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config); + Q_INVOKABLE ModalDialogListener* customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config); QQuickItem* createInputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current); QQuickItem* createCustomInputDialog(const Icon icon, const QString& title, const QVariantMap& config); QVariant waitForInputDialogResult(QQuickItem* inputDialog); @@ -194,13 +217,13 @@ public: } // Compatibility with QInputDialog::getText - static void getTextAsync(void* ignored, const QString & title, const QString & label, + static ModalDialogListener* getTextAsync(void* ignored, const QString & title, const QString & label, QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) { return getTextAsync(OffscreenUi::ICON_NONE, title, label, text); } // Compatibility with QInputDialog::getItem - static void getItemAsync(void *ignored, const QString & title, const QString & label, const QStringList & items, + static ModalDialogListener* getItemAsync(void *ignored, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) { return getItemAsync(OffscreenUi::ICON_NONE, title, label, items, current, editable); @@ -209,27 +232,27 @@ public: static QString getText(const Icon icon, const QString & title, const QString & label, const QString & text = QString(), bool * ok = 0); static QString getItem(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0); static QVariant getCustomInfo(const Icon icon, const QString& title, const QVariantMap& config, bool* ok = 0); - static void getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString()); - static void getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true); - static void getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config); + static ModalDialogListener* getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString()); + static ModalDialogListener* getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true); + static ModalDialogListener* getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config); unsigned int getMenuUserDataId() const; QList &getModalDialogListeners(); signals: void showDesktop(); - void response(QMessageBox::StandardButton response); - void fileDialogResponse(QString response); - void assetDialogResponse(QString response); - void inputDialogResponse(QVariant response); +// void response(QMessageBox::StandardButton response); +// void fileDialogResponse(QString response); +// void assetDialogResponse(QString response); +// void inputDialogResponse(QVariant response); public slots: void removeModalDialog(QObject* modal); private: QString fileDialog(const QVariantMap& properties); - void fileDialogAsync(const QVariantMap &properties); + ModalDialogListener *fileDialogAsync(const QVariantMap &properties); QString assetDialog(const QVariantMap& properties); - void assetDialogAsync(const QVariantMap& properties); + ModalDialogListener* assetDialogAsync(const QVariantMap& properties); QQuickItem* _desktop { nullptr }; QQuickItem* _toolWindow { nullptr }; From a8d24d9161c9bef01a730fd944b25f188f06dcc0 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 16 Sep 2017 22:41:18 +0200 Subject: [PATCH 15/19] Cleanup --- interface/src/scripting/WindowScriptingInterface.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 8dacfe5569..292fe46a85 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -272,8 +272,6 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString& } emit openFileChanged(result); }); - - } /// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current From 1cde504c741d146ec9751aac9a6883ce6ba1c13f Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 16 Sep 2017 22:57:26 +0200 Subject: [PATCH 16/19] Memleak fix. Cleanup --- interface/src/scripting/WindowScriptingInterface.cpp | 4 ---- libraries/ui/src/OffscreenUi.cpp | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 292fe46a85..4b981207f1 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -132,8 +132,6 @@ void WindowScriptingInterface::promptAsync(const QString& message, const QString disconnect(dlg, &ModalDialogListener::response, this, nullptr); emit promptTextChanged(result.toString()); }); - - } CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) { @@ -319,8 +317,6 @@ void WindowScriptingInterface::saveAsync(const QString& title, const QString& di } emit saveFileChanged(result); }); - - } /// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index da12d8ac31..9039889998 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -94,6 +94,7 @@ QObject* OffscreenUi::getFlags() { void OffscreenUi::removeModalDialog(QObject* modal) { if (modal) { _modalDialogListeners.removeOne(modal); + modal->deleteLater(); } } From 50ee41e0955982bacd3141ee98be81ef76a3a1dd Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 19 Sep 2017 22:42:15 +0200 Subject: [PATCH 17/19] Fix file loading dialogs --- libraries/ui/src/OffscreenUi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 9039889998..9dfe831081 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -675,7 +675,7 @@ class FileDialogListener : public ModalDialogListener { private slots: void onSelectedFile(QVariant file) { - _result = file; + _result = file.toUrl().toLocalFile(); _finished = true; auto offscreenUi = DependencyManager::get(); emit response(_result); From c17ca955dbdadbd484276daeb3347faaf7c15567 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 20 Sep 2017 20:03:53 +0200 Subject: [PATCH 18/19] Listener disconnects in C++ now --- scripts/system/edit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index a6d09f3ea6..350d1bd8a3 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1463,7 +1463,6 @@ function onFileSaveChanged(filename) { } function onFileOpenChanged(filename) { - Window.openFileChanged.disconnect(onFileOpenChanged); var importURL = null; if (filename !== "") { importURL = "file:///" + filename; From 6e3cfe81ab5288cbef675e8b9f492e66900c1b56 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 20 Sep 2017 21:01:16 +0200 Subject: [PATCH 19/19] Fix crash on second bookmark saving --- interface/src/Bookmarks.cpp | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 0bd6b01128..f48b5e1f5b 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -58,20 +58,25 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& b Menu* menubar = Menu::getInstance(); if (contains(bookmarkName)) { auto offscreenUi = DependencyManager::get(); - auto duplicateBookmarkMessage = offscreenUi->createMessageBox(OffscreenUi::ICON_WARNING, "Duplicate Bookmark", - "The bookmark name you entered already exists in your list.", - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?"); - auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage); - if (result != QMessageBox::Yes) { - return; - } - removeBookmarkFromMenu(menubar, bookmarkName); - } + ModalDialogListener* dlg = OffscreenUi::asyncWarning("Duplicate Bookmark", + "The bookmark name you entered already exists in your list.", + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + dlg->setProperty("informativeText", "Would you like to overwrite it?"); + QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); - addBookmarkToMenu(menubar, bookmarkName, bookmark); - insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName. - enableMenuItems(true); + if (QMessageBox::Yes == static_cast(answer.toInt())) { + removeBookmarkFromMenu(menubar, bookmarkName); + addBookmarkToMenu(menubar, bookmarkName, bookmark); + insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName. + enableMenuItems(true); + } + }); + } else { + addBookmarkToMenu(menubar, bookmarkName, bookmark); + insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName. + enableMenuItems(true); + } } void Bookmarks::insert(const QString& name, const QVariant& bookmark) {