674 lines · 26.6 KB · C++
RawDownload
////  WindowScriptingInterface.cpp//  interface/src/scripting////  Created by Ryan Huffman on 4/29/14.//  Copyright 2014 High Fidelity, Inc.//  Copyright 2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "WindowScriptingInterface.h" #include <QClipboard>#include <QtCore/QDir>#include <QMessageBox>#include <ScriptValue.h>#include <QtGui/QDesktopServices>#include <shared/QtHelpers.h>#include <SettingHandle.h> #include <plugins/PluginManager.h>#include <display-plugins/CompositorHelper.h>#include <AddressManager.h>#include "AndroidHelper.h"#include "Application.h"#include "DomainHandler.h"#include "MainWindow.h"#include "Menu.h"#include "OffscreenUi.h"#include "NetworkingConstants.h" static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";static const QString LAST_BROWSE_ASSETS_LOCATION_SETTING = "LastBrowseAssetsLocation";  WindowScriptingInterface::WindowScriptingInterface() {    const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();    connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);    connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &WindowScriptingInterface::disconnectedFromDomain);     connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);    connect(&domainHandler, &DomainHandler::redirectErrorStateChanged, this, &WindowScriptingInterface::redirectErrorStateChanged);     connect(qApp, &Application::svoImportRequested, [this](const QString& urlString) {        static const QMetaMethod svoImportRequestedSignal =            QMetaMethod::fromSignal(&WindowScriptingInterface::svoImportRequested);         if (isSignalConnected(svoImportRequestedSignal)) {            QUrl url(urlString);            emit svoImportRequested(url.url());        } else {            OffscreenUi::asyncWarning("Import SVO Error", "You need to be running edit.js to import entities.");        }    });     connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &WindowScriptingInterface::onWindowGeometryChanged);    connect(qApp->getWindow(), &MainWindow::windowMinimizedChanged, this, &WindowScriptingInterface::minimizedChanged);    connect(qApp, &Application::interstitialModeChanged, [this] (bool interstitialMode) {        emit interstitialModeChanged(interstitialMode);    });} WindowScriptingInterface::~WindowScriptingInterface() {    QHashIterator<int, QQuickItem*> i(_messageBoxes);    while (i.hasNext()) {        i.next();        auto messageBox = i.value();        disconnect(messageBox);        messageBox->setVisible(false);        messageBox->deleteLater();    }     _messageBoxes.clear();} ScriptValue WindowScriptingInterface::hasFocus() {    Q_ASSERT(engine);    return engine()->newValue(qApp->hasFocus());} void WindowScriptingInterface::setFocus() {    // It's forbidden to call focus() from another thread.    qApp->postLambdaEvent([] {        qApp->setFocus();    });} void WindowScriptingInterface::raise() {    // It's forbidden to call raise() from another thread.    qApp->postLambdaEvent([] {        qApp->raise();    });} /// Display an alert box/// \param const QString& message message to display/// \return ScriptValue::UndefinedValuevoid WindowScriptingInterface::alert(const QString& message) {    OffscreenUi::asyncWarning("", message, QMessageBox::Ok, QMessageBox::Ok);} /// Display a confirmation box with the options 'Yes' and 'No'/// \param const QString& message message to display/// \return ScriptValue `true` if 'Yes' was clicked, `false` otherwiseScriptValue WindowScriptingInterface::confirm(const QString& message) {    Q_ASSERT(engine);    return engine()->newValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)));} /// Display a prompt with a text box/// \param const QString& message message to display/// \param const QString& defaultText default text in the text box/// \return ScriptValue string text value in text box if the dialog was accepted, `null` otherwise.ScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) {    QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText);    Q_ASSERT(engine);    auto sResult = engine()->newValue(result);    if (sResult.equals(engine()->newValue(""))) {        return engine()->nullValue();    }    return sResult;} /// Display a prompt with a text box/// \param const QString& message message to display/// \param const QString& defaultText default text in the text boxvoid WindowScriptingInterface::promptAsync(const QString& message, const QString& defaultText) {    bool ok = false;    ModalDialogListener* dlg = OffscreenUi::getTextAsync(nullptr, "", message, QLineEdit::Normal, defaultText, &ok);    connect(dlg, &ModalDialogListener::response, this, [=] (QVariant result) {        disconnect(dlg, &ModalDialogListener::response, this, nullptr);        emit promptTextChanged(result.toString());    });} void WindowScriptingInterface::disconnectedFromDomain() {    emit domainChanged(QUrl());} void WindowScriptingInterface::openUrl(const QUrl& url) {    if (!url.isEmpty()) {        auto scheme = url.scheme();        if (scheme == URL_SCHEME_OVERTE) {            DependencyManager::get<AddressManager>()->handleLookupString(url.toString());        } else {#if defined(Q_OS_ANDROID)            QMap<QString, QString> args;            args["url"] = url.toString();            AndroidHelper::instance().requestActivity("WebView", true, args);#else            QDesktopServices::openUrl(url);#endif        }    }} void WindowScriptingInterface::openAndroidActivity(const QString& activityName, const bool backToScene) {#if defined(Q_OS_ANDROID)    AndroidHelper::instance().requestActivity(activityName, backToScene);#endif}  QString fixupPathForMac(const QString& directory) {    // On OS X `directory` does not work as expected unless a file is included in the path, so we append a bogus    // filename if the directory is valid.    QString path = "";    QFileInfo fileInfo = QFileInfo(directory);    if (fileInfo.isDir()) {        fileInfo.setFile(directory, "__HIFI_INVALID_FILE__");        path = fileInfo.filePath();    }    return path;} QString WindowScriptingInterface::getPreviousBrowseLocation() const {    return Setting::Handle<QString>(LAST_BROWSE_LOCATION_SETTING, DESKTOP_LOCATION).get();} void WindowScriptingInterface::setPreviousBrowseLocation(const QString& location) {    Setting::Handle<QVariant>(LAST_BROWSE_LOCATION_SETTING).set(location);} QString WindowScriptingInterface::getPreviousBrowseAssetLocation() const {    QString ASSETS_ROOT_PATH = "/";    return Setting::Handle<QString>(LAST_BROWSE_ASSETS_LOCATION_SETTING, ASSETS_ROOT_PATH).get();} void WindowScriptingInterface::setPreviousBrowseAssetLocation(const QString& location) {    Setting::Handle<QVariant>(LAST_BROWSE_ASSETS_LOCATION_SETTING).set(location);} bool WindowScriptingInterface::getInterstitialModeEnabled() const {    return DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();} void WindowScriptingInterface::setInterstitialModeEnabled(bool enableInterstitialMode) {    DependencyManager::get<NodeList>()->getDomainHandler().setInterstitialModeEnabled(enableInterstitialMode);} bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {    auto offscreenUI = DependencyManager::get<OffscreenUi>();    return offscreenUI ? offscreenUI->isPointOnDesktopWindow(point) : false;} /// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and/// might be in same thread as a script that sets the reticle to invisiblevoid WindowScriptingInterface::ensureReticleVisible() const {    auto compositorHelper = DependencyManager::get<CompositorHelper>();    if (!compositorHelper->getReticleVisible()) {        compositorHelper->setReticleVisible(true);    }} /// 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 directory browser at/// \return ScriptValue file path as a string if one was selected, otherwise `ScriptValue::NullValue`ScriptValue WindowScriptingInterface::browseDir(const QString& title, const QString& directory) {    ensureReticleVisible();    QString path = directory;    if (path.isEmpty()) {        path = getPreviousBrowseLocation();    }#ifndef Q_OS_WIN    path = fixupPathForMac(directory);#endif    QString result = OffscreenUi::getExistingDirectory(nullptr, title, path);    if (!result.isEmpty()) {        setPreviousBrowseLocation(QFileInfo(result).absolutePath());    }    Q_ASSERT(engine);    return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);} /// Display a "browse to directory" dialog.  If `directory` is an invalid file or directory the browser will start at the current/// working directory./// \param const QString& title title of the window/// \param const QString& directory directory to start the directory browser atvoid 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`/// \return ScriptValue file path as a string if one was selected, otherwise `ScriptValue::NullValue`ScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) {    ensureReticleVisible();    QString path = directory;    if (path.isEmpty()) {        path = getPreviousBrowseLocation();    }#ifndef Q_OS_WIN    path = fixupPathForMac(directory);#endif    QString result = OffscreenUi::getOpenFileName(nullptr, title, path, nameFilter);    if (!result.isEmpty()) {        setPreviousBrowseLocation(QFileInfo(result).absolutePath());    }    Q_ASSERT(engine);    return result.isEmpty() ? engine()->nullValue() : engine()->newValue(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 browseChanged(result);    });} /// Display a save file dialog.  If `directory` is an invalid file or directory the browser will start at the current/// working directory./// \param const QString& title title of the window/// \param const QString& directory directory to start the file browser at/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`/// \return ScriptValue file path as a string if one was selected, otherwise `ScriptValue::NullValue`ScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) {    ensureReticleVisible();    QString path = directory;    if (path.isEmpty()) {        path = getPreviousBrowseLocation();    }#ifndef Q_OS_WIN    path = fixupPathForMac(directory);#endif    QString result = OffscreenUi::getSaveFileName(nullptr, title, path, nameFilter);    if (!result.isEmpty()) {        setPreviousBrowseLocation(QFileInfo(result).absolutePath());    }    Q_ASSERT(engine);    return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);} /// Display a save file dialog.  If `directory` is an invalid file or directory the browser will start at the current/// working directory./// \param const QString& title title of the window/// \param const QString& directory directory to start the file browser at/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`void WindowScriptingInterface::saveAsync(const QString& title, const QString& directory, const QString& nameFilter) {    ensureReticleVisible();    QString path = directory;    if (path.isEmpty()) {        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/// \param const QString& nameFilter filter to filter asset names by - see `QFileDialog`/// \return ScriptValue asset path as a string if one was selected, otherwise `ScriptValue::NullValue`ScriptValue WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) {    ensureReticleVisible();    QString path = directory;    if (path.isEmpty()) {        path = getPreviousBrowseAssetLocation();    }    if (path.left(1) != "/") {        path = "/" + path;    }    if (path.right(1) != "/") {        path = path + "/";    }    QString result = OffscreenUi::getOpenAssetName(nullptr, title, path, nameFilter);    if (!result.isEmpty()) {        setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath());    }    Q_ASSERT(engine);    return result.isEmpty() ? engine()->nullValue() : engine()->newValue(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));} QString WindowScriptingInterface::checkVersion() {    return QCoreApplication::applicationVersion();} QString WindowScriptingInterface::getUserAgent() {    return NetworkingConstants::OVERTE_USER_AGENT;} QString WindowScriptingInterface::protocolSignature() {    return protocolVersionsSignatureBase64();} int WindowScriptingInterface::getInnerWidth() {    return qApp->getPrimaryWidget()->geometry().width();} int WindowScriptingInterface::getInnerHeight() {    return qApp->getPrimaryWidget()->geometry().height();} glm::vec2 WindowScriptingInterface::getDeviceSize() const {    return qApp->getDeviceSize();} int WindowScriptingInterface::getLastDomainConnectionError() const {    return DependencyManager::get<NodeList>()->getDomainHandler().getLastDomainConnectionError();} int WindowScriptingInterface::getX() {    return qApp->getWindow()->geometry().x();} int WindowScriptingInterface::getY() {    auto menu = qApp->getPrimaryMenu();    int menuHeight = menu ? menu->geometry().height() : 0;    return qApp->getWindow()->geometry().y() + menuHeight;} void WindowScriptingInterface::onWindowGeometryChanged(const QRect& windowGeometry) {    auto geometry = windowGeometry;    auto menu = qApp->getPrimaryMenu();    if (menu) {        geometry.setY(geometry.y() + menu->geometry().height());    }    emit geometryChanged(geometry);} void WindowScriptingInterface::copyToClipboard(const QString& text) {    if (QThread::currentThread() != qApp->thread()) {        QMetaObject::invokeMethod(this, "copyToClipboard", Q_ARG(QString, text));        return;    }     qDebug() << "Copying";    QApplication::clipboard()->setText(text);}  bool WindowScriptingInterface::setDisplayTexture(const QString& name) {    return  qApp->getActiveDisplayPlugin()->setDisplayTexture(name);   // Plugins that don't know how, answer false.} void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {    qApp->takeSnapshot(notify, includeAnimated, aspectRatio, filename);} void WindowScriptingInterface::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) {    qApp->takeSecondaryCameraSnapshot(notify, filename);} void WindowScriptingInterface::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {    qApp->takeSecondaryCamera360Snapshot(cameraPosition, cubemapOutputFormat, notify, filename);} void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& href) {    qApp->shareSnapshot(path, href);} void WindowScriptingInterface::makeConnection(bool success, const QString& userNameOrError) {    if (success) {        emit connectionAdded(userNameOrError);    } else {        emit connectionError(userNameOrError);

Lines 1–500 of 674.