mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #11159 from vladest/modal_dialogs_async
Modal dialogs async
This commit is contained in:
commit
e397d49564
18 changed files with 965 additions and 372 deletions
|
@ -980,7 +980,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
|
||||
|
@ -1845,7 +1845,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:
|
||||
|
@ -3751,7 +3751,7 @@ bool Application::acceptSnapshot(const QString& urlString) {
|
|||
DependencyManager::get<AddressManager>()->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;
|
||||
|
@ -6151,7 +6151,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;
|
||||
}
|
||||
|
||||
|
@ -6163,41 +6163,55 @@ 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) {
|
||||
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);
|
||||
|
||||
bool ok = (QMessageBox::Ok == static_cast<QMessageBox::StandardButton>(answer.toInt()));
|
||||
if (ok) {
|
||||
getMyAvatar()->useFullAvatarURL(url, modelName);
|
||||
emit fullAvatarURLChanged(url, modelName);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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",
|
||||
modelLicense + "\nDo you agree to these terms?",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
}
|
||||
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);
|
||||
|
||||
bool ok = false;
|
||||
agreeToLicense = (static_cast<QMessageBox::StandardButton>(answer.toInt()) == 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;
|
||||
}
|
||||
|
||||
if (!agreeToLicence) {
|
||||
qCDebug(interfaceapp) << "Declined to agree to avatar license: " << url;
|
||||
//auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
});
|
||||
} 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;
|
||||
|
@ -6205,8 +6219,6 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
|
||||
|
||||
bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
||||
QMessageBox::StandardButton reply;
|
||||
|
||||
QString shortName = scriptFilenameOrURL;
|
||||
|
||||
QUrl scriptURL { scriptFilenameOrURL };
|
||||
|
@ -6218,15 +6230,20 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
|||
}
|
||||
|
||||
QString message = "Would you like to run this script:\n" + shortName;
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncQuestion(getWindow(), "Run Script", message,
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
reply = OffscreenUi::question(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No);
|
||||
QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
const QString& fileName = scriptFilenameOrURL;
|
||||
if (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes) {
|
||||
qCDebug(interfaceapp) << "Chose to run the script: " << fileName;
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(fileName);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL;
|
||||
}
|
||||
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
});
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL;
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(scriptFilenameOrURL);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6263,22 +6280,26 @@ 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 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<QMessageBox::StandardButton>(answer.toInt()) == 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;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// json parse error
|
||||
auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\"");
|
||||
|
@ -6351,20 +6372,7 @@ bool Application::askToReplaceDomainContent(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 {
|
||||
|
@ -6945,12 +6953,17 @@ void Application::openUrl(const QUrl& url) const {
|
|||
|
||||
void Application::loadDialog() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
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<ScriptEngines>()->loadScript(fileNameString, true, false, false, true); // Don't load from cache
|
||||
}
|
||||
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<ScriptEngines>()->loadScript(response, true, false, false, true); // Don't load from cache
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QString Application::getPreviousScriptLocation() {
|
||||
|
@ -6963,12 +6976,16 @@ 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");
|
||||
} else if (!newScript.isEmpty()) {
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(newScript.trimmed());
|
||||
}
|
||||
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");
|
||||
} else if (!newScript.isEmpty()) {
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(newScript.trimmed());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Application::loadLODToolsDialog() {
|
||||
|
@ -7085,7 +7102,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7094,7 +7111,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 {
|
||||
|
|
|
@ -429,7 +429,6 @@ private slots:
|
|||
|
||||
bool askToWearAvatarAttachmentUrl(const QString& url);
|
||||
void displayAvatarAttachmentWarning(const QString& message) const;
|
||||
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
||||
|
||||
bool askToReplaceDomainContent(const QString& url);
|
||||
|
||||
|
|
|
@ -106,30 +106,30 @@ void AvatarBookmarks::changeToBookmarkedAvatar() {
|
|||
}
|
||||
|
||||
void AvatarBookmarks::addBookmark() {
|
||||
bool ok = false;
|
||||
auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString(), &ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->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());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
|
||||
});
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
|
||||
|
|
|
@ -58,20 +58,25 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& b
|
|||
Menu* menubar = Menu::getInstance();
|
||||
if (contains(bookmarkName)) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
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<QMessageBox::StandardButton>(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) {
|
||||
|
|
|
@ -74,20 +74,21 @@ void LocationBookmarks::teleportToBookmark() {
|
|||
}
|
||||
|
||||
void LocationBookmarks::addBookmark() {
|
||||
bool ok = false;
|
||||
auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString(), &ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString());
|
||||
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
auto bookmarkName = response.toString();
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
QString bookmarkAddress = addressManager->currentAddress().toString();
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
QString bookmarkAddress = addressManager->currentAddress().toString();
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
|
||||
});
|
||||
}
|
||||
|
||||
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) {
|
||||
|
@ -101,4 +102,4 @@ void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, co
|
|||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole);
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -42,117 +42,161 @@ 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;
|
||||
|
||||
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 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();
|
||||
ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_dialogParent, tr("Select an entity-server content file to migrate"),
|
||||
QString(), tr("Entity-Server Content (*.gz)"));
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
bool isModelURL = (i == 0);
|
||||
quint8 replacementType = i;
|
||||
auto migrationURLString = (isModelURL) ? modelURLString : compoundURLString;
|
||||
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;
|
||||
|
||||
if (!migrationURLString.isEmpty()) {
|
||||
QUrl migrationURL = QUrl(migrationURLString);
|
||||
auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) {
|
||||
auto request =
|
||||
DependencyManager::get<ResourceManager>()->createResourceRequest(this, migrationURL);
|
||||
|
||||
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 (request) {
|
||||
qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration";
|
||||
|
||||
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();
|
||||
}
|
||||
// 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)});
|
||||
|
||||
jsonValue = entityObject;
|
||||
} else if (wantsToMigrateResource(migrationURL)) {
|
||||
auto request =
|
||||
DependencyManager::get<ResourceManager>()->createResourceRequest(this, migrationURL);
|
||||
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();
|
||||
|
||||
if (request) {
|
||||
qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration";
|
||||
checkIfFinished();
|
||||
}
|
||||
request->deleteLater();
|
||||
});
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
} else {
|
||||
_ignoredUrls.insert(migrationURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
request->send();
|
||||
} else {
|
||||
++_errorCount;
|
||||
qWarning() << "Count not create request for asset at" << migrationURL.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_doneReading = true;
|
||||
};
|
||||
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."
|
||||
};
|
||||
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);
|
||||
|
||||
checkIfFinished();
|
||||
|
||||
} else {
|
||||
OffscreenUi::warning(_dialogParent, "Error",
|
||||
"There was a problem loading that entity-server file for ATP asset migration. Please try again");
|
||||
if (QMessageBox::Yes == static_cast<QMessageBox::StandardButton>(answer.toInt())) {
|
||||
// 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::asyncWarning(_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();
|
||||
|
||||
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 (!_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 {
|
||||
|
||||
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."
|
||||
};
|
||||
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<QMessageBox::StandardButton>(answer.toInt()) ==
|
||||
QMessageBox::Yes) {
|
||||
wantsCompleteMigration = true;
|
||||
migrateResources(migrationURL, jsonValue, isModelURL);
|
||||
} else {
|
||||
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<QMessageBox::StandardButton>(answer.toInt()) ==
|
||||
QMessageBox::Yes) {
|
||||
migrateResources(migrationURL, jsonValue, isModelURL);
|
||||
} else {
|
||||
_ignoredUrls.insert(migrationURL);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ATPAssetMigrator::migrateResource(ResourceRequest* request) {
|
||||
|
@ -248,9 +292,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,39 +329,43 @@ 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();
|
||||
ModalDialogListener* dlg = OffscreenUi::getSaveFileNameAsync(_dialogParent, "Save Migrated Entities File");
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
const QString& saveName = response.toString();
|
||||
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::information(_dialogParent, "Success", infoMessage);
|
||||
} else {
|
||||
OffscreenUi::warning(_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));
|
||||
}
|
||||
|
||||
} else {
|
||||
OffscreenUi::warning(_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();
|
||||
});
|
||||
}
|
||||
|
||||
void ATPAssetMigrator::reset() {
|
||||
|
|
|
@ -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'
|
||||
|
@ -123,6 +123,17 @@ QScriptValue WindowScriptingInterface::prompt(const QString& message, const QStr
|
|||
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) {
|
||||
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());
|
||||
});
|
||||
}
|
||||
|
||||
CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) {
|
||||
CustomPromptResult result;
|
||||
bool ok = false;
|
||||
|
@ -191,8 +202,31 @@ QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QSt
|
|||
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
|
||||
/// 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()) {
|
||||
path = getPreviousBrowseLocation();
|
||||
}
|
||||
#ifndef Q_OS_WIN
|
||||
path = fixupPathForMac(directory);
|
||||
#endif
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/// \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`
|
||||
|
@ -213,6 +247,31 @@ QScriptValue WindowScriptingInterface::browse(const QString& title, const QStrin
|
|||
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::browseAsync(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
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
@ -235,7 +294,32 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString&
|
|||
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
|
||||
/// 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()) {
|
||||
path = getPreviousBrowseLocation();
|
||||
}
|
||||
#ifndef Q_OS_WIN
|
||||
path = fixupPathForMac(directory);
|
||||
#endif
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
@ -260,6 +344,35 @@ QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const
|
|||
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::browseAssetsAsync(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 + "/";
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::showAssetServer(const QString& upload) {
|
||||
QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload));
|
||||
}
|
||||
|
|
|
@ -51,12 +51,17 @@ public slots:
|
|||
void raiseMainWindow();
|
||||
void alert(const QString& message = "");
|
||||
QScriptValue confirm(const QString& message = "");
|
||||
QScriptValue 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);
|
||||
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 = "");
|
||||
QString checkVersion();
|
||||
void copyToClipboard(const QString& text);
|
||||
|
@ -89,6 +94,11 @@ signals:
|
|||
void announcement(const QString& message);
|
||||
|
||||
void messageBoxClosed(int id, int button);
|
||||
void browseDirChanged(QString browseDir);
|
||||
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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -91,6 +91,13 @@ QObject* OffscreenUi::getFlags() {
|
|||
return offscreenFlags;
|
||||
}
|
||||
|
||||
void OffscreenUi::removeModalDialog(QObject* modal) {
|
||||
if (modal) {
|
||||
_modalDialogListeners.removeOne(modal);
|
||||
modal->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void OffscreenUi::create() {
|
||||
OffscreenQmlSurface::create();
|
||||
auto myContext = getSurfaceContext();
|
||||
|
@ -145,45 +152,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
|
||||
|
||||
|
@ -204,6 +172,9 @@ private slots:
|
|||
void onSelected(int button) {
|
||||
_result = button;
|
||||
_finished = true;
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
emit response(_result);
|
||||
offscreenUi->removeModalDialog(qobject_cast<QObject*>(this));
|
||||
disconnect(_dialog);
|
||||
}
|
||||
};
|
||||
|
@ -263,6 +234,25 @@ QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& ti
|
|||
return static_cast<QMessageBox::StandardButton>(waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, 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<QObject*>(messageBoxListener);
|
||||
_modalDialogListeners.push_back(modalDialog);
|
||||
return messageBoxListener;
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return DependencyManager::get<OffscreenUi>()->messageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton);
|
||||
|
@ -271,15 +261,36 @@ QMessageBox::StandardButton OffscreenUi::information(const QString& title, const
|
|||
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return DependencyManager::get<OffscreenUi>()->messageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::asyncCritical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return DependencyManager::get<OffscreenUi>()->asyncMessageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::asyncInformation(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return DependencyManager::get<OffscreenUi>()->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<OffscreenUi>()->messageBox(OffscreenUi::Icon::ICON_QUESTION, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
ModalDialogListener *OffscreenUi::asyncQuestion(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return DependencyManager::get<OffscreenUi>()->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<OffscreenUi>()->messageBox(OffscreenUi::Icon::ICON_WARNING, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::asyncWarning(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
|
||||
return DependencyManager::get<OffscreenUi>()->asyncMessageBox(OffscreenUi::Icon::ICON_WARNING, title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
|
||||
class InputDialogListener : public ModalDialogListener {
|
||||
|
@ -296,6 +307,9 @@ class InputDialogListener : public ModalDialogListener {
|
|||
private slots:
|
||||
void onSelected(const QVariant& result) {
|
||||
_result = result;
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
emit response(_result);
|
||||
offscreenUi->removeModalDialog(qobject_cast<QObject*>(this));
|
||||
_finished = true;
|
||||
disconnect(_dialog);
|
||||
}
|
||||
|
@ -349,6 +363,31 @@ QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const
|
|||
return result;
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::getTextAsync(const Icon icon, const QString& title, const QString& label, const QString& text) {
|
||||
return DependencyManager::get<OffscreenUi>()->inputDialogAsync(icon, title, label, text);
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::getItemAsync(const Icon icon, const QString& title, const QString& label, const QStringList& items,
|
||||
int current, bool editable) {
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto inputDialog = offscreenUi->createInputDialog(icon, title, label, current);
|
||||
if (!inputDialog) {
|
||||
return nullptr;
|
||||
}
|
||||
inputDialog->setProperty("items", items);
|
||||
inputDialog->setProperty("editable", editable);
|
||||
|
||||
InputDialogListener* inputDialogListener = new InputDialogListener(inputDialog);
|
||||
offscreenUi->getModalDialogListeners().push_back(qobject_cast<QObject*>(inputDialogListener));
|
||||
|
||||
return inputDialogListener;
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config) {
|
||||
return DependencyManager::get<OffscreenUi>()->customInputDialogAsync(icon, title, config);
|
||||
}
|
||||
|
||||
QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariant result;
|
||||
|
@ -364,6 +403,24 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q
|
|||
return waitForInputDialogResult(createInputDialog(icon, title, label, 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 ret;
|
||||
}
|
||||
|
||||
InputDialogListener* inputDialogListener = new InputDialogListener(createInputDialog(icon, title, label, current));
|
||||
QObject* inputDialog = qobject_cast<QObject*>(inputDialogListener);
|
||||
_modalDialogListeners.push_back(inputDialog);
|
||||
return inputDialogListener;
|
||||
}
|
||||
|
||||
QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, const QVariantMap& config) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariant result;
|
||||
|
@ -384,6 +441,23 @@ QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, c
|
|||
return result;
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
ModalDialogListener* ret;
|
||||
BLOCKING_INVOKE_METHOD(this, "customInputDialogAsync",
|
||||
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<QObject*>(inputDialogListener);
|
||||
_modalDialogListeners.push_back(inputDialog);
|
||||
return inputDialogListener;
|
||||
}
|
||||
|
||||
void OffscreenUi::togglePinned() {
|
||||
bool invokeResult = QMetaObject::invokeMethod(_desktop, "togglePinned");
|
||||
if (!invokeResult) {
|
||||
|
@ -555,6 +629,7 @@ void OffscreenUi::createDesktop(const QUrl& url) {
|
|||
#endif
|
||||
|
||||
load(url, [=](QQmlContext* context, QObject* newObject) {
|
||||
Q_UNUSED(context)
|
||||
_desktop = static_cast<QQuickItem*>(newObject);
|
||||
getSurfaceContext()->setContextProperty("desktop", _desktop);
|
||||
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
||||
|
@ -600,8 +675,11 @@ class FileDialogListener : public ModalDialogListener {
|
|||
|
||||
private slots:
|
||||
void onSelectedFile(QVariant file) {
|
||||
_result = file;
|
||||
_result = file.toUrl().toLocalFile();
|
||||
_finished = true;
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
emit response(_result);
|
||||
offscreenUi->removeModalDialog(qobject_cast<QObject*>(this));
|
||||
disconnect(_dialog);
|
||||
}
|
||||
};
|
||||
|
@ -637,6 +715,35 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) {
|
|||
return result.toUrl().toLocalFile();
|
||||
}
|
||||
|
||||
ModalDialogListener* OffscreenUi::fileDialogAsync(const QVariantMap& properties) {
|
||||
QVariant buildDialogResult;
|
||||
bool invokeResult;
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
TabletProxy* tablet = dynamic_cast<TabletProxy*>(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;
|
||||
}
|
||||
|
||||
FileDialogListener* fileDialogListener = new FileDialogListener(qvariant_cast<QQuickItem*>(buildDialogResult));
|
||||
QObject* fileModalDialog = qobject_cast<QObject*>(fileDialogListener);
|
||||
_modalDialogListeners.push_back(fileModalDialog);
|
||||
|
||||
return fileDialogListener;
|
||||
}
|
||||
|
||||
QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QString result;
|
||||
|
@ -659,6 +766,28 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir,
|
|||
return fileDialog(map);
|
||||
}
|
||||
|
||||
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 ret;
|
||||
}
|
||||
|
||||
// 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<int>(options));
|
||||
return fileDialogAsync(map);
|
||||
}
|
||||
|
||||
QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QString result;
|
||||
|
@ -683,6 +812,30 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir,
|
|||
return fileDialog(map);
|
||||
}
|
||||
|
||||
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 ret;
|
||||
}
|
||||
|
||||
// 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<int>(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;
|
||||
|
@ -705,18 +858,58 @@ QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QStri
|
|||
return fileDialog(map);
|
||||
}
|
||||
|
||||
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_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;
|
||||
map.insert("caption", caption);
|
||||
map.insert("dir", QUrl::fromLocalFile(dir));
|
||||
map.insert("filter", filter);
|
||||
map.insert("options", static_cast<int>(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) {
|
||||
Q_UNUSED(ignored)
|
||||
return DependencyManager::get<OffscreenUi>()->fileOpenDialog(caption, dir, filter, selectedFilter, 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<OffscreenUi>()->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<OffscreenUi>()->fileSaveDialog(caption, dir, filter, selectedFilter, 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<OffscreenUi>()->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<OffscreenUi>()->existingDirectoryDialog(caption, dir, filter, selectedFilter, 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<OffscreenUi>()->existingDirectoryDialogAsync(caption, dir, filter, selectedFilter, options);
|
||||
}
|
||||
|
||||
class AssetDialogListener : public ModalDialogListener {
|
||||
// ATP equivalent of FileDialogListener.
|
||||
Q_OBJECT
|
||||
|
@ -732,6 +925,9 @@ class AssetDialogListener : public ModalDialogListener {
|
|||
private slots:
|
||||
void onSelectedAsset(QVariant asset) {
|
||||
_result = asset;
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
emit response(_result);
|
||||
offscreenUi->removeModalDialog(qobject_cast<QObject*>(this));
|
||||
_finished = true;
|
||||
disconnect(_dialog);
|
||||
}
|
||||
|
@ -769,6 +965,39 @@ QString OffscreenUi::assetDialog(const QVariantMap& properties) {
|
|||
return result.toUrl().toString();
|
||||
}
|
||||
|
||||
ModalDialogListener *OffscreenUi::assetDialogAsync(const QVariantMap& properties) {
|
||||
// ATP equivalent of fileDialog().
|
||||
QVariant buildDialogResult;
|
||||
bool invokeResult;
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
TabletProxy* tablet = dynamic_cast<TabletProxy*>(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 nullptr;
|
||||
}
|
||||
|
||||
AssetDialogListener* assetDialogListener = new AssetDialogListener(qvariant_cast<QQuickItem*>(buildDialogResult));
|
||||
QObject* assetModalDialog = qobject_cast<QObject*>(assetDialogListener);
|
||||
_modalDialogListeners.push_back(assetModalDialog);
|
||||
return assetDialogListener;
|
||||
}
|
||||
|
||||
QList<QObject *> &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()) {
|
||||
|
@ -792,11 +1021,41 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir,
|
|||
return assetDialog(map);
|
||||
}
|
||||
|
||||
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 ret;
|
||||
}
|
||||
|
||||
// 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<int>(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().
|
||||
Q_UNUSED(ignored)
|
||||
return DependencyManager::get<OffscreenUi>()->assetOpenDialog(caption, dir, filter, selectedFilter, 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<OffscreenUi>()->assetOpenDialogAsync(caption, dir, filter, selectedFilter, options);
|
||||
}
|
||||
|
||||
bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
|
||||
if (!filterEnabled(originalDestination, event)) {
|
||||
return false;
|
||||
|
@ -836,5 +1095,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"
|
||||
|
|
|
@ -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,6 +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 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
|
||||
|
@ -88,18 +110,39 @@ public:
|
|||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) {
|
||||
return information(title, text, buttons, defaultButton);
|
||||
}
|
||||
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 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);
|
||||
}
|
||||
/// 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,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) {
|
||||
return question(title, text, buttons, defaultButton);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/// 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 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);
|
||||
}
|
||||
|
||||
static QMessageBox::StandardButton critical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
|
@ -107,30 +150,55 @@ public:
|
|||
static QMessageBox::StandardButton information(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
static ModalDialogListener* asyncCritical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
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 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 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 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 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 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 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 QString getOpenFileName(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 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 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 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 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 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);
|
||||
|
@ -148,21 +216,47 @@ public:
|
|||
return getItem(OffscreenUi::ICON_NONE, title, label, items, current, editable, ok);
|
||||
}
|
||||
|
||||
// Compatibility with QInputDialog::getText
|
||||
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 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);
|
||||
}
|
||||
|
||||
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 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<QObject *> &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);
|
||||
|
||||
private:
|
||||
QString fileDialog(const QVariantMap& properties);
|
||||
ModalDialogListener *fileDialogAsync(const QVariantMap &properties);
|
||||
QString assetDialog(const QVariantMap& properties);
|
||||
ModalDialogListener* assetDialogAsync(const QVariantMap& properties);
|
||||
|
||||
QQuickItem* _desktop { nullptr };
|
||||
QQuickItem* _toolWindow { nullptr };
|
||||
QList<QObject*> _modalDialogListeners;
|
||||
std::unordered_map<int, bool> _pressedKeys;
|
||||
VrMenu* _vrMenu { nullptr };
|
||||
QQueue<std::function<void(VrMenu*)>> _queuedMenuInitializers;
|
||||
|
|
|
@ -433,17 +433,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.browseAsync("Select Model to Import", "", "*.json");
|
||||
});
|
||||
|
||||
addButton("openAssetBrowserButton", "assets-01.svg", function() {
|
||||
|
@ -1461,6 +1452,39 @@ 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 onFileOpenChanged(filename) {
|
||||
var importURL = null;
|
||||
if (filename !== "") {
|
||||
importURL = "file:///" + filename;
|
||||
}
|
||||
if (importURL) {
|
||||
if (!isActive && (Entities.canRez() && Entities.canRezTmp())) {
|
||||
toolBar.toggle();
|
||||
}
|
||||
importSVO(importURL);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -1478,30 +1502,16 @@ 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.saveAsync("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.browseAsync("Select Model to Import", "", "*.json");
|
||||
} else {
|
||||
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.promptAsync("URL of SVO to import", "");
|
||||
}
|
||||
} else if (menuItem === "Entity List...") {
|
||||
entityListTool.toggleVisible();
|
||||
|
|
|
@ -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.saveAsync("Select Where to Save", "", "*.json");
|
||||
}
|
||||
} else if (data.type == "pal") {
|
||||
var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates.
|
||||
|
|
|
@ -113,15 +113,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.browseDirAsync("Choose Snapshots Directory", "", "");
|
||||
break;
|
||||
case 'openSettings':
|
||||
if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false))
|
||||
|
@ -572,6 +565,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);
|
||||
|
|
|
@ -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.browseAssetsAsync("Select Recording to Play", "recordings", "*.hfr");
|
||||
break;
|
||||
case START_RECORDING_ACTION:
|
||||
// Start making a recording.
|
||||
|
|
Loading…
Reference in a new issue