From 46061a2a15bf49c2da631480e5189e28eff2902a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 16 Jul 2018 11:54:05 -0700 Subject: [PATCH] initial with pal --- scripts/modules/appUi.js | 161 +++++++++++++++++++++++++++++++++++++++ scripts/system/pal.js | 101 ++++++++++-------------- 2 files changed, 201 insertions(+), 61 deletions(-) create mode 100644 scripts/modules/appUi.js diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js new file mode 100644 index 0000000000..636affb94a --- /dev/null +++ b/scripts/modules/appUi.js @@ -0,0 +1,161 @@ +// +// libraries/appUi.js +// +// Created by Howard Stearns on 3/20/18. +// Copyright 2018 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 +// + +function AppUi(properties) { + /* Example development order: + 1. var AppUi = Script.require('appUi'); + 2. Put appname-i.svg, appname-a.svg in graphicsDirectory (where non-default graphicsDirectory can be added in #3). + 3. ui = new AppUi({buttonName: "APPNAME", home: "qml-or-html-path"}); + (and if converting an existing app, + define var tablet = ui.tablet, button = ui.button; as needed. + remove button.clicked.[dis]connect and tablet.remove(button).) + 4. Define onOpened and onClosed behavior in #3, if any. + (and if converting an existing app, remove screenChanged.[dis]connect.) + 5. Define onMessage in #3, if any. + (and if converting an existing app, remove code that [un]wires that message handling.) + x. lint! + */ + + var that = this; + function defaultButton(name, suffix) { + var base = that[name] || (that.buttonPrefix + suffix); + that[name] = (base.indexOf('/') >= 0) ? base : (that.graphicsDirectory + base); //poor man's merge + } + + // Defaults: + that.tabletName = "com.highfidelity.interface.tablet.system"; + that.inject = ""; + that.graphicsDirectory = "icons/tablet-icons/"; // Where to look for button svgs. See below. + that.checkIsOpen = function checkIsOpen(type, tabletUrl) { // Are we active? Value used to set isOpen. + return (type === that.type) && (tabletUrl.indexOf(that.home) >= 0); // Actual url may have prefix or suffix. + } + that.toOpen = function toOpen() { // How to open the app. + if (that.isQML()) { + that.tablet.loadQMLSource(that.home); + } else { + that.tablet.gotoWebScreen(that.home, that.inject); + } + }; + that.toClose = function toClose() { // How to close the app. + // for toolbar-mode: go back to home screen, this will close the window. + that.tablet.gotoHomeScreen(); + }; + that.buttonActive = function buttonActive(isActive) { // How to make the button active (white). + that.button.editProperties({isActive: isActive}); + }; + that.messagesWaiting = function messagesWaiting(isWaiting) { // How to indicate a message light on button. + // Note that waitingButton doesn't have to exist unless someone explicitly calls this with isWaiting true. + that.button.editProperties({ + icon: isWaiting ? that.normalMessagesButton : that.normalButton, + activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton + }); + }; + that.isQML = function isQML() { // We set type property in onClick. + return that.type === 'QML'; + } + that.eventSignal = function eventSignal() { // What signal to hook onMessage to. + return that.isQML() ? that.tablet.fromQml : that.tablet.webEventReceived; + }; + + // Overwrite with the given properties: + Object.keys(properties).forEach(function (key) { that[key] = properties[key]; }); + + // Properties: + that.tablet = Tablet.getTablet(that.tabletName); + // Must be after we gather properties. + that.buttonPrefix = that.buttonPrefix || that.buttonName.toLowerCase() + "-"; + defaultButton('normalButton', 'i.svg'); + defaultButton('activeButton', 'a.svg'); + defaultButton('normalMessagesButton', 'i-msg.svg'); + defaultButton('activeMessagesButton', 'a-msg.svg'); + that.button = that.tablet.addButton({ + icon: that.normalButton, + activeIcon: that.activeButton, + text: that.buttonName, + sortOrder: that.sortOrder + }); + that.ignore = function ignore() { }; + + // Handlers + that.onScreenChanged = function onScreenChanged(type, url) { + // Set isOpen, wireEventBridge, set buttonActive as appropriate, + // and finally call onOpened() or onClosed() IFF defined. + print('hrs fixme onScreenChanged', type, url, that.isOpen); + if (that.checkIsOpen(type, url)) { + if (!that.isOpen) { + that.isOpen = true; + that.wireEventBridge(true); + that.buttonActive(true); + if (that.onOpened) { + that.onOpened(); + } + } + + } else { // Not us. Should we do something for type Home, Menu, and particularly Closed (meaning tablet hidden? + if (that.isOpen) { + that.isOpen = false; + that.wireEventBridge(false); + that.buttonActive(false); + if (that.onClosed) { + that.onClosed(); + } + } + } + }; + that.hasEventBridge = false; + that.wireEventBridge = function wireEventBridge(on) { + // Sets hasEventBridge and wires onMessage to eventSignal as appropriate, IFF onMessage defined. + print('hrs fixme wireEventBridge', on, that.hasEventBridge); + if (!that.onMessage) { return; } + if (on) { + if (!that.hasEventBridge) { + print('hrs fixme connecting', that.eventSignal()); + that.eventSignal().connect(that.onMessage); + that.hasEventBridge = true; + } + } else { + if (that.hasEventBridge) { + print('hrs fixme connecting', that.eventSignal()); + that.eventSignal().disconnect(that.onMessage); + that.hasEventBridge = false; + } + } + }; + that.isOpen = false; + // To facilitate incremental development, only wire onClicked to do something when "home" is defined in properties. + that.onClicked = that.home + ? function onClicked() { + // Call toOpen() or toClose(), and reset type based on current home property. + if (that.isOpen) { + that.toClose(); + } else { + that.type = /.qml$/.test(that.home) ? 'QML' : 'Web' + that.toOpen(); + } + } : that.ignore; + that.onScriptEnding = function onScriptEnding() { + // Close if necessary, clean up any remaining handlers, and remove the button. + if (that.isOpen) { + that.toClose(); + } + that.tablet.screenChanged.disconnect(that.onScreenChanged); + if (that.button) { + if (that.onClicked) { + that.button.clicked.disconnect(that.onClicked); + } + that.tablet.removeButton(that.button); + } + }; + // Set up the handlers. + that.tablet.screenChanged.connect(that.onScreenChanged); + that.button.clicked.connect(that.onClicked); + Script.scriptEnding.connect(that.onScriptEnding); +}; +module.exports = AppUi; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index e967ee6469..cc12b7798f 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -15,9 +15,10 @@ (function() { // BEGIN LOCAL_SCOPE var request = Script.require('request').request; + var AppUi = Script.require('appUi'); var populateNearbyUserList, color, textures, removeOverlays, - controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged, + controllerComputePickRay, off, receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL, createAudioInterval, tablet, CHANNEL, getConnectionData, findableByChanged, avatarAdded, avatarRemoved, avatarSessionChanged; // forward references; @@ -678,20 +679,7 @@ function tabletVisibilityChanged() { var wasOnPalScreen = false; var onPalScreen = false; -var PAL_QML_SOURCE = "hifi/Pal.qml"; -function onTabletButtonClicked() { - if (!tablet) { - print("Warning in onTabletButtonClicked(): 'tablet' undefined!"); - return; - } - if (onPalScreen) { - // In Toolbar Mode, `gotoHomeScreen` will close the app window. - tablet.gotoHomeScreen(); - } else { - tablet.loadQMLSource(PAL_QML_SOURCE); - } -} -var hasEventBridge = false; +/*var hasEventBridge = false; function wireEventBridge(on) { if (on) { if (!hasEventBridge) { @@ -704,38 +692,31 @@ function wireEventBridge(on) { hasEventBridge = false; } } -} - -function onTabletScreenChanged(type, url) { +}*/ +function captureState() { wasOnPalScreen = onPalScreen; - onPalScreen = (type === "QML" && url === PAL_QML_SOURCE); - wireEventBridge(onPalScreen); - // for toolbar mode: change button to active when window is first openend, false otherwise. - button.editProperties({isActive: onPalScreen}); - - if (onPalScreen) { - isWired = true; - - ContextOverlay.enabled = false; - Users.requestsDomainListData = true; - - audioTimer = createAudioInterval(AUDIO_LEVEL_UPDATE_INTERVAL_MS); - - tablet.tabletShownChanged.connect(tabletVisibilityChanged); - Script.update.connect(updateOverlays); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - Users.usernameFromIDReply.connect(usernameFromIDReply); - triggerMapping.enable(); - triggerPressMapping.enable(); - populateNearbyUserList(); - } else { - off(); - if (wasOnPalScreen) { - ContextOverlay.enabled = true; - } - } + onPalScreen = ui.isOpen; + //wireEventBridge(onPalScreen); } +function on() { + captureState(); + isWired = true; + + ContextOverlay.enabled = false; + Users.requestsDomainListData = true; + + audioTimer = createAudioInterval(AUDIO_LEVEL_UPDATE_INTERVAL_MS); + + tablet.tabletShownChanged.connect(tabletVisibilityChanged); + Script.update.connect(updateOverlays); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + Users.usernameFromIDReply.connect(usernameFromIDReply); + triggerMapping.enable(); + triggerPressMapping.enable(); + populateNearbyUserList(); +} +var button, ui, tablet; // // Message from other scripts, such as edit.js @@ -749,7 +730,7 @@ function receiveMessage(channel, messageString, senderID) { switch (message.method) { case 'select': if (!onPalScreen) { - tablet.loadQMLSource(PAL_QML_SOURCE); + tablet.loadQMLSource(ui.home); Script.setTimeout(function () { sendToQml(message); }, 1000); } else { sendToQml(message); // Accepts objects, not just strings. @@ -847,20 +828,17 @@ function avatarSessionChanged(avatarID) { sendToQml({ method: 'palIsStale', params: [avatarID, 'avatarSessionChanged'] }); } - -var button; -var buttonName = "PEOPLE"; -var tablet = null; function startup() { - tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - button = tablet.addButton({ - text: buttonName, - icon: "icons/tablet-icons/people-i.svg", - activeIcon: "icons/tablet-icons/people-a.svg", - sortOrder: 7 + ui = new AppUi({ + buttonName: "PEOPLE", + sortOrder: 7, + home: "hifi/Pal.qml", + onOpened: on, + onClosed: off, + onMessage: fromQml }); - button.clicked.connect(onTabletButtonClicked); - tablet.screenChanged.connect(onTabletScreenChanged); + tablet = ui.tablet; + button = ui.button; Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); Messages.subscribe(CHANNEL); @@ -877,6 +855,7 @@ var isWired = false; var audioTimer; var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) function off() { + captureState(); if (isWired) { Script.update.disconnect(updateOverlays); Controller.mousePressEvent.disconnect(handleMouseEvent); @@ -896,15 +875,15 @@ function off() { } removeOverlays(); + if (wasOnPalScreen) { + ContextOverlay.enabled = true; + } } function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } - button.clicked.disconnect(onTabletButtonClicked); - tablet.removeButton(button); - tablet.screenChanged.disconnect(onTabletScreenChanged); Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL); Messages.subscribe(CHANNEL);