First cut of pal on tablet.

This commit is contained in:
Anthony J. Thibault 2017-02-08 17:27:19 -08:00
parent 7db86204d1
commit a209d0372a
8 changed files with 138 additions and 17 deletions

View file

@ -16,6 +16,8 @@ import QtQuick.Controls 1.4
import "../styles-uit" import "../styles-uit"
import "../controls-uit" as HifiControls import "../controls-uit" as HifiControls
// references HMD, Users, UserActivityLogger from root context
Rectangle { Rectangle {
id: pal id: pal
// Size // Size
@ -36,6 +38,8 @@ Rectangle {
// NOTE: if another script modifies the per-avatar gain, this value won't be accurate! // NOTE: if another script modifies the per-avatar gain, this value won't be accurate!
property var gainSliderValueDB: ({}); property var gainSliderValueDB: ({});
HifiConstants { id: hifi }
// The letterbox used for popup messages // The letterbox used for popup messages
LetterboxMessage { LetterboxMessage {
id: letterboxMessage id: letterboxMessage

View file

@ -18,6 +18,16 @@ Item {
loader.item.scriptURL = injectedJavaScriptUrl; 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 { SoundEffect {
id: buttonClickSound id: buttonClickSound
volume: 0.1 volume: 0.1
@ -55,6 +65,9 @@ Item {
} }
}); });
} }
if (loader.item.hasOwnProperty("sendToScript")) {
loader.item.sendToScript.connect(tabletRoot.sendToScript);
}
loader.item.forceActiveFocus(); loader.item.forceActiveFocus();
} }
} }

View file

@ -23,11 +23,14 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#include <GeometryCache.h> #include <GeometryCache.h>
#include <GeometryUtil.h> #include <GeometryUtil.h>
#include <scripting/HMDScriptingInterface.h>
#include <gl/OffscreenQmlSurface.h> #include <gl/OffscreenQmlSurface.h>
#include <PathUtils.h> #include <PathUtils.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <TabletScriptingInterface.h> #include <TabletScriptingInterface.h>
#include <TextureCache.h> #include <TextureCache.h>
#include <UsersScriptingInterface.h>
#include <UserActivityLoggerScriptingInterface.h>
#include <AbstractViewStateInterface.h> #include <AbstractViewStateInterface.h>
#include <gl/OffscreenQmlSurface.h> #include <gl/OffscreenQmlSurface.h>
#include <gl/OffscreenQmlSurfaceCache.h> #include <gl/OffscreenQmlSurfaceCache.h>
@ -149,6 +152,10 @@ void Web3DOverlay::loadSourceURL() {
_webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {}); _webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {});
_webSurface->resume(); _webSurface->resume();
_webSurface->getRootContext()->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
_webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
_webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto flags = tabletScriptingInterface->getFlags(); auto flags = tabletScriptingInterface->getFlags();

View file

@ -604,6 +604,9 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QOb
qFatal("Could not load object as root item"); qFatal("Could not load object as root item");
return nullptr; return nullptr;
} }
connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant)));
// The root item is ready. Associate it with the window. // The root item is ready. Associate it with the window.
_rootItem = newItem; _rootItem = newItem;
_rootItem->setParentItem(_quickWindow->contentItem()); _rootItem->setParentItem(_quickWindow->contentItem());
@ -952,4 +955,13 @@ void OffscreenQmlSurface::emitWebEvent(const QVariant& message) {
} }
} }
void OffscreenQmlSurface::sendToQml(const QVariant& message) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "emitQmlEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
} else if (_rootItem) {
// call fromScript method on qml root
QMetaObject::invokeMethod(_rootItem, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
}
}
#include "OffscreenQmlSurface.moc" #include "OffscreenQmlSurface.moc"

View file

@ -107,6 +107,11 @@ signals:
void scriptEventReceived(const QVariant& message); void scriptEventReceived(const QVariant& message);
void webEventReceived(const QVariant& message); void webEventReceived(const QVariant& message);
// qml event bridge
public slots:
void sendToQml(const QVariant& message);
signals:
void fromQml(QVariant message);
protected: protected:
bool filterEnabled(QObject* originalDestination, QEvent* event) const; bool filterEnabled(QObject* originalDestination, QEvent* event) const;

View file

@ -183,6 +183,18 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr
_qmlTabletRoot = qmlTabletRoot; _qmlTabletRoot = qmlTabletRoot;
if (_qmlTabletRoot && _qmlOffscreenSurface) { if (_qmlTabletRoot && _qmlOffscreenSurface) {
QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SIGNAL(webEventReceived(QVariant))); 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;
}
});
gotoHomeScreen(); gotoHomeScreen();
QMetaObject::invokeMethod(_qmlTabletRoot, "setUsername", Q_ARG(const QVariant&, QVariant(getUsername()))); QMetaObject::invokeMethod(_qmlTabletRoot, "setUsername", Q_ARG(const QVariant&, QVariant(getUsername())));
@ -197,6 +209,7 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr
} else { } else {
removeButtonsFromHomeScreen(); removeButtonsFromHomeScreen();
_state = State::Uninitialized; _state = State::Uninitialized;
emit screenChanged(QVariant("Closed"), QVariant(""));
} }
} }
@ -208,6 +221,7 @@ void TabletProxy::gotoMenuScreen() {
QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen()), Qt::DirectConnection); QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen()), Qt::DirectConnection);
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL)));
_state = State::Menu; _state = State::Menu;
emit screenChanged(QVariant("Menu"), QVariant(VRMENU_SOURCE_URL));
} }
} }
} }
@ -217,6 +231,7 @@ void TabletProxy::loadQMLSource(const QVariant& path) {
if (_state != State::QML) { if (_state != State::QML) {
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, path)); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, path));
_state = State::QML; _state = State::QML;
emit screenChanged(QVariant("QML"), path);
} }
} }
} }
@ -228,6 +243,7 @@ void TabletProxy::gotoHomeScreen() {
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL))); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL)));
QMetaObject::invokeMethod(_qmlTabletRoot, "playButtonClickSound"); QMetaObject::invokeMethod(_qmlTabletRoot, "playButtonClickSound");
_state = State::Home; _state = State::Home;
emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));
} }
} }
} }
@ -244,6 +260,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
if (_state != State::Web) { if (_state != State::Web) {
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL)));
_state = State::Web; _state = State::Web;
emit screenChanged(QVariant("Web"), QVariant(url));
} }
QMetaObject::invokeMethod(_qmlTabletRoot, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), QMetaObject::invokeMethod(_qmlTabletRoot, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)),
Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl))); Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl)));
@ -306,6 +323,12 @@ void TabletProxy::emitScriptEvent(QVariant msg) {
} }
} }
void TabletProxy::sendToQml(QVariant msg) {
if (_qmlOffscreenSurface) {
QMetaObject::invokeMethod(_qmlOffscreenSurface, "sendToQml", Qt::AutoConnection, Q_ARG(QVariant, msg));
}
}
void TabletProxy::addButtonsToHomeScreen() { void TabletProxy::addButtonsToHomeScreen() {
auto tablet = getQmlTablet(); auto tablet = getQmlTablet();
if (!tablet) { if (!tablet) {

View file

@ -122,6 +122,13 @@ public:
*/ */
Q_INVOKABLE void emitScriptEvent(QVariant msg); Q_INVOKABLE void emitScriptEvent(QVariant msg);
/**jsdoc
* Used to send an event to the qml embedded in the tablet
* @function TabletProxy#sendToQml
* @param msg {object|string}
*/
Q_INVOKABLE void sendToQml(QVariant msg);
Q_INVOKABLE bool onHomeScreen(); Q_INVOKABLE bool onHomeScreen();
QObject* getTabletSurface(); QObject* getTabletSurface();
@ -139,6 +146,22 @@ signals:
*/ */
void webEventReceived(QVariant msg); void webEventReceived(QVariant msg);
/**jsdoc
* Signaled when this tablet receives an event from the qml embedded in the tablet
* @function TabletProxy#fromQml
* @param msg {object|string}
* @returns {Signal}
*/
void fromQml(QVariant msg);
/**jsdoc
* Signales when this tablet screen changes.
* @function TabletProxy#screenChanged
* @param type {string} - "Home", "Web", "Menu", "QML", "Closed"
* @param url {string} - only valid for Web and QML.
*/
void screenChanged(QVariant type, QVariant url);
private slots: private slots:
void addButtonsToHomeScreen(); void addButtonsToHomeScreen();
void addButtonsToMenuScreen(); void addButtonsToMenuScreen();

View file

@ -203,8 +203,8 @@ var pal = new OverlayWindow({
height: 640, height: 640,
visible: false visible: false
}); });
pal.fromQml.connect(function (message) { // messages are {method, params}, like json-rpc. See also sendToQml. function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
print('From PAL QML:', JSON.stringify(message)); print('AJT: From PAL QML:', JSON.stringify(message));
switch (message.method) { switch (message.method) {
case 'selected': case 'selected':
selectedIds = message.params; selectedIds = message.params;
@ -234,6 +234,7 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like
} }
break; break;
case 'refresh': case 'refresh':
print("AJT: REFRESH!");
removeOverlays(); removeOverlays();
populateUserList(message.params); populateUserList(message.params);
UserActivityLogger.palAction("refresh", ""); UserActivityLogger.palAction("refresh", "");
@ -259,7 +260,15 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like
default: default:
print('Unrecognized message from Pal.qml:', JSON.stringify(message)); print('Unrecognized message from Pal.qml:', JSON.stringify(message));
} }
}); }
function sendToQml(message) {
if (Settings.getValue("HUDUIEnabled")) {
pal.sendToQml(message);
} else {
tablet.sendToQml(message);
}
}
// //
// Main operations. // Main operations.
@ -298,10 +307,10 @@ function populateUserList(selectData) {
data.push(avatarPalDatum); data.push(avatarPalDatum);
print('PAL data:', JSON.stringify(avatarPalDatum)); print('PAL data:', JSON.stringify(avatarPalDatum));
}); });
pal.sendToQml({ method: 'users', params: data }); sendToQml({ method: 'users', params: data });
if (selectData) { if (selectData) {
selectData[2] = true; selectData[2] = true;
pal.sendToQml({ method: 'select', params: selectData }); sendToQml({ method: 'select', params: selectData });
} }
} }
@ -322,7 +331,7 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) {
} }
print('Username Data:', JSON.stringify(data)); print('Username Data:', JSON.stringify(data));
// Ship the data off to QML // Ship the data off to QML
pal.sendToQml({ method: 'updateUsername', params: data }); sendToQml({ method: 'updateUsername', params: data });
} }
var pingPong = true; var pingPong = true;
@ -396,7 +405,7 @@ function handleClick(pickRay) {
ExtendedOverlay.applyPickRay(pickRay, function (overlay) { ExtendedOverlay.applyPickRay(pickRay, function (overlay) {
// Don't select directly. Tell qml, who will give us back a list of ids. // Don't select directly. Tell qml, who will give us back a list of ids.
var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]}; var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]};
pal.sendToQml(message); sendToQml(message);
return true; return true;
}); });
} }
@ -492,6 +501,7 @@ if (Settings.getValue("HUDUIEnabled")) {
visible: true, visible: true,
alpha: 0.9 alpha: 0.9
}); });
pal.fromQml.connect(fromQml);
} else { } else {
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
button = tablet.addButton({ button = tablet.addButton({
@ -499,7 +509,9 @@ if (Settings.getValue("HUDUIEnabled")) {
icon: "icons/tablet-icons/people-i.svg", icon: "icons/tablet-icons/people-i.svg",
sortOrder: 7 sortOrder: 7
}); });
tablet.fromQml.connect(fromQml);
} }
var isWired = false; var isWired = false;
var audioTimer; var audioTimer;
var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too)
@ -518,10 +530,26 @@ function off() {
Users.requestsDomainListData = false; Users.requestsDomainListData = false;
} }
function onClicked() { function onClicked() {
if (!pal.visible) { if (Settings.getValue("HUDUIEnabled")) {
if (!pal.visible) {
Users.requestsDomainListData = true;
populateUserList();
pal.raise();
isWired = true;
Script.update.connect(updateOverlays);
Controller.mousePressEvent.connect(handleMouseEvent);
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
triggerMapping.enable();
triggerPressMapping.enable();
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
} else {
off();
}
pal.setVisible(!pal.visible);
} else {
tablet.loadQMLSource("../Pal.qml");
Users.requestsDomainListData = true; Users.requestsDomainListData = true;
populateUserList(); populateUserList();
pal.raise();
isWired = true; isWired = true;
Script.update.connect(updateOverlays); Script.update.connect(updateOverlays);
Controller.mousePressEvent.connect(handleMouseEvent); Controller.mousePressEvent.connect(handleMouseEvent);
@ -529,10 +557,7 @@ function onClicked() {
triggerMapping.enable(); triggerMapping.enable();
triggerPressMapping.enable(); triggerPressMapping.enable();
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
} else {
off();
} }
pal.setVisible(!pal.visible);
} }
// //
@ -550,7 +575,7 @@ function receiveMessage(channel, messageString, senderID) {
if (!pal.visible) { if (!pal.visible) {
onClicked(); onClicked();
} }
pal.sendToQml(message); // Accepts objects, not just strings. sendToQml(message); // Accepts objects, not just strings.
break; break;
default: default:
print('Unrecognized PAL message', messageString); print('Unrecognized PAL message', messageString);
@ -607,13 +632,13 @@ function createAudioInterval(interval) {
var userId = id || 0; var userId = id || 0;
param[userId] = level; param[userId] = level;
}); });
pal.sendToQml({method: 'updateAudioLevel', params: param}); sendToQml({method: 'updateAudioLevel', params: param});
}, interval); }, interval);
} }
function avatarDisconnected(nodeID) { function avatarDisconnected(nodeID) {
// remove from the pal list // remove from the pal list
pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]}); sendToQml({method: 'avatarDisconnected', params: [nodeID]});
} }
// //
// Button state. // Button state.
@ -624,11 +649,20 @@ function onVisibleChanged() {
button.clicked.connect(onClicked); button.clicked.connect(onClicked);
pal.visibleChanged.connect(onVisibleChanged); pal.visibleChanged.connect(onVisibleChanged);
pal.closed.connect(off); pal.closed.connect(off);
if (!Settings.getValue("HUDUIEnabled")) {
tablet.screenChanged.connect(function (type, url) {
if (type !== "QML" || url !== "../Pal.qml") {
off();
}
});
}
Users.usernameFromIDReply.connect(usernameFromIDReply); Users.usernameFromIDReply.connect(usernameFromIDReply);
Users.avatarDisconnected.connect(avatarDisconnected); Users.avatarDisconnected.connect(avatarDisconnected);
function clearLocalQMLDataAndClosePAL() { function clearLocalQMLDataAndClosePAL() {
pal.sendToQml({ method: 'clearLocalQMLData' }); sendToQml({ method: 'clearLocalQMLData' });
if (pal.visible) { if (pal.visible) {
onClicked(); // Close the PAL onClicked(); // Close the PAL
} }