diff --git a/BUILD.md b/BUILD.md
index bd264a74ad..c9bb48d4b7 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -106,3 +106,4 @@ The following build options can be used when running CMake
#### Devices
You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.
+
\ No newline at end of file
diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h
index 34dc25914f..3ef908bedb 100644
--- a/assignment-client/src/avatars/ScriptableAvatar.h
+++ b/assignment-client/src/avatars/ScriptableAvatar.h
@@ -74,7 +74,8 @@
* avatar. Read-only.
* @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's
* size in the virtual world. Read-only.
- * @property {boolean} hasPriority - is the avatar in a Hero zone? Read-only.
+ * @property {boolean} hasPriority - true
if the avatar is in a "hero" zone, false
if it isn't.
+ * Read-only.
*
* @example
Locomotion control types.
+ *Value | Name | Description |
---|---|---|
0 | Default | Your walking speed is constant; it doesn't change depending on how far + * forward you push your controller's joystick. Fully pushing your joystick forward makes your avatar run. |
1 | Analog | Your walking speed changes in steps based on how far forward you push your + * controller's joystick. Fully pushing your joystick forward makes your avatar run. |
2 | AnalogPlus | Your walking speed changes proportionally to how far forward you push + * your controller's joystick. Fully pushing your joystick forward makes your avatar run. |
true
if the avatar is in a "hero" zone, false
if it isn't.
+ * Read-only.
*
* @comment IMPORTANT: This group of properties is copied from Avatar.h; they should NOT be edited here.
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
@@ -239,9 +258,16 @@ class MyAvatar : public Avatar {
* where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated.
* Read-only.
*
- * @property {number} walkSpeed - The walk speed of your avatar.
- * @property {number} walkBackwardSpeed - The walk backward speed of your avatar.
- * @property {number} sprintSpeed - The sprint speed of your avatar.
+ * @property {number} walkSpeed - The walk speed of your avatar for the current control scheme (see
+ * {@link MyAvatar.getControlScheme|getControlScheme}).
+ * @property {number} walkBackwardSpeed - The walk backward speed of your avatar for the current control scheme (see
+ * {@link MyAvatar.getControlScheme|getControlScheme}).
+ * @property {number} sprintSpeed - The sprint (run) speed of your avatar for the current control scheme (see
+ * {@link MyAvatar.getControlScheme|getControlScheme}).
+ * @property {number} analogPlusWalkSpeed - The walk speed of your avatar for the "AnalogPlus" control scheme.
+ * Warning: Setting this value also sets the value of analogPlusSprintSpeed
to twice
+ * the value.
true
if your avatar is sitting (avatar leaning is disabled,
* recenntering is enabled), false
if it is standing (avatar leaning is enabled, and avatar recenters if it
@@ -281,6 +307,7 @@ class MyAvatar : public Avatar {
* @borrows Avatar.updateAvatarEntity as updateAvatarEntity
* @borrows Avatar.clearAvatarEntity as clearAvatarEntity
* @borrows Avatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected
+ * @borrows Avatar.setSkeletonModelURL as setSkeletonModelURL
* @borrows Avatar.getAttachmentData as getAttachmentData
* @borrows Avatar.setAttachmentData as setAttachmentData
* @borrows Avatar.attach as attach
@@ -308,7 +335,6 @@ class MyAvatar : public Avatar {
* @comment Avatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame - Don't borrow because implementation is different.
* @borrows Avatar.getTargetScale as getTargetScale
* @borrows Avatar.resetLastSent as resetLastSent
- * @borrows Avatar.hasPriority as hasPriority
*/
// FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type
Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition)
@@ -583,14 +609,13 @@ public:
* the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see
* Avatar Standards.
* @function MyAvatar.overrideAnimation
- * @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the
+ * @param {string} url - The URL to the animation file. Animation files need to be FBX format, but only need to contain the
* avatar skeleton and animation data.
- * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
- * @param loop {boolean} Set to true if the animation should loop.
- * @param firstFrame {number} The frame the animation should start at.
- * @param lastFrame {number} The frame the animation should end at.
+ * @param {number} fps - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
+ * @param {boolean} loop - true
if the animation should loop, false
if it shouldn't.
+ * @param {number} firstFrame - The frame to start the animation at.
+ * @param {number} lastFrame - The frame to end the animation at.
* @example overrideHandAnimation()
Gets the overrides the default hand poses that are triggered with controller buttons.
- * use {@link MyAvatar.restoreHandAnimation}. to restore the default poses.
+ * Overrides the default hand poses that are triggered with controller buttons.
+ * Use {@link MyAvatar.restoreHandAnimation} to restore the default poses.
* @function MyAvatar.overrideHandAnimation
- * @param isLeft {boolean} Set true if using the left hand
- * @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the
+ * @param isLeft {boolean} true
to override the left hand, false
to override the right hand.
+ * @param {string} url - The URL of the animation file. Animation files need to be FBX format, but only need to contain the
* avatar skeleton and animation data.
- * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
- * @param loop {boolean} Set to true if the animation should loop.
- * @param firstFrame {number} The frame the animation should start at.
- * @param lastFrame {number} The frame the animation should end at
- * @example true
if the animation should loop, false
if it shouldn't.
+ * @param {number} firstFrame - The frame to start the animation at.
+ * @param {number} lastFrame - The frame to end the animation at.
+ * @example The avatar animation system includes a set of default animations along with rules for how those animations are blended
* together with procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will
- * override the default animations. restoreHandAnimation()
is used to restore the default hand poses
- * If you aren't currently playing an override hand
- * animation, this function has no effect.
restoreHandAnimation()
is used to restore the default hand poses.
+ * If you aren't currently playing an override hand animation, this function has no effect.
* @function MyAvatar.restoreHandAnimation
* @param isLeft {boolean} Set to true if using the left hand
* @example true
if the animation should loop, false
if it shouldn't.
+ * @param {number} firstFrame - The frame the animation should start at.
+ * @param {number} lastFrame - The frame the animation should end at.
* @example true
to do snap turns in HMD mode; false
to do smooth turns in HMD mode.
*/
Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; }
- /**
+ /**jsdoc
+ * Gets the control scheme that is in use.
* @function MyAvatar.getControlScheme
- * @returns {number}
- */
+ * @returns {MyAvatar.LocomotionControlsMode} The control scheme that is in use.
+ */
Q_INVOKABLE int getControlScheme() const { return _controlSchemeIndex; }
- /**
+ /**jsdoc
+ * Sets the control scheme to use.
* @function MyAvatar.setControlScheme
- * @param {number} index
- */
+ * @param {MyAvatar.LocomotionControlsMode} controlScheme - The control scheme to use.
+ */
Q_INVOKABLE void setControlScheme(int index) { _controlSchemeIndex = (index >= 0 && index <= 2) ? index : 0; }
/**jsdoc
+ * Gets whether your avatar hovers when its feet are not on the ground.
* @function MyAvatar.hoverWhenUnsupported
- * @returns {boolean}
+ * @returns {boolean} true
if your avatar hovers when its feet are not on the ground, false
if it
+ * falls.
*/
+ // FIXME: Should be named, getHoverWhenUnsupported().
Q_INVOKABLE bool hoverWhenUnsupported() const { return _hoverWhenUnsupported; }
+
/**jsdoc
+ * Sets whether your avatar hovers when its feet are not on the ground.
* @function MyAvatar.setHoverWhenUnsupported
- * @param {boolean} on
+ * @param {boolean} hover - true
if your avatar hovers when its feet are not on the ground, false
+ * if it falls.
*/
Q_INVOKABLE void setHoverWhenUnsupported(bool on) { _hoverWhenUnsupported = on; }
@@ -826,26 +859,31 @@ public:
* @returns {string} "left"
for the left hand, "right"
for the right hand.
*/
Q_INVOKABLE QString getDominantHand() const;
+
/**jsdoc
- * @function MyAVatar.setStrafeEnabled
- * @param {bool} enabled
- */
+ * Sets whether strafing is enabled.
+ * @function MyAvatar.setStrafeEnabled
+ * @param {boolean} enabled - true
if strafing is enabled, false
if it isn't.
+ */
Q_INVOKABLE void setStrafeEnabled(bool enabled);
+
/**jsdoc
- * @function MyAvatar.getStrafeEnabled
- * @returns {bool}
- */
+ * Gets whether strafing is enabled.
+ * @function MyAvatar.getStrafeEnabled
+ * @returns {boolean} true
if strafing is enabled, false
if it isn't.
+ */
Q_INVOKABLE bool getStrafeEnabled() const;
+
/**jsdoc
+ * Sets the HMD alignment relative to your avatar.
* @function MyAvatar.setHmdAvatarAlignmentType
* @param {string} type - "head"
to align your head and your avatar's head, "eyes"
to align your
* eyes and your avatar's eyes.
- *
*/
Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& type);
/**jsdoc
- * Gets the HMD alignment for your avatar.
+ * Gets the HMD alignment relative to your avatar.
* @function MyAvatar.getHmdAvatarAlignmentType
* @returns {string} "head"
if aligning your head and your avatar's head, "eyes"
if aligning your
* eyes and your avatar's eyes.
@@ -1495,18 +1533,8 @@ public:
*/
Q_INVOKABLE float getDriveGear5();
- /**jsdoc
- * Choose the control scheme.
- * @function MyAvatar.setControlSchemeIndex
- * @param {number} Choose the control scheme to be used.
- */
void setControlSchemeIndex(int index);
- /**jsdoc
- * Check what control scheme is in use.
- * @function MyAvatar.getControlSchemeIndex
- * @returns {number} Returns the index associated with a given control scheme.
- */
int getControlSchemeIndex();
/**jsdoc
@@ -1584,8 +1612,8 @@ public:
Q_INVOKABLE bool getCharacterControllerEnabled(); // deprecated
/**jsdoc
- * @comment Different behavior to the Avatar version of this method.
* Gets the rotation of a joint relative to the avatar.
+ * @comment Different behavior to the Avatar version of this method.
* @function MyAvatar.getAbsoluteJointRotationInObjectFrame
* @param {number} index - The index of the joint.
* @returns {Quat} The rotation of the joint relative to the avatar.
@@ -1597,8 +1625,8 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
/**jsdoc
- * @comment Different behavior to the Avatar version of this method.
* Gets the translation of a joint relative to the avatar.
+ * @comment Different behavior to the Avatar version of this method.
* @function MyAvatar.getAbsoluteJointTranslationInObjectFrame
* @param {number} index - The index of the joint.
* @returns {Vec3} The translation of the joint relative to the avatar.
@@ -2441,6 +2469,9 @@ private:
void updateEyeContactTarget(float deltaTime);
// These are made private for MyAvatar so that you will use the "use" methods instead
+ /**jsdoc
+ * @comment Borrows the base class's JSDoc.
+ */
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
virtual void updatePalms() override {}
diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp
index 4f8b86cc0c..7f9a612697 100644
--- a/interface/src/graphics/GraphicsEngine.cpp
+++ b/interface/src/graphics/GraphicsEngine.cpp
@@ -132,6 +132,10 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI
bool GraphicsEngine::shouldPaint() const {
auto displayPlugin = qApp->getActiveDisplayPlugin();
+ if (!displayPlugin) {
+ // We're shutting down
+ return false;
+ }
#ifdef DEBUG_PAINT_DELAY
static uint64_t paintDelaySamples{ 0 };
@@ -175,6 +179,10 @@ void GraphicsEngine::render_performFrame() {
{
PROFILE_RANGE(render, "/getActiveDisplayPlugin");
displayPlugin = qApp->getActiveDisplayPlugin();
+ if (!displayPlugin) {
+ // We're shutting down
+ return;
+ }
}
{
diff --git a/interface/src/main.cpp b/interface/src/main.cpp
index 7fc4a5b651..3fd65f452c 100644
--- a/interface/src/main.cpp
+++ b/interface/src/main.cpp
@@ -85,6 +85,7 @@ int main(int argc, const char* argv[]) {
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts InteractiveWindow
.
* @typedef {object} InteractiveWindow.Properties
@@ -152,12 +162,16 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
_dockWidget->getQuickView()->rootContext()->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
QObject::connect(rootItem, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)),
Qt::QueuedConnection);
+ QObject::connect(rootItem, SIGNAL(keyPressEvent(int, int)), this, SLOT(forwardKeyPressEvent(int, int)),
+ Qt::QueuedConnection);
+ QObject::connect(rootItem, SIGNAL(keyReleaseEvent(int, int)), this, SLOT(forwardKeyReleaseEvent(int, int)),
+ Qt::QueuedConnection);
emit mainWindow->windowGeometryChanged(qApp->getWindow()->geometry());
}
});
+
_dockWidget->setSource(QUrl(sourceUrl));
-
mainWindow->addDockWidget(dockArea, _dockWidget.get());
} else {
auto offscreenUi = DependencyManager::getlodAngle
property changes.
+ * @function Stats.lodAngleChanged
+ * @returns {Signal}
+ */
+ void lodAngleChanged();
+
+ /**jsdoc
+ * Triggered when the value of the lodTargetFramerate
property changes.
+ * @function Stats.lodTargetFramerateChanged
+ * @returns {Signal}
+ */
+ void lodTargetFramerateChanged();
+
/**jsdoc
* Triggered when the value of the lodStatus
property changes.
* @function Stats.lodStatusChanged
diff --git a/launchers/darwin/CMakeLists.txt b/launchers/darwin/CMakeLists.txt
index d8baea6e3c..2de43d93cb 100644
--- a/launchers/darwin/CMakeLists.txt
+++ b/launchers/darwin/CMakeLists.txt
@@ -66,6 +66,10 @@ endfunction()
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME})
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
+if (LAUNCHER_HMAC_SECRET STREQUAL "")
+ message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set")
+endif()
+
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_HMAC_SECRET="${LAUNCHER_HMAC_SECRET}")
file(GLOB NIB_FILES "nib/*.xib")
diff --git a/launchers/darwin/images/interface.icns b/launchers/darwin/images/interface.icns
index 4aeb8301ce..8dadfd5037 100644
Binary files a/launchers/darwin/images/interface.icns and b/launchers/darwin/images/interface.icns differ
diff --git a/launchers/darwin/nib/Window.xib b/launchers/darwin/nib/Window.xib
index 8260f1d3cd..48948e3825 100644
--- a/launchers/darwin/nib/Window.xib
+++ b/launchers/darwin/nib/Window.xib
@@ -11,7 +11,7 @@
uuid
.
+ * @property {number} stableOrder - The order in which the button was created: each button created gets a value incremented by
+ * one.
+ *
+ * @property {string} icon - The url of the default button icon displayed. (50 x 50 pixels. SVG, PNG, or other image format.)
+ * @property {string} hoverIcon - The url of the button icon displayed when the button is hovered and not active.
+ * @property {string} activeIcon - The url of the button icon displayed when the button is active.
+ * @property {string} activeHoverIcon - The url of the button icon displayed when the button is hovered and active.
+ * @property {string} text - The button caption.
+ * @property {string} hoverText - The button caption when the button is hovered and not active.
+ * @property {string} activeText - The button caption when the button is active.
+ * @property {string} activeHoverText - The button caption when the button is hovered and active.
+ * @comment {string} defaultCaptionColor="#ffffff" - Internal property.
+ * @property {string} captionColor="#ffffff" - The color of the button caption.
+
+ * @property {boolean} isActive=false - true
if the button is active, false
if it isn't.
+ * @property {boolean} isEntered - true
if the button is being hovered, false
if it isn't.
+ * @property {boolean} buttonEnabled=true - true
if the button is enabled, false
if it is disabled.
+ * @property {number} sortOrder=100 - Determines the order of the buttons: buttons with lower numbers appear before buttons
+ * with larger numbers.
+ *
+ * @property {boolean} inDebugMode - If true
and the tablet is being used, the button's isActive
+ * state toggles each time the button is clicked. Tablet only.
+ *
+ * @comment {object} tabletRoot - Internal tablet-only property.
+ * @property {object} flickable - Internal tablet-only property.
+ * @property {object} gridView - Internal tablet-only property.
+ * @property {number} buttonIndex - Internal tablet-only property.
+ *
+ * @comment {number} imageOffOut - Internal toolbar-only property.
+ * @comment {number} imageOffIn - Internal toolbar-only property.
+ * @comment {number} imageOnOut - Internal toolbar-only property.
+ * @comment {number} imageOnIn - Internal toolbar-only property.
+ */
TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) :
_uuid(QUuid::createUuid()),
_stableOrder(++s_stableOrder),
@@ -977,6 +1017,7 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) :
_properties[UUID_KEY] = _uuid;
_properties[OBJECT_NAME_KEY] = _uuid.toString();
_properties[STABLE_ORDER_KEY] = _stableOrder;
+ // Other properties are defined in TabletButton.qml and ToolbarButton.qml.
if (QThread::currentThread() != qApp->thread()) {
qCWarning(uiLogging) << "Creating tablet button proxy on wrong thread";
}
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h
index 0b8dc95fa4..afd77a15f6 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.h
+++ b/libraries/ui/src/ui/TabletScriptingInterface.h
@@ -39,6 +39,10 @@ class QmlWindowClass;
class OffscreenQmlSurface;
/**jsdoc
+ * The Tablet
API provides the facilities to work with the system or other tablet. In toolbar mode (Developer >
+ * UI > Tablet Becomes Toolbar), the tablet's menu buttons are displayed in a toolbar and other tablet content is displayed
+ * in a dialog.
+ *
* @namespace Tablet
*
* @hifi-interface
@@ -46,6 +50,8 @@ class OffscreenQmlSurface;
* @hifi-avatar
*/
/**jsdoc
+ * The tabletInterface
API provides the facilities to work with the system or other tablet.
+ *
* @namespace tabletInterface
*
* @hifi-interface
@@ -53,12 +59,17 @@ class OffscreenQmlSurface;
* @hifi-avatar
*
* @deprecated This API is deprecated and will be removed. Use {@link Tablet} instead.
+ *
+ * @borrows Tablet.getTablet as getTablet
+ * @borrows Tablet.playSound as playSound
+ * @borrows Tablet.tabletNotification as tabletNotification
*/
class TabletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
/**jsdoc
+ * Standard tablet sounds.
* Value | Description |
---|---|
2 | Tablet open. |
3 | Tablet hands in. |
4 | Tablet hands out. |
5 | Last. |
Note: Only triggered if the script is running in the same script engine as the script that created + * the tablet. By default, this means in scripts included as part of the default scripts.
* @function Tablet.tabletNotification * @returns {Signal} */ - /**jsdoc - * Triggered when a tablet message or dialog is created. - * @function tabletInterface.tabletNotification - * @returns {Signal} - */ void tabletNotification(); private: @@ -149,7 +154,9 @@ protected: }; /**jsdoc - * @typedef {object} TabletProxy#ButtonList + * Information on the buttons in the tablet main menu (toolbar in toolbar mode) for use in QML. Has properties and functions + * per http://doc.qt.io/qt-5/qabstractlistmodel.html. + * @typedef {object} TabletProxy.TabletButtonListModel */ class TabletButtonListModel : public QAbstractListModel { Q_OBJECT @@ -203,18 +210,27 @@ private: Q_DECLARE_METATYPE(TabletButtonsProxyModel*); /**jsdoc + * An instance of a tablet. In toolbar mode (Developer > + * UI > Tablet Becomes Toolbar), the tablet's menu buttons are displayed in a toolbar and other tablet content is displayed + * in a dialog. + * + *Create a new tablet or retrieve an existing tablet using {@link Tablet.getTablet}.
+ * * @class TabletProxy - * + * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {string} name - Name of this tablet. Read-only. - * @property {boolean} toolbarMode - Used to transition this tablet into and out of toolbar mode. - * When tablet is in toolbar mode, all its buttons will appear in a floating toolbar. - * @property {boolean} landscape - * @property {boolean} tabletShown Read-only. - * @property {TabletProxy#ButtonList} buttons Read-only. + * @property {string} name - A unique name that identifies the tablet. Read-only. + * @property {boolean} toolbarMode -true
if the tablet is in toolbar mode, false
if it isn't.
+ * @property {boolean} landscape - true
if the tablet is displayed in landscape mode, false
if it is
+ * displayed in portrait mode.
+ * Note: This property isn't used in toolbar mode.
+ * @property {boolean} tabletShown -true
if the tablet is currently displayed, false
if it isn't.
+ * Note: This property isn't used in toolbar mode.
+ * @property {TabletProxy.TabletButtonListModel} buttons - Information on the buttons in the tablet main menu (or toolbar in + * toolbar mode) for use in QML. Read-only. */ class TabletProxy : public QObject { Q_OBJECT @@ -235,140 +251,190 @@ public: void unfocus(); /**jsdoc + * Displays the tablet menu. The tablet is opened if it isn't already open. * @function TabletProxy#gotoMenuScreen - * @param {string} [submenu=""] + * @param {string} [submenu=""] - The name of a submenu to display, if any. + * @exampletrue
, the web page or app is displayed in a frame with "back"
+ * and "close" buttons.
*/
Q_INVOKABLE void gotoWebScreen(const QString& url);
Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase = false);
/**jsdoc
+ * Opens a QML app or dialog on the tablet.
* @function TabletProxy#loadQMLSource
- * @param {string} path
- * @param {boolean} [resizable=false]
+ * @param {string} path - The path of the QML app or dialog.
+ * @param {boolean} [resizable=false] - true
to make the dialog resizable in toolbar mode, false
+ * to have it not resizable.
*/
Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false);
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
// it should be initialized internally so it cannot fail
/**jsdoc
+ * Displays a QML dialog over the top of the current dialog, without closing the current dialog. Use
+ * {@link TabletProxy#popFromStack|popFromStack} to close the dialog.
+ * If the current dialog or its ancestors contain a QML StackView
with objectName: "stack"
and
+ * function pushSource(path)
, that function is called; otherwise,
+ * {@link TabletProxy#loadQMLSource|loadQMLSource} is called. The Create app provides an example of using a QML
+ * StackView
.
true
if the dialog was successfully opened, false
if it wasn't.
*/
+ // edit.js provides an example of using this outside of main menu.
Q_INVOKABLE bool pushOntoStack(const QVariant& path);
/**jsdoc
+ * Closes a QML dialog that was displayed using {@link Tablet#pushOntoStack|pushOntoStack} with a dialog implementing a QML
+ * StackView
; otherwise, no action is taken.
+ * If using a QML StackView
, its popSource()
function is called.
true
if a modal, non-modal, or message dialog is open, false
if there isn't.
*/
Q_INVOKABLE bool isMessageDialogOpen();
/**jsdoc
- * Close any open dialogs.
- * @function TabletProxy#closeDialog
- */
+ * Closes any open modal, non-modal, or message dialog, opened by {@link Window.prompt}, {@link Window.promptAsync},
+ * {@link Window.openMessageBox}, or similar.
+ * @function TabletProxy#closeDialog
+ */
Q_INVOKABLE void closeDialog();
/**jsdoc
- * Creates a new button, adds it to this and returns it.
+ * Adds a new button to the tablet menu.
* @function TabletProxy#addButton
- * @param {object} properties - Button properties.
- * @returns {TabletButtonProxy}
+ * @param {TabletButtonProxy.ButtonProperties} properties - Button properties.
+ * @returns {TabletButtonProxy} The button added.
+ * @example EventBridge
that is automatically provided to the script:
+ * EventBridge.scriptEventReceived.connect(function(message) {
+ * ...
+ * });
* @function TabletProxy#emitScriptEvent
- * @param {object|string} message
+ * @param {string|object} message - The message to send to the web page.
*/
Q_INVOKABLE void emitScriptEvent(const QVariant& msg);
/**jsdoc
- * Used to send an event to the QML embedded in the tablet.
+ * Sends a message to the current QML page. To receive the message, the QML page must implement a function:
+ * function fromScript(message) {
+ * ...
+ * }
* @function TabletProxy#sendToQml
- * @param {object|string} message
+ * @param {string|object} message - The message to send to the QML page.
*/
Q_INVOKABLE void sendToQml(const QVariant& msg);
/**jsdoc
- * Check if the tablet is on the home screen.
+ * Checks if the tablet is on the home screen.
* @function TabletProxy#onHomeScreen
- * @returns {boolean}
+ * @returns {boolean} true
if the tablet is on the home screen, false
if it isn't.
*/
Q_INVOKABLE bool onHomeScreen();
/**jsdoc
- * Set tablet into or out of landscape mode.
+ * Sets whether the tablet is displayed in landscape or portrait mode.
+ * Note: The setting isn't used in toolbar mode.
* @function TabletProxy#setLandscape - * @param {boolean} landscape -true
for landscape, true
to display the tablet in landscape mode, false
to display it
+ * in portrait mode.
*/
Q_INVOKABLE void setLandscape(bool landscape) { _landscape = landscape; }
/**jsdoc
+ * Gets whether the tablet is displayed in landscape or portrait mode.
+ * Note: The setting isn't used in toolbar mode.
* @function TabletProxy#getLandscape - * @returns {boolean} + * @returns {boolean}true
if the tablet is displayed in landscape mode, false
if it is displayed
+ * in portrait mode.
*/
Q_INVOKABLE bool getLandscape() { return _landscape; }
/**jsdoc
+ * Checks if a path is the current app or dialog displayed.
* @function TabletProxy#isPathLoaded
- * @param {string} path
- * @returns {boolean}
+ * @param {string} path - The path to test.
+ * @returns {boolean} true
if path
is the current app or dialog, false
if it isn't.
*/
Q_INVOKABLE bool isPathLoaded(const QVariant& path);
@@ -384,44 +450,74 @@ public:
signals:
/**jsdoc
- * Signaled when this tablet receives an event from the html/js embedded in the tablet.
+ * Triggered when a message from the current HTML web page displayed on the tablet is received. The HTML web page can send
+ * a message by calling:
+ * EventBridge.emitWebEvent(message);
* @function TabletProxy#webEventReceived
- * @param {object|string} message
+ * @param {string|object} message - The message received.
* @returns {Signal}
*/
void webEventReceived(QVariant msg);
/**jsdoc
- * Signaled when this tablet receives an event from the qml embedded in the tablet.
+ * Triggered when a message from the current QML page displayed on the tablet is received. The QML page can send a message
+ * (string or object) by calling: sendToScript(message);
* @function TabletProxy#fromQml
- * @param {object|string} message
+ * @param {string|object} message - The message received.
* @returns {Signal}
*/
void fromQml(QVariant msg);
/**jsdoc
- * Signaled when this tablet screen changes.
+ * Triggered when the tablet's screen changes.
* @function TabletProxy#screenChanged
- * @param type {string} - "Home", "Web", "Menu", "QML", "Closed".
- * @param url {string} - Only valid for Web and QML.
+ * @param type {string} - The type of the new screen or change: "Home"
, "Menu"
,
+ * "QML"
, "Web"
, "Closed"
, or "Unknown"
.
+ * @param url {string} - The url of the page displayed. Only valid for Web and QML.
+ * @returns {Signal}
*/
void screenChanged(QVariant type, QVariant url);
/**jsdoc
- * Signaled when the tablet becomes visible or becomes invisible.
- * @function TabletProxy#isTabletShownChanged
+ * Triggered when the tablet is opened or closed.
+ * Note: Doesn't apply in toolbar mode.
+ * @function TabletProxy#tabletShownChanged * @returns {Signal} */ void tabletShownChanged(); /**jsdoc + * Triggered when the tablet's toolbar mode changes. * @function TabletProxy#toolbarModeChanged + * @returns {Signal} + * @exampleCreate a new button using {@link TabletProxy#addButton}.
+ * * @class TabletButtonProxy * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {Uuid} uuid - Uniquely identifies this button. Read-only. - * @property {TabletButtonProxy.ButtonProperties} properties + * @property {Uuid} uuid - The ID of the button. Read-only. + * @property {TabletButtonProxy.ButtonProperties} properties - The current values of the button's properties. Only properties + * that have been set during button creation or subsequently edited are returned. Read-only. */ class TabletButtonProxy : public QObject { Q_OBJECT @@ -472,28 +574,66 @@ public: QUuid getUuid() const { return _uuid; } /**jsdoc - * Returns the current value of this button's properties. + * Gets the current values of the button's properties. Only properties that have been set during button creation or + * subsequently edited are returned. * @function TabletButtonProxy#getProperties - * @returns {TabletButtonProxy.ButtonProperties} + * @returns {TabletButtonProxy.ButtonProperties} The button properties. + * @exampletrue
when button is active.
- * @property {number} sortOrder - Determines sort order on tablet. lower numbers will appear before larger numbers.
- * Default is 100.
- */
// FIXME: There are additional properties.
QVariantMap _properties;
};
diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.h b/libraries/ui/src/ui/ToolbarScriptingInterface.h
index 409ea28fdc..952d3cce95 100644
--- a/libraries/ui/src/ui/ToolbarScriptingInterface.h
+++ b/libraries/ui/src/ui/ToolbarScriptingInterface.h
@@ -150,6 +150,9 @@ public:
* @returns {ToolbarProxy}
*/
Q_INVOKABLE ToolbarProxy* getToolbar(const QString& toolbarId);
+
+signals:
+ void toolbarVisibleChanged(bool isVisible, QString toolbarName);
};
diff --git a/scripts/developer/utilities/render/lodWindow.js b/scripts/developer/utilities/render/lodWindow.js
new file mode 100644
index 0000000000..2f6b403a48
--- /dev/null
+++ b/scripts/developer/utilities/render/lodWindow.js
@@ -0,0 +1,5 @@
+var window = Desktop.createWindow(Script.resolvePath('./lod.qml'), {
+ title: "LOD Setup",
+ presentationMode: Desktop.PresentationMode.NATIVE,
+ size: {x: 350, y: 500}
+});
diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml
index 2dc8fda081..98fd3039d1 100644
--- a/scripts/developer/utilities/render/luci.qml
+++ b/scripts/developer/utilities/render/luci.qml
@@ -26,8 +26,9 @@ Rectangle {
color: global.color
ScrollView {
- id: control
+ id: scrollView
anchors.fill: parent
+ contentWidth: parent.width
clip: true
Column {
diff --git a/scripts/developer/utilities/render/performanceSetup.qml b/scripts/developer/utilities/render/performanceSetup.qml
index ab00d31f2b..4654736f72 100644
--- a/scripts/developer/utilities/render/performanceSetup.qml
+++ b/scripts/developer/utilities/render/performanceSetup.qml
@@ -24,9 +24,12 @@ Rectangle {
color: global.colorBack
ScrollView {
+ id: scrollView
anchors.fill: parent
+ contentWidth: parent.width
clip: true
- Column {
+
+ Column {
anchors.left: parent.left
anchors.right: parent.right
@@ -35,8 +38,6 @@ Rectangle {
isUnfold: true
panelFrameData: Component {
PerformanceSettings {
- anchors.left: parent.left
- anchors.right: parent.right
}
}
}
@@ -45,8 +46,6 @@ Rectangle {
isUnfold: true
panelFrameData: Component {
RenderSettings {
- anchors.left: parent.left
- anchors.right: parent.right
}
}
}
@@ -54,8 +53,6 @@ Rectangle {
label: "Platform"
panelFrameData: Component {
Platform {
- anchors.left: parent.left
- anchors.right: parent.right
}
}
}
diff --git a/scripts/simplifiedUI/system/progress.js b/scripts/simplifiedUI/system/progress.js
index b373612790..a641dd4556 100644
--- a/scripts/simplifiedUI/system/progress.js
+++ b/scripts/simplifiedUI/system/progress.js
@@ -83,9 +83,7 @@
// The initial delay cooldown keeps us from tracking progress before the allotted time
// has passed.
INITIAL_DELAY_COOLDOWN_TIME = 1000,
- initialDelayCooldown = 0,
-
- isInInterstitialMode = false;
+ initialDelayCooldown = 0;
function fade() {
@@ -267,7 +265,7 @@
// Update state
if (!visible) { // Not visible because no recent downloads
- if ((displayProgress < 100 || gpuTextures > 0) && !isInInterstitialMode && !isInterstitialOverlaysVisible) { // Have started downloading so fade in
+ if (displayProgress < 100 || gpuTextures > 0) { // Have started downloading so fade in
visible = true;
alphaDelta = ALPHA_DELTA_IN;
fadeTimer = Script.setInterval(fade, FADE_INTERVAL);
@@ -307,9 +305,6 @@
} else {
x = x * BAR_HMD_REPEAT;
}
- if (isInInterstitialMode || isInterstitialOverlaysVisible) {
- visible = false;
- }
// Update progress bar
Overlays.editOverlay(barDesktop.overlay, {
@@ -349,10 +344,6 @@
}
}
- function interstitialModeChanged(inMode) {
- isInInterstitialMode = inMode;
- }
-
function setUp() {
var is4k = Window.innerWidth > 3000;
@@ -378,7 +369,6 @@
}
setUp();
- Window.interstitialModeChanged.connect(interstitialModeChanged);
GlobalServices.downloadInfoChanged.connect(onDownloadInfoChanged);
GlobalServices.updateDownloadInfo();
Script.setInterval(update, 1000 / 60);
diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js
index cdf6a9591a..70679b09bd 100644
--- a/scripts/simplifiedUI/ui/simplifiedUI.js
+++ b/scripts/simplifiedUI/ui/simplifiedUI.js
@@ -415,7 +415,7 @@ function getInputDeviceMutedOverlayTopY() {
var inputDeviceMutedOverlay = false;
var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_X_PX = 353;
var INPUT_DEVICE_MUTED_OVERLAY_DEFAULT_Y_PX = 95;
-var INPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20;
+var INPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20 + TOP_BAR_HEIGHT_PX;
function updateInputDeviceMutedOverlay(isMuted) {
if (isMuted) {
var props = {
@@ -457,12 +457,21 @@ function onGeometryChanged(rect) {
}
}
-function ensureFirstPersonCameraInHMD(isHMDMode) {
+function onDisplayModeChanged(isHMDMode) {
if (isHMDMode) {
Camera.setModeString("first person");
}
}
+function onToolbarVisibleChanged(isVisible, toolbarName) {
+ if (isVisible && toolbarName == TOOLBAR_NAME && !Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false)) {
+ var toolbar = Toolbars.getToolbar(toolbarName);
+ if (toolbar) {
+ toolbar.writeProperty("visible", false);
+ }
+ }
+}
+
function onStatusChanged() {
sendLocalStatusToQml();
@@ -490,7 +499,9 @@ function startup() {
if (!HMD.active) {
var toolbar = Toolbars.getToolbar(TOOLBAR_NAME);
- toolbar.writeProperty("visible", false);
+ if (toolbar) {
+ toolbar.writeProperty("visible", false);
+ }
}
}
@@ -505,11 +516,12 @@ function startup() {
updateOutputDeviceMutedOverlay(isOutputMuted());
Audio.mutedDesktopChanged.connect(onDesktopInputDeviceMutedChanged);
Window.geometryChanged.connect(onGeometryChanged);
- HMD.displayModeChanged.connect(ensureFirstPersonCameraInHMD);
+ HMD.displayModeChanged.connect(onDisplayModeChanged);
Audio.avatarGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
Audio.localInjectorGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
Audio.serverInjectorGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
Audio.systemInjectorGainChanged.connect(maybeUpdateOutputDeviceMutedOverlay);
+ Toolbars.toolbarVisibleChanged.connect(onToolbarVisibleChanged);
oldShowAudioTools = AvatarInputs.showAudioTools;
AvatarInputs.showAudioTools = false;
@@ -535,7 +547,9 @@ function shutdown() {
if (!HMD.active) {
var toolbar = Toolbars.getToolbar(TOOLBAR_NAME);
- toolbar.writeProperty("visible", true);
+ if (toolbar) {
+ toolbar.writeProperty("visible", true);
+ }
}
}
@@ -559,11 +573,12 @@ function shutdown() {
Audio.mutedDesktopChanged.disconnect(onDesktopInputDeviceMutedChanged);
Window.geometryChanged.disconnect(onGeometryChanged);
- HMD.displayModeChanged.disconnect(ensureFirstPersonCameraInHMD);
+ HMD.displayModeChanged.disconnect(onDisplayModeChanged);
Audio.avatarGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
Audio.localInjectorGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
Audio.serverInjectorGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
Audio.systemInjectorGainChanged.disconnect(maybeUpdateOutputDeviceMutedOverlay);
+ Toolbars.toolbarVisibleChanged.disconnect(onToolbarVisibleChanged);
AvatarInputs.showAudioTools = oldShowAudioTools;
AvatarInputs.showBubbleTools = oldShowBubbleTools;
diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js
index d19da2b342..6498c92f17 100644
--- a/scripts/system/libraries/entityList.js
+++ b/scripts/system/libraries/entityList.js
@@ -10,7 +10,7 @@
/* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages,
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible,
- keyUpEventFromUIWindow */
+ keyUpEventFromUIWindow, Script, SelectionDisplay, SelectionManager, Clipboard */
var PROFILING_ENABLED = false;
var profileIndent = '';
@@ -148,6 +148,20 @@ EntityListTool = function(shouldUseEditTabletApp) {
return value !== undefined ? value : "";
}
+ function entityIsBaked(properties) {
+ if (properties.type === "Model") {
+ var lowerModelURL = properties.modelURL.toLowerCase();
+ return lowerModelURL.endsWith(".baked.fbx") || lowerModelURL.endsWith(".baked.fst");
+ } else if (properties.type === "Zone") {
+ var lowerSkyboxURL = properties.skybox ? properties.skybox.url.toLowerCase() : "";
+ var lowerAmbientURL = properties.ambientLight ? properties.ambientLight.ambientURL.toLowerCase() : "";
+ return (lowerSkyboxURL === "" || lowerSkyboxURL.endsWith(".texmeta.json")) &&
+ (lowerAmbientURL === "" || lowerAmbientURL.endsWith(".texmeta.json"));
+ } else {
+ return false;
+ }
+ }
+
that.sendUpdate = function() {
PROFILE('Script-sendUpdate', function() {
var entities = [];
@@ -164,7 +178,8 @@ EntityListTool = function(shouldUseEditTabletApp) {
var cameraPosition = Camera.position;
PROFILE("getMultipleProperties", function () {
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['name', 'type', 'locked',
- 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID']);
+ 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID',
+ 'skybox.url', 'ambientLight.url']);
for (var i = 0; i < multipleProperties.length; i++) {
var properties = multipleProperties[i];
@@ -193,7 +208,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
valueIfDefined(properties.renderInfo.texturesSize) : ""),
hasTransparent: (properties.renderInfo !== undefined ?
valueIfDefined(properties.renderInfo.hasTransparent) : ""),
- isBaked: properties.type === "Model" ? url.toLowerCase().endsWith(".baked.fbx") : false,
+ isBaked: entityIsBaked(properties),
drawCalls: (properties.renderInfo !== undefined ?
valueIfDefined(properties.renderInfo.drawCalls) : ""),
hasScript: properties.script !== ""
diff --git a/tools/ci-scripts/postbuild.py b/tools/ci-scripts/postbuild.py
index b6593b1cf3..00b3007104 100644
--- a/tools/ci-scripts/postbuild.py
+++ b/tools/ci-scripts/postbuild.py
@@ -3,6 +3,7 @@ import os
import sys
import shutil
import zipfile
+import base64
SOURCE_PATH = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..', '..'))
# FIXME move the helper python modules somewher other than the root of the repo
@@ -111,10 +112,43 @@ def fixupWinZip(filename):
print("Replacing {} with fixed {}".format(fullPath, outFullPath))
shutil.move(outFullPath, fullPath)
-def buildLightLauncher():
- # FIXME remove once MFC is enabled on the windows build hosts
- if sys.platform == 'win32':
+def signBuild(executablePath):
+ if sys.platform != 'win32':
+ print('Skipping signing because platform is not win32')
return
+
+ RELEASE_TYPE = os.getenv("RELEASE_TYPE", "")
+ if RELEASE_TYPE != "PRODUCTION":
+ print('Skipping signing because RELEASE_TYPE "{}" != "PRODUCTION"'.format(RELEASE_TYPE))
+ return
+
+ HF_PFX_FILE = os.getenv("HF_PFX_FILE", "")
+ if HF_PFX_FILE == "":
+ print('Skipping signing because HF_PFX_FILE is empty')
+ return
+
+ HF_PFX_PASSPHRASE = os.getenv("HF_PFX_PASSPHRASE", "")
+ if HF_PFX_PASSPHRASE == "":
+ print('Skipping signing because HF_PFX_PASSPHRASE is empty')
+ return
+
+ # FIXME use logic similar to the SetPackagingParameteres.cmake to locate the executable
+ SIGN_TOOL = "C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x64/signtool.exe"
+ # sign the launcher executable
+ print("Signing {}".format(executablePath))
+ hifi_utils.executeSubprocess([
+ SIGN_TOOL,
+ 'sign',
+ '/fd', 'sha256',
+ '/f', HF_PFX_FILE,
+ '/p', HF_PFX_PASSPHRASE,
+ '/tr', 'http://sha256timestamp.ws.symantec.com/sha256/timestamp',
+ '/td', 'SHA256',
+ executablePath
+ ])
+
+
+def buildLightLauncher():
launcherSourcePath = os.path.join(SOURCE_PATH, 'launchers', sys.platform)
launcherBuildPath = os.path.join(BUILD_PATH, 'launcher')
if not os.path.exists(launcherBuildPath):
@@ -144,13 +178,16 @@ def buildLightLauncher():
launcherDestFile = os.path.join(BUILD_PATH, "{}.dmg".format(computeArchiveName('Launcher')))
launcherSourceFile = os.path.join(launcherBuildPath, "HQ Launcher.dmg")
elif sys.platform == 'win32':
- # FIXME
launcherDestFile = os.path.join(BUILD_PATH, "{}.exe".format(computeArchiveName('Launcher')))
- launcherSourceFile = os.path.join(launcherBuildPath, "Launcher.exe")
+ launcherSourceFile = os.path.join(launcherBuildPath, "Release", "HQLauncher.exe")
+
print("Moving {} to {}".format(launcherSourceFile, launcherDestFile))
shutil.move(launcherSourceFile, launcherDestFile)
+ signBuild(launcherDestFile)
+
+# Main
for wipePath in WIPE_PATHS:
wipeClientBuildPath(wipePath)