diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 481c7846a9..792c8d5082 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -33,6 +33,8 @@ Item { } } + function setShown(value) {} + SoundEffect { id: buttonClickSound volume: 0.1 @@ -85,5 +87,5 @@ Item { } width: 480 - height: 720 + height: 706 } diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml new file mode 100644 index 0000000000..14e969b3a6 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -0,0 +1,94 @@ +// +// WindowRoot.qml +// +// Created by Anthony Thibault on 14 Feb 2017 +// Copyright 2017 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 +// +// This qml is used when tablet content is shown on the 2d overlay ui +// TODO: FIXME: this is practically identical to TabletRoot.qml + +import "../../windows" as Windows +import QtQuick 2.0 +import Hifi 1.0 + +Windows.ScrollingWindow { + id: tabletRoot + objectName: "tabletRoot" + property string username: "Unknown user" + property var eventBridge; + shown: false + resizable: false + + signal showDesktop(); + + function loadSource(url) { + loader.source = url; + } + + function loadWebUrl(url, injectedJavaScriptUrl) { + loader.item.url = url; + loader.item.scriptURL = injectedJavaScriptUrl; + } + + // used to send a message from qml to interface script. + signal sendToScript(var message); + + // used to receive messages from interface script + function fromScript(message) { + if (loader.item.hasOwnProperty("fromScript")) { + loader.item.fromScript(message); + } + } + + SoundEffect { + id: buttonClickSound + volume: 0.1 + source: "../../../sounds/Gamemaster-Audio-button-click.wav" + } + + function playButtonClickSound() { + // Because of the asynchronous nature of initalization, it is possible for this function to be + // called before the C++ has set the globalPosition context variable. + if (typeof globalPosition !== 'undefined') { + buttonClickSound.play(globalPosition); + } + } + + function setUsername(newUsername) { + username = newUsername; + } + + Loader { + id: loader + objectName: "loader" + asynchronous: false + + height: pane.scrollHeight + width: pane.contentWidth + anchors.left: parent.left + anchors.top: parent.top + + onLoaded: { + if (loader.item.hasOwnProperty("eventBridge")) { + loader.item.eventBridge = eventBridge; + + // Hook up callback for clara.io download from the marketplace. + eventBridge.webEventReceived.connect(function (event) { + if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") { + ApplicationInterface.addAssetToWorldFromURL(event.slice(18)); + } + }); + } + if (loader.item.hasOwnProperty("sendToScript")) { + loader.item.sendToScript.connect(tabletRoot.sendToScript); + } + loader.item.forceActiveFocus(); + } + } + + implicitWidth: 480 + implicitHeight: 706 +} diff --git a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml index 0e4423268b..591544d614 100644 --- a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml +++ b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml @@ -12,7 +12,9 @@ StateImage { property int imageOnIn: 2 property string text: "" + property string activeText: button.text property string icon: "icons/tablet-icons/blank.svg" + property string activeIcon: button.icon signal clicked() @@ -73,13 +75,13 @@ StateImage { anchors.bottomMargin: 0 anchors.horizontalCenter: parent.horizontalCenter fillMode: Image.Stretch - source: urlHelper(button.icon) + source: urlHelper(button.isActive ? button.activeIcon : button.icon) } Text { id: caption - color: "#ffffff" - text: button.text + color: button.isActive ? "#000000" : "#ffffff" + text: button.isActive ? button.activeText : button.text font.bold: false font.pixelSize: 9 anchors.bottom: parent.bottom diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index d22d8ecbe8..20216ed7ae 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -85,6 +85,10 @@ Fadable { function setDefaultFocus() {} // Default function; can be overridden by dialogs. + function setShown(value) { + window.shown = value; + } + property var rectifier: Timer { property bool executing: false; interval: 100 diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 576fec0ca3..dc6b1d6aa3 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -15,7 +15,8 @@ #include #include "ScriptEngineLogging.h" #include "DependencyManager.h" -#include "OffscreenUi.h" +#include +#include #include "SoundEffect.h" TabletScriptingInterface::TabletScriptingInterface() { @@ -156,9 +157,10 @@ QObject* TabletScriptingInterface::getFlags() 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"; +static int s_windowNameCounter = 1; TabletProxy::TabletProxy(QString name) : _name(name) { - ; + } void TabletProxy::setToolbarMode(bool toolbarMode) { @@ -171,9 +173,27 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { if (toolbarMode) { removeButtonsFromHomeScreen(); addButtonsToToolbar(); + + // create new desktop window + auto offscreenUi = DependencyManager::get(); + offscreenUi->executeOnUiThread([=, this] { + InfoView::registerType(); + QString name = _name + QString("%1").arg(s_windowNameCounter++); + offscreenUi->show(QUrl("hifi/tablet/WindowRoot.qml"), name, [&](QQmlContext* context, QObject* newObject) { + QQuickItem* item = dynamic_cast(newObject); + _desktopWindow = item; + QObject::connect(_desktopWindow, SIGNAL(windowClosed), this, SLOT(desktopWindowClosed())); + }); + }); } else { removeButtonsFromToolbar(); addButtonsToHomeScreen(); + + // destroy desktop window + if (_desktopWindow) { + _desktopWindow->deleteLater(); + _desktopWindow = nullptr; + } } } @@ -246,39 +266,63 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr } void TabletProxy::gotoMenuScreen(const QString& submenu) { - if (_qmlTabletRoot) { + + QObject* root = nullptr; + if (!_toolbarMode && _qmlTabletRoot) { + root = _qmlTabletRoot; + } else if (_toolbarMode && _desktopWindow) { + root = _desktopWindow; + } + + if (root) { if (_state != State::Menu) { removeButtonsFromHomeScreen(); - QMetaObject::invokeMethod(_qmlTabletRoot, "setOption", Q_ARG(const QVariant&, QVariant(submenu))); - auto loader = _qmlTabletRoot->findChild("loader"); + QMetaObject::invokeMethod(root, "setOption", Q_ARG(const QVariant&, QVariant(submenu))); + auto loader = root->findChild("loader"); QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen()), Qt::DirectConnection); - QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); + 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) { - if (_qmlTabletRoot) { + + QObject* root = nullptr; + if (!_toolbarMode && _qmlTabletRoot) { + root = _qmlTabletRoot; + } else if (_toolbarMode && _desktopWindow) { + root = _desktopWindow; + } + + if (root) { if (_state != State::QML) { removeButtonsFromHomeScreen(); - QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, path)); + 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))); } } } + void TabletProxy::gotoHomeScreen() { - if (_qmlTabletRoot) { - if (_state != State::Home) { + if (_state != State::Home) { + if (!_toolbarMode && _qmlTabletRoot) { auto loader = _qmlTabletRoot->findChild("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"); - _state = State::Home; - emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); + } else if (_toolbarMode && _desktopWindow) { + // close desktop window + if (_desktopWindow) { + QMetaObject::invokeMethod(_desktopWindow, "setShown", Q_ARG(const QVariant&, QVariant(false))); + } } + _state = State::Home; + emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } } @@ -287,17 +331,24 @@ void TabletProxy::gotoWebScreen(const QString& url) { } void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl) { - if (_qmlTabletRoot) { - if (_state == State::Home) { - removeButtonsFromHomeScreen(); + + QObject* root = nullptr; + if (!_toolbarMode && _qmlTabletRoot) { + root = _qmlTabletRoot; + } else if (_toolbarMode && _desktopWindow) { + root = _desktopWindow; + } + + if (_state != State::Web) { + if (root) { + QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); } - if (_state != State::Web) { - QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); - _state = State::Web; - emit screenChanged(QVariant("Web"), QVariant(url)); - } - QMetaObject::invokeMethod(_qmlTabletRoot, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), - Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl))); + _state = State::Web; + emit screenChanged(QVariant("Web"), QVariant(url)); + } + if (root) { + 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))); } } @@ -391,11 +442,18 @@ QObject* TabletProxy::getTabletSurface() { } void TabletProxy::addButtonsToMenuScreen() { - if (!_qmlTabletRoot) { + QObject* root = nullptr; + if (!_toolbarMode && _qmlTabletRoot) { + root = _qmlTabletRoot; + } else if (_toolbarMode && _desktopWindow) { + root = _desktopWindow; + } + + if (!root) { return; } - auto loader = _qmlTabletRoot->findChild("loader"); + auto loader = root->findChild("loader"); if (!loader) { return; } @@ -420,6 +478,10 @@ void TabletProxy::removeButtonsFromHomeScreen() { } } +void TabletProxy::desktopWindowClosed() { + gotoHomeScreen(); +} + void TabletProxy::addButtonsToToolbar() { auto tabletScriptingInterface = DependencyManager::get(); QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); @@ -515,6 +577,9 @@ void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { std::lock_guard guard(_mutex); _toolbarButtonProxy = toolbarButtonProxy; + if (_toolbarButtonProxy) { + QObject::connect(_toolbarButtonProxy, SIGNAL(clicked()), this, SLOT(clickedSlot())); + } } QVariantMap TabletButtonProxy::getProperties() const { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index b2f9f2ab47..c9aee518fc 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -182,6 +182,7 @@ signals: protected slots: void addButtonsToHomeScreen(); void addButtonsToMenuScreen(); + void desktopWindowClosed(); protected: void removeButtonsFromHomeScreen(); void addButtonsToToolbar(); @@ -192,6 +193,7 @@ protected: std::vector> _tabletButtonProxies; QQuickItem* _qmlTabletRoot { nullptr }; QObject* _qmlOffscreenSurface { nullptr }; + QQuickItem* _desktopWindow { nullptr }; bool _toolbarMode { false }; enum class State { Uninitialized, Home, Web, Menu, QML }; diff --git a/libraries/ui/src/InfoView.cpp b/libraries/ui/src/InfoView.cpp index d2c72bf5f2..cb80e3f6db 100644 --- a/libraries/ui/src/InfoView.cpp +++ b/libraries/ui/src/InfoView.cpp @@ -20,17 +20,22 @@ const QString InfoView::NAME{ "InfoView" }; Setting::Handle infoVersion("info-version", QString()); -InfoView::InfoView(QQuickItem* parent) : QQuickItem(parent) { +static bool registered{ false }; +InfoView::InfoView(QQuickItem* parent) : QQuickItem(parent) { + registerType(); } -void InfoView::registerType() { - qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); -} +void InfoView::registerType() { + if (!registered) { + qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); + registered = true; + } +} QString fetchVersion(const QUrl& url) { QXmlQuery query; - query.bindVariable("file", QVariant(url)); + query.bindVariable("file", QVariant(url)); query.setQuery("string((doc($file)//input[@id='version'])[1]/@value)"); QString r; query.evaluateTo(&r); @@ -38,14 +43,10 @@ QString fetchVersion(const QUrl& url) { } void InfoView::show(const QString& path, bool firstOrChangedOnly, QString urlQuery) { - static bool registered{ false }; - if (!registered) { - registerType(); - registered = true; - } + registerType(); QUrl url; if (QDir(path).isRelative()) { - url = QUrl::fromLocalFile(PathUtils::resourcesPath() + path); + url = QUrl::fromLocalFile(PathUtils::resourcesPath() + path); } else { url = QUrl::fromLocalFile(path); } @@ -56,7 +57,7 @@ void InfoView::show(const QString& path, bool firstOrChangedOnly, QString urlQue const QString version = fetchVersion(url); // If we have version information stored if (lastVersion != QString::null) { - // Check to see the document version. If it's valid and matches + // Check to see the document version. If it's valid and matches // the stored version, we're done, so exit if (version == QString::null || version == lastVersion) { return; @@ -87,4 +88,3 @@ void InfoView::setUrl(const QUrl& url) { emit urlChanged(); } } - diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 3e52eec4dd..de869ad7de 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -547,7 +547,7 @@ function startup(mode) { } // var mode = Settings.getValue("HUDUIEnabled"); -startup(HMD.active ? "tablet" : "toolbar"); +startup("tablet"); var isWired = false; var audioTimer; @@ -720,9 +720,4 @@ function shutdown() { // Script.scriptEnding.connect(shutdown); -HMD.displayModeChanged.connect(function () { - shutdown(); - startup(HMD.active ? "tablet" : "toolbar"); -}); - }()); // END LOCAL_SCOPE