1212 lines · 47.2 KB · C++
RawDownload
////  OffscreenUi.cpp//  interface/src/render-utils////  Created by Bradley Austin Davis on 2015-04-04//  Copyright 2015 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 "OffscreenUi.h" #include <QtCore/QVariant>#include <QtGui/QGuiApplication>#include <QtQuick/QQuickWindow>#include <QtQml/QtQml> #include <shared/QtHelpers.h>#include <gl/GLHelpers.h> #include <AbstractUriHandler.h>#include <AccountManager.h>#include <DependencyManager.h> #include "ui/TabletScriptingInterface.h"#include "FileDialogHelper.h"#include "VrMenu.h" #include "ui/Logging.h"#include "ui/ToolbarScriptingInterface.h" #include <PointerManager.h>#include "MainWindow.h" /*@jsdoc * The <code>OffscreenFlags</code> API enables gamepad joystick navigation of UI. * * <p><em>This API currently has no effect and is not used.</em></p> * * @namespace OffscreenFlags * * @hifi-interface * @hifi-client-entity * @hifi-avatar * * @property {boolean} navigationFocused - <code>true</code> if UI has joystick navigation focus, <code>false</code> if it *     doesn't. * @property {boolean} navigationFocusDisabled - <code>true</code> if UI joystick navigation focus is disabled, *     <code>false</code> if it isn't. */ // Needs to match the constants in resources/qml/Global.jsclass OffscreenFlags : public QObject {    Q_OBJECT    Q_PROPERTY(bool navigationFocused READ isNavigationFocused WRITE setNavigationFocused NOTIFY navigationFocusedChanged)     // Allow scripts that are doing their own navigation support to disable navigation focus (i.e. handControllerPointer.js)    Q_PROPERTY(bool navigationFocusDisabled READ isNavigationFocusDisabled WRITE setNavigationFocusDisabled NOTIFY navigationFocusDisabledChanged) public:     OffscreenFlags(QObject* parent = nullptr) : QObject(parent) {}    bool isNavigationFocused() const { return _navigationFocused; }    void setNavigationFocused(bool focused) {        if (_navigationFocused != focused) {            _navigationFocused = focused;            emit navigationFocusedChanged();        }    }     bool isNavigationFocusDisabled() const { return _navigationFocusDisabled; }    void setNavigationFocusDisabled(bool disabled) {        if (_navigationFocusDisabled != disabled) {            _navigationFocusDisabled = disabled;            emit navigationFocusDisabledChanged();        }    } signals:     /*@jsdoc     * Triggered when the value of the <code>navigationFocused</code> property changes.     * @function OffscreenFlags.navigationFocusedChanged     * @returns {Signal}     */    void navigationFocusedChanged();     /*@jsdoc     * Triggered when the value of the <code>navigationFocusDisabled</code> property changes.     * @function OffscreenFlags.navigationFocusDisabledChanged     * @returns {Signal}     */    void navigationFocusDisabledChanged(); private:    bool _navigationFocused { false };    bool _navigationFocusDisabled{ false };}; static OffscreenFlags* offscreenFlags { nullptr }; // This hack allows the QML UI to work with keys that are also bound as// shortcuts at the application level.  However, it seems as though the// bound actions are still getting triggered.  At least for backspace.// Not sure why.//// However, the problem may go away once we switch to the new menu system,// so I think it's OK for the time being.bool OffscreenUi::shouldSwallowShortcut(QEvent* event) {    Q_ASSERT(event->type() == QEvent::ShortcutOverride);    auto window = getWindow();    if (window) {        QObject* focusObject = getWindow()->focusObject();        if (focusObject != getWindow() && focusObject != getRootItem()) {            event->accept();            return true;        }    }    return false;} static QTouchDevice _touchDevice;OffscreenUi::OffscreenUi() {    static std::once_flag once;    std::call_once(once, [&] {        _touchDevice.setCapabilities(QTouchDevice::Position);        _touchDevice.setType(QTouchDevice::TouchScreen);        _touchDevice.setName("OffscreenUiTouchDevice");        _touchDevice.setMaximumTouchPoints(4);    });     auto pointerManager = DependencyManager::get<PointerManager>();    connect(pointerManager.data(), &PointerManager::hoverBeginHUD, this, &OffscreenUi::hoverBeginEvent);    connect(pointerManager.data(), &PointerManager::hoverContinueHUD, this, &OffscreenUi::handlePointerEvent);    connect(pointerManager.data(), &PointerManager::hoverEndHUD, this, &OffscreenUi::hoverEndEvent);    connect(pointerManager.data(), &PointerManager::triggerBeginHUD, this, &OffscreenUi::handlePointerEvent);    connect(pointerManager.data(), &PointerManager::triggerContinueHUD, this, &OffscreenUi::handlePointerEvent);    connect(pointerManager.data(), &PointerManager::triggerEndHUD, this, &OffscreenUi::handlePointerEvent);    connect(pointerManager.data(), &PointerManager::scrollHUD, this, &OffscreenUi::handlePointerEvent);} void OffscreenUi::hoverBeginEvent(const PointerEvent& event) {    OffscreenQmlSurface::hoverBeginEvent(event, _touchDevice);} void OffscreenUi::hoverEndEvent(const PointerEvent& event) {    OffscreenQmlSurface::hoverEndEvent(event, _touchDevice);} void OffscreenUi::handlePointerEvent(const PointerEvent& event) {    OffscreenQmlSurface::handlePointerEvent(event, _touchDevice);} QObject* OffscreenUi::getFlags() {    return offscreenFlags;} void OffscreenUi::removeModalDialog(QObject* modal) {    if (modal) {        _modalDialogListeners.removeOne(modal);        modal->deleteLater();    }} void OffscreenUi::onRootContextCreated(QQmlContext* qmlContext) {    OffscreenQmlSurface::onRootContextCreated(qmlContext);    qmlContext->setContextProperty("OffscreenUi", this);    qmlContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags());    qmlContext->setContextProperty("fileDialogHelper", new FileDialogHelper());#ifdef DEBUG    qmlContext->setContextProperty("DebugQML", QVariant(true));#else    qmlContext->setContextProperty("DebugQML", QVariant(false));#endif } void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {    emit showDesktop();    QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);    // First load?    if (!item) {        load(url, f);        item = getRootItem()->findChild<QQuickItem*>(name);    }     if (item) {        QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).write(true);    }} void OffscreenUi::hideDesktopWindows() {    if (_desktop) {        if (QThread::currentThread() != thread()) {            BLOCKING_INVOKE_METHOD(this, "hideDesktopWindows");        }        QMetaObject::invokeMethod(_desktop, "hideDesktopWindows");    }} void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {    QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);    if (!item) {        show(url, name, f);        return;    }     // Already loaded, so just flip the bit    QQmlProperty shownProperty(item, OFFSCREEN_VISIBILITY_PROPERTY);    shownProperty.write(!shownProperty.read().toBool());} bool OffscreenUi::isPointOnDesktopWindow(QVariant point) {    if (_desktop) {        QVariant result;        BLOCKING_INVOKE_METHOD(_desktop, "isPointOnWindow",                               Q_RETURN_ARG(QVariant, result),                               Q_ARG(QVariant, point));        return result.toBool();    }    return false;} void OffscreenUi::hide(const QString& name) {    auto rootItem = getRootItem();    if (rootItem) {        QQuickItem* item = rootItem->findChild<QQuickItem*>(name);        if (item) {            QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).write(false);        }    }} bool OffscreenUi::isVisible(const QString& name) {    auto rootItem = getRootItem();    if (rootItem) {        QQuickItem* item = rootItem->findChild<QQuickItem*>(name);        if (item) {            return QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).read().toBool();        }    }    return false;} class MessageBoxListener : public ModalDialogListener {    Q_OBJECT     friend class OffscreenUi;    MessageBoxListener(QQuickItem* messageBox) : ModalDialogListener(messageBox) {        if (_finished) {            return;        }        connect(_dialog, SIGNAL(selected(int)), this, SLOT(onSelected(int)));    }     virtual QMessageBox::StandardButton waitForButtonResult() {        ModalDialogListener::waitForResult();        return static_cast<QMessageBox::StandardButton>(_result.toInt());    } protected slots:    virtual void onDestroyed() override {        ModalDialogListener::onDestroyed();        onSelected(QMessageBox::NoButton);    } private slots:    void onSelected(int button) {        _result = button;        _finished = true;        auto offscreenUi = DependencyManager::get<OffscreenUi>();        emit response(_result);        if (!offscreenUi.isNull()) {            offscreenUi->removeModalDialog(qobject_cast<QObject*>(this));        }        disconnect(_dialog);    }}; QQuickItem* OffscreenUi::createMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {    QVariantMap map;    map.insert("title", title);    map.insert("text", text);    map.insert("icon", icon);    map.insert("buttons", buttons.operator int());    map.insert("defaultButton", defaultButton);    QVariant result;    bool invokeResult = false;    auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();    TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));    if (tablet->getToolbarMode() && _desktop) {       invokeResult =  QMetaObject::invokeMethod(_desktop, "messageBox",                                  Q_RETURN_ARG(QVariant, result),                                  Q_ARG(QVariant, QVariant::fromValue(map)));    } else if (QQuickItem* tabletRoot = tablet->getTabletRoot()) {        invokeResult =  QMetaObject::invokeMethod(tabletRoot, "messageBox",                                                  Q_RETURN_ARG(QVariant, result),                                                  Q_ARG(QVariant, QVariant::fromValue(map)));        emit tabletScriptingInterface->tabletNotification();    }     if (!invokeResult) {        qWarning() << "Failed to create message box";        return nullptr;    }    return qvariant_cast<QQuickItem*>(result);} int OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) {    if (!messageBox) {        return QMessageBox::NoButton;    }     return MessageBoxListener(messageBox).waitForButtonResult();}  QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {    if (QThread::currentThread() != thread()) {        QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton;        BLOCKING_INVOKE_METHOD(this, "messageBox",            Q_RETURN_ARG(QMessageBox::StandardButton, result),            Q_ARG(Icon, icon),            Q_ARG(QString, title),            Q_ARG(QString, text),            Q_ARG(QMessageBox::StandardButtons, buttons),            Q_ARG(QMessageBox::StandardButton, defaultButton));        return result;    }     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);}QMessageBox::StandardButton OffscreenUi::information(const QString& title, const QString& text,    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 {    Q_OBJECT     friend class OffscreenUi;    InputDialogListener(QQuickItem* queryBox) : ModalDialogListener(queryBox) {        if (_finished) {            return;        }        connect(_dialog, SIGNAL(selected(QVariant)), this, SLOT(onSelected(const QVariant&)));        connect(_dialog, SIGNAL(canceled()), this, SLOT(onSelected()));    } 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);    }}; QString OffscreenUi::getText(const Icon icon, const QString& title, const QString& label, const QString& text, bool* ok) {    if (ok) { *ok = false; }    QVariant result = DependencyManager::get<OffscreenUi>()->inputDialog(icon, title, label, text).toString();    if (ok && result.isValid()) {        *ok = true;    }    return result.toString();} QString OffscreenUi::getItem(const Icon icon, const QString& title, const QString& label, const QStringList& items,    int current, bool editable, bool* ok) {     if (ok) {        *ok = false;    }     auto offscreenUi = DependencyManager::get<OffscreenUi>();    auto inputDialog = offscreenUi->createInputDialog(icon, title, label, current);    if (!inputDialog) {        return QString();    }    inputDialog->setProperty("items", items);    inputDialog->setProperty("editable", editable);     QVariant result = offscreenUi->waitForInputDialogResult(inputDialog);    if (!result.isValid()) {        return QString();    }     if (ok) {        *ok = true;    }    return result.toString();} 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;} QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) {    if (QThread::currentThread() != thread()) {        QVariant result;        BLOCKING_INVOKE_METHOD(this, "inputDialog",            Q_RETURN_ARG(QVariant, result),            Q_ARG(Icon, icon),            Q_ARG(QString, title),            Q_ARG(QString, label),            Q_ARG(QVariant, current));        return result;    }     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);

Lines 1–500 of 1212.