694 lines · 25.1 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" 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;    }} QObject* 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.data();    } else {        // allocate a new tablet, add it to the map then return it.        auto tabletProxy = QSharedPointer<TabletProxy>(new TabletProxy(tabletId));        _tabletProxies[tabletId] = tabletProxy;        tabletProxy->setToolbarMode(_toolbarMode);        return tabletProxy.data();    }} 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, SIGNAL(webEventReceived(QVariant)));             // 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";    }} 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::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, SIGNAL(webEventReceived(QVariant)));         // 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())));            }        });    } else {        removeButtonsFromHomeScreen();        _state = State::Uninitialized;        emit screenChanged(QVariant("Closed"), QVariant(""));    }} 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));        QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));    }} 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);            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) {    if (_qmlTabletRoot) {        auto stack = _qmlTabletRoot->findChild<QQuickItem*>("stack");        if (stack) {            QMetaObject::invokeMethod(stack, "pushSource", Q_ARG(const QVariant&, path));        } else {            qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot doesn't have child stack";        }    } else if (_desktopWindow) {        auto stack = _desktopWindow->asQuickItem()->findChild<QQuickItem*>("stack");        if (stack) {            QMetaObject::invokeMethod(stack, "pushSource", Q_ARG(const QVariant&, path));        } else {            qCDebug(scriptengine) << "tablet cannot push QML because _desktopWindow doesn't have child stack";        }    } else {        qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null";    }} void TabletProxy::popFromStack() {    if (_qmlTabletRoot) {        auto stack = _qmlTabletRoot->findChild<QQuickItem*>("stack");        if (stack) {            QMetaObject::invokeMethod(stack, "popSource");        } else {            qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot doesn't have child stack";        }    } else if (_desktopWindow) {        auto stack = _desktopWindow->asQuickItem()->findChild<QQuickItem*>("stack");        if (stack) {            QMetaObject::invokeMethod(stack, "popSource");        } else {             qCDebug(scriptengine) << "tablet cannot pop QML because _desktopWindow doesn't have child stack";        }    } 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));    }} void TabletProxy::gotoWebScreen(const QString& url) {    gotoWebScreen(url, "");} void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl) {     QObject* root = nullptr;    if (!_toolbarMode && _qmlTabletRoot) {        root = _qmlTabletRoot;    } else if (_toolbarMode && _desktopWindow) {        root = _desktopWindow->asQuickItem();    }     if (root) {        removeButtonsFromHomeScreen();        QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL)));        QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));        QMetaObject::invokeMethod(root, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl)));    }    _state = State::Web;    emit screenChanged(QVariant("Web"), QVariant(url));} QObject* TabletProxy::addButton(const QVariant& properties) {    auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));    std::lock_guard<std::mutex> guard(_mutex);    _tabletButtonProxies.push_back(tabletButtonProxy);    if (!_toolbarMode && _qmlTabletRoot) {        auto tablet = getQmlTablet();        if (tablet) {            addButtonProxyToQmlTablet(tablet, tabletButtonProxy.data());        } else {            qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";        }    } else if (_toolbarMode) {         auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();        QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();         Qt::ConnectionType connectionType = Qt::AutoConnection;        if (QThread::currentThread() != toolbarProxy->thread()) {            connectionType = Qt::BlockingQueuedConnection;        }         // copy properties from tablet button proxy to toolbar button proxy.        QObject* toolbarButtonProxy = nullptr;        bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties()));        if (hasResult) {            tabletButtonProxy->setToolbarButtonProxy(toolbarButtonProxy);        } else {            qCWarning(scriptengine) << "ToolbarProxy addButton has no result";        }    }    return tabletButtonProxy.data();} bool TabletProxy::onHomeScreen() {    return _state == State::Home;} void TabletProxy::removeButton(QObject* tabletButtonProxy) {    std::lock_guard<std::mutex> guard(_mutex);     auto tablet = getQmlTablet();    if (!tablet) {        qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";    }     auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy);    if (iter != _tabletButtonProxies.end()) {        if (!_toolbarMode && _qmlTabletRoot) {            (*iter)->setQmlButton(nullptr);            if (tablet) {                QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getProperties()));            }        } else if (_toolbarMode) {            auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();            QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();             // remove button from toolbarProxy            QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getUuid().toString()));            (*iter)->setToolbarButtonProxy(nullptr);        }        _tabletButtonProxies.erase(iter);    } else {        qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;    }}

Lines 1–500 of 694.