780 lines · 27.5 KB · C++
RawDownload
////  Created by Anthony J. Thibault on 2016-12-12//  Copyright 2013-2016 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "TabletScriptingInterface.h" #include <QtCore/QThread> #include <AccountManager.h>#include "DependencyManager.h"#include <PathUtils.h>#include <QmlWindowClass.h>#include <QQmlProperty>#include <RegisteredMetaTypes.h>#include "ScriptEngineLogging.h"#include <OffscreenUi.h>#include <InfoView.h>#include "SoundEffect.h"  QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in) {    return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);} void tabletFromScriptValue(const QScriptValue& value, TabletProxy* &out) {    out = qobject_cast<TabletProxy*>(value.toQObject());} TabletScriptingInterface::TabletScriptingInterface() {    qmlRegisterType<SoundEffect>("Hifi", 1, 0, "SoundEffect");} QObject* TabletScriptingInterface::getSystemToolbarProxy() {    const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";    Qt::ConnectionType connectionType = Qt::AutoConnection;    if (QThread::currentThread() != _toolbarScriptingInterface->thread()) {        connectionType = Qt::BlockingQueuedConnection;    }    QObject* toolbarProxy = nullptr;    bool hasResult = QMetaObject::invokeMethod(_toolbarScriptingInterface, "getToolbar", connectionType, Q_RETURN_ARG(QObject*, toolbarProxy), Q_ARG(QString, SYSTEM_TOOLBAR));    if (hasResult) {        return toolbarProxy;    } else {        qCWarning(scriptengine) << "ToolbarScriptingInterface getToolbar has no result";        return nullptr;    }} TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) {     std::lock_guard<std::mutex> guard(_mutex);     // look up tabletId in the map.    auto iter = _tabletProxies.find(tabletId);    if (iter != _tabletProxies.end()) {        // tablet already exists, just return it.        return iter->second;    } else {        // allocate a new tablet, add it to the map then return it.        auto tabletProxy = new TabletProxy(tabletId);        tabletProxy->setParent(this);        _tabletProxies[tabletId] = tabletProxy;        tabletProxy->setToolbarMode(_toolbarMode);        return tabletProxy;    }} void TabletScriptingInterface::setToolbarMode(bool toolbarMode) {    std::lock_guard<std::mutex> guard(_mutex);     _toolbarMode = toolbarMode;     for (auto& iter : _tabletProxies) {        iter.second->setToolbarMode(toolbarMode);    }} void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) {    TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet(tabletId));    if (tablet) {        tablet->setQmlTabletRoot(qmlTabletRoot, qmlOffscreenSurface);    } else {        qCWarning(scriptengine) << "TabletScriptingInterface::setupTablet() bad tablet object";    }} QQuickWindow* TabletScriptingInterface::getTabletWindow() {    TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet("com.highfidelity.interface.tablet.system"));    QObject* qmlSurface = tablet->getTabletSurface();    OffscreenQmlSurface* surface = dynamic_cast<OffscreenQmlSurface*>(qmlSurface);     if (!surface) {        return nullptr;    }    QQuickWindow* window = surface->getWindow();    return window;} void TabletScriptingInterface::processMenuEvents(QObject* object, const QKeyEvent* event) {    switch (event->key()) {        case Qt::Key_Down:            QMetaObject::invokeMethod(object, "nextItem");            break;         case Qt::Key_Up:            QMetaObject::invokeMethod(object, "previousItem");            break;         case Qt::Key_Left:            QMetaObject::invokeMethod(object, "previousPage");            break;         case Qt::Key_Right:            QMetaObject::invokeMethod(object, "selectCurrentItem");            break;         case Qt::Key_Return:            QMetaObject::invokeMethod(object, "selectCurrentItem");            break;         default:            break;    }} void TabletScriptingInterface::processTabletEvents(QObject* object, const QKeyEvent* event) {    switch (event->key()) {        case Qt::Key_Down:            QMetaObject::invokeMethod(object, "downItem");            break;         case Qt::Key_Up:            QMetaObject::invokeMethod(object, "upItem");            break;         case Qt::Key_Left:            QMetaObject::invokeMethod(object, "previousItem");            break;         case Qt::Key_Right:            QMetaObject::invokeMethod(object, "nextItem");            break;         case Qt::Key_Return:            QMetaObject::invokeMethod(object, "selectItem");            break;         default:            break;    }}  void TabletScriptingInterface::processEvent(const QKeyEvent* event) {    TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet("com.highfidelity.interface.tablet.system"));    QObject* qmlTablet = tablet->getQmlTablet();    QObject* qmlMenu = tablet->getQmlMenu();     if (qmlTablet) {        processTabletEvents(qmlTablet, event);    } else if (qmlMenu) {        processMenuEvents(qmlMenu, event);    }} QObject* TabletScriptingInterface::getFlags(){    auto offscreenUi = DependencyManager::get<OffscreenUi>();    return offscreenUi->getFlags();} //// TabletProxy// static const char* TABLET_SOURCE_URL = "Tablet.qml";static const char* WEB_VIEW_SOURCE_URL = "TabletWebView.qml";static const char* VRMENU_SOURCE_URL = "TabletMenu.qml"; class TabletRootWindow : public QmlWindowClass {    virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; }}; TabletProxy::TabletProxy(QString name) : _name(name) {} void TabletProxy::setToolbarMode(bool toolbarMode) {    if (toolbarMode == _toolbarMode) {        return;    }     _toolbarMode = toolbarMode;     if (toolbarMode) {        removeButtonsFromHomeScreen();        addButtonsToToolbar();         // create new desktop window        auto offscreenUi = DependencyManager::get<OffscreenUi>();        offscreenUi->executeOnUiThread([=] {            auto tabletRootWindow = new TabletRootWindow();            tabletRootWindow->initQml(QVariantMap());            auto quickItem = tabletRootWindow->asQuickItem();            _desktopWindow = tabletRootWindow;            QMetaObject::invokeMethod(quickItem, "setShown", Q_ARG(const QVariant&, QVariant(false)));             QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed()));             QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);             // forward qml surface events to interface js            connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);        });    } else {        removeButtonsFromToolbar();        addButtonsToHomeScreen();         // destroy desktop window        if (_desktopWindow) {            _desktopWindow->deleteLater();            _desktopWindow = nullptr;        }    }} static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) {    QVariant resultVar;    Qt::ConnectionType connectionType = Qt::AutoConnection;    if (QThread::currentThread() != qmlTablet->thread()) {        connectionType = Qt::BlockingQueuedConnection;    }    bool hasResult = QMetaObject::invokeMethod(qmlTablet, "addButtonProxy", connectionType,                                               Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, buttonProxy->getProperties()));    if (!hasResult) {        qCWarning(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet has no result";        return;    }     QObject* qmlButton = qvariant_cast<QObject *>(resultVar);    if (!qmlButton) {        qCWarning(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet result not a QObject";        return;    }    QObject::connect(qmlButton, SIGNAL(clicked()), buttonProxy, SLOT(clickedSlot()));    buttonProxy->setQmlButton(qobject_cast<QQuickItem*>(qmlButton));} static QString getUsername() {    QString username = "Unknown user";    auto accountManager = DependencyManager::get<AccountManager>();    if (accountManager->isLoggedIn()) {        return accountManager->getAccountInfo().getUsername();    } else {        return "Unknown user";    }} void TabletProxy::initialScreen(const QVariant& url) {    if (_qmlTabletRoot) {        pushOntoStack(url);    } else {        _initialScreen = true;        _initialPath = url;    }} bool TabletProxy::isMessageDialogOpen() {    if (_qmlTabletRoot) {        QVariant result;        QMetaObject::invokeMethod(_qmlTabletRoot, "isDialogOpen",Qt::DirectConnection,                                  Q_RETURN_ARG(QVariant, result));         return result.toBool();    }     return false;} void TabletProxy::emitWebEvent(QVariant msg) {    emit webEventReceived(msg);} bool TabletProxy::isPathLoaded(QVariant path) {    return path.toString() == _currentPathLoaded.toString();}void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) {    std::lock_guard<std::mutex> guard(_mutex);    _qmlOffscreenSurface = qmlOffscreenSurface;    _qmlTabletRoot = qmlTabletRoot;    if (_qmlTabletRoot && _qmlOffscreenSurface) {         QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);         // forward qml surface events to interface js        connect(dynamic_cast<OffscreenQmlSurface*>(_qmlOffscreenSurface), &OffscreenQmlSurface::fromQml, [this](QVariant message) {            if (message.canConvert<QJSValue>()) {                emit fromQml(qvariant_cast<QJSValue>(message).toVariant());            } else if (message.canConvert<QString>()) {                emit fromQml(message.toString());            } else {                qWarning() << "fromQml: Unsupported message type " << message;            }        });         if (_toolbarMode) {            // if someone creates the tablet in toolbar mode, make sure to display the home screen on the tablet.            auto loader = _qmlTabletRoot->findChild<QQuickItem*>("loader");            QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()), Qt::DirectConnection);            QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL)));        }         // force to the tablet to go to the homescreen        loadHomeScreen(true);         QMetaObject::invokeMethod(_qmlTabletRoot, "setUsername", Q_ARG(const QVariant&, QVariant(getUsername())));         // hook up username changed signal.        auto accountManager = DependencyManager::get<AccountManager>();        QObject::connect(accountManager.data(), &AccountManager::profileChanged, [this]() {            if (_qmlTabletRoot) {                QMetaObject::invokeMethod(_qmlTabletRoot, "setUsername", Q_ARG(const QVariant&, QVariant(getUsername())));            }        });         if (_initialScreen) {            pushOntoStack(_initialPath);            _initialScreen = false;        }    } else {        removeButtonsFromHomeScreen();        _state = State::Uninitialized;        emit screenChanged(QVariant("Closed"), QVariant(""));        _currentPathLoaded = "";    }} void TabletProxy::gotoHomeScreen() {    loadHomeScreen(false);}void TabletProxy::gotoMenuScreen(const QString& submenu) {     QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        removeButtonsFromHomeScreen();        auto offscreenUi = DependencyManager::get<OffscreenUi>();        QObject* menu = offscreenUi->getRootMenu();        QMetaObject::invokeMethod(root, "setMenuProperties", Q_ARG(QVariant, QVariant::fromValue(menu)), Q_ARG(const QVariant&, QVariant(submenu)));        QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL)));        _state = State::Menu;        emit screenChanged(QVariant("Menu"), QVariant(VRMENU_SOURCE_URL));        _currentPathLoaded = VRMENU_SOURCE_URL;        QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));    }} void TabletProxy::loadQMLOnTop(const QVariant& path) {    QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, path));        QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));    } else {        qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null";    }} void TabletProxy::returnToPreviousApp() {    QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        QMetaObject::invokeMethod(root, "returnToPreviousApp");        QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));    } else {        qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null";    }}    void TabletProxy::loadQMLSource(const QVariant& path) {     QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        if (_state != State::QML) {            removeButtonsFromHomeScreen();            QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path));            _state = State::QML;            emit screenChanged(QVariant("QML"), path);            _currentPathLoaded = path;            QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));        }    } else {        qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null";    }} void TabletProxy::pushOntoStack(const QVariant& path) {    QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        auto stack = root->findChild<QQuickItem*>("stack");        if (stack) {            QMetaObject::invokeMethod(stack, "pushSource", Q_ARG(const QVariant&, path));        } else {            loadQMLSource(path);        }    } else {        qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null";    }} void TabletProxy::popFromStack() {    QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        auto stack = root->findChild<QQuickItem*>("stack");        QMetaObject::invokeMethod(stack, "popSource");    } else {        qCDebug(scriptengine) << "tablet cannot pop QML because _qmlTabletRoot or _desktopWindow is null";    }} void TabletProxy::loadHomeScreen(bool forceOntoHomeScreen) {    if ((_state != State::Home && _state != State::Uninitialized) || forceOntoHomeScreen) {        if (!_toolbarMode && _qmlTabletRoot) {            auto loader = _qmlTabletRoot->findChild<QQuickItem*>("loader");            QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()), Qt::DirectConnection);            QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL)));            QMetaObject::invokeMethod(_qmlTabletRoot, "playButtonClickSound");        } else if (_toolbarMode && _desktopWindow) {            // close desktop window            if (_desktopWindow->asQuickItem()) {                QMetaObject::invokeMethod(_desktopWindow->asQuickItem(), "setShown", Q_ARG(const QVariant&, QVariant(false)));            }        }        _state = State::Home;        emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));        _currentPathLoaded = TABLET_SOURCE_URL;    }} void TabletProxy::gotoWebScreen(const QString& url) {    gotoWebScreen(url, "");} void TabletProxy::loadWebScreenOnTop(const QVariant& url) {    loadWebScreenOnTop(url, "");} void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJavaScriptUrl) {    QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL)));        QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));        QMetaObject::invokeMethod(root, "loadWebOnTop", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectJavaScriptUrl)));    }    _state = State::Web;} void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl) {

Lines 1–500 of 780.