Merge pull request #15601 from zfox23/SUI/integratedStatusEtc

Simplified UI: Updates! (See Description)
This commit is contained in:
Zach Fox 2019-05-21 16:15:09 -07:00 committed by GitHub
commit 39d32025a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 400 additions and 33 deletions

View file

@ -19,8 +19,8 @@ Flickable {
id: root
contentWidth: parent.width
contentHeight: audioColumnLayout.height
topMargin: 16
bottomMargin: 16
topMargin: 24
bottomMargin: 24
clip: true
function changePeakValuesEnabled(enabled) {
@ -189,7 +189,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.input
delegate: Item {
@ -200,6 +200,8 @@ Flickable {
id: inputDeviceCheckbox
anchors.left: parent.left
width: parent.width - inputLevel.width
height: paintedHeight
wrapLabel: false
checked: selectedDesktop
text: model.devicename
ButtonGroup.group: inputDeviceButtonGroup
@ -288,7 +290,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.output
delegate: Item {
@ -299,8 +301,10 @@ Flickable {
id: outputDeviceCheckbox
anchors.left: parent.left
width: parent.width
height: paintedHeight
checked: selectedDesktop
text: model.devicename
wrapLabel: false
ButtonGroup.group: outputDeviceButtonGroup
onClicked: {
AudioScriptingInterface.setOutputDevice(model.info, false); // `false` argument for Desktop mode setting

View file

@ -20,8 +20,8 @@ Flickable {
id: root
contentWidth: parent.width
contentHeight: generalColumnLayout.height
topMargin: 16
bottomMargin: 16
topMargin: 24
bottomMargin: 24
clip: true
onAvatarNametagModeChanged: {
@ -63,6 +63,7 @@ Flickable {
ColumnLayout {
id: avatarNameTagsRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
SimplifiedControls.RadioButton {
id: avatarNameTagsOff
@ -110,6 +111,7 @@ Flickable {
ColumnLayout {
id: performanceRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
SimplifiedControls.RadioButton {
id: performanceLow
@ -157,6 +159,7 @@ Flickable {
ColumnLayout {
id: cameraRadioButtonGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
SimplifiedControls.RadioButton {
id: firstPerson

View file

@ -19,8 +19,8 @@ Flickable {
id: root
contentWidth: parent.width
contentHeight: vrColumnLayout.height
topMargin: 16
bottomMargin: 16
topMargin: 24
bottomMargin: 24
clip: true
function changePeakValuesEnabled(enabled) {
@ -70,6 +70,7 @@ Flickable {
id: controlsRadioButtonGroup
width: parent.width
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
ButtonGroup { id: controlsButtonGroup }
@ -202,7 +203,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.input
delegate: Item {
@ -301,7 +302,7 @@ Flickable {
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false
height: contentItem.height
spacing: 4
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true
model: AudioScriptingInterface.devices.output
delegate: Item {

View file

@ -147,7 +147,7 @@ QtObject {
}
readonly property color darkSeparator: "#595959"
readonly property color darkBackground: "#1A1A1A"
readonly property color darkBackground: "#000000"
readonly property color darkBackgroundHighlight: "#575757"
readonly property color highlightOnDark: Qt.rgba(1, 1, 1, 0.2)
readonly property color white: "#FFFFFF"
@ -182,9 +182,10 @@ QtObject {
}
readonly property QtObject settings: QtObject {
property real subtitleTopMargin: 2
property real settingsGroupTopMargin: 10
property real spacingBetweenSettings: 48
property int subtitleTopMargin: 2
property int settingsGroupTopMargin: 24
property int spacingBetweenSettings: 48
property int spacingBetweenRadiobuttons: 14
}
}

View file

@ -87,7 +87,6 @@ RadioButton {
contentItem: Text {
id: radioButtonLabel
height: root.radioButtonRadius
font.pixelSize: 14
font.family: "Graphik"
font.weight: Font.Normal
@ -99,5 +98,6 @@ RadioButton {
enabled: root.enabled
verticalAlignment: Text.AlignVCenter
leftPadding: radioButtonIndicator.width + root.labelLeftMargin
topPadding: -3 // For perfect alignment when using Graphik
}
}

View file

@ -242,6 +242,69 @@ Rectangle {
}
Item {
id: statusButtonContainer
anchors.verticalCenter: parent.verticalCenter
anchors.left: outputDeviceButtonContainer.right
anchors.leftMargin: 8
width: 36
height: width
Rectangle {
id: statusButton
property string currentStatus
anchors.centerIn: parent
anchors.horizontalCenterOffset: 1
anchors.verticalCenterOffset: 2
width: 13
height: width
radius: width/2
visible: false
}
ColorOverlay {
anchors.fill: statusButton
opacity: statusButton.currentStatus ? 1 : 0
source: statusButton
color: if (statusButton.currentStatus === "busy") {
"#ff001a"
} else if (statusButton.currentStatus === "available") {
"#009036"
} else if (statusButton.currentStatus) {
"#ffed00"
}
}
Image {
id: focusIcon
source: "./images/focus.svg"
opacity: statusButtonMouseArea.containsMouse ? 1.0 : (statusButton.currentStatus === "busy" ? 0.7 : 0.3)
anchors.centerIn: parent
width: 36
height: 20
fillMode: Image.PreserveAspectFit
}
MouseArea {
id: statusButtonMouseArea
anchors.fill: parent
enabled: statusButton.currentStatus
hoverEnabled: true
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
sendToScript({
"source": "SimplifiedTopBar.qml",
"method": "toggleStatus"
});
}
}
}
Item {
id: hmdButtonContainer
@ -280,11 +343,6 @@ Rectangle {
Tablet.playSound(TabletEnums.ButtonClick);
var displayPluginCount = Window.getDisplayPluginCount();
if (HMD.active) {
// This next line seems backwards and shouldn't be necessary - the NOTIFY handler should
// result in `displayModeImage.source` changing automatically - but that's not working.
// This is working. So, I'm keeping it.
displayModeImage.source = "./images/vrMode.svg";
// Switch to desktop mode - selects first VR display plugin
for (var i = 0; i < displayPluginCount; i++) {
if (!Window.isDisplayPluginHmd(i)) {
@ -293,11 +351,6 @@ Rectangle {
}
}
} else {
// This next line seems backwards and shouldn't be necessary - the NOTIFY handler should
// result in `displayModeImage.source` changing automatically - but that's not working.
// This is working. So, I'm keeping it.
displayModeImage.source = "./images/desktopMode.svg";
// Switch to VR mode - selects first HMD display plugin
for (var i = 0; i < displayPluginCount; i++) {
if (Window.isDisplayPluginHmd(i)) {
@ -401,6 +454,10 @@ Rectangle {
outputDeviceButton.outputMuted = message.data.outputMuted;
break;
case "updateStatusButton":
statusButton.currentStatus = message.data.currentStatus;
break;
default:
console.log('SimplifiedTopBar.qml: Unrecognized message from JS');
break;

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32" height="19.001" fill="none" version="1.1" viewBox="0 0 32 19.001" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<path d="m7 19h-3.542c-0.46115 0.0089-0.91905-0.0192-1.3443-0.2001-0.42525-0.1808-0.80846-0.4496-1.125-0.7892-0.3234-0.3355-0.57737-0.7329-0.74698-1.1691-0.16962-0.4362-0.25146-0.9023-0.24076-1.3709v-5.47c-0.0066251-0.46326 0.078429-0.92318 0.25019-1.3526 0.17176-0.42944 0.42674-0.81971 0.74988-1.1478 0.32313-0.32808 0.70786-0.58732 1.1316-0.76244 0.42368-0.17511 0.87772-0.26255 1.3353-0.25716h1.4718c0.03732-0.87492 0.24704-1.7335 0.61682-2.5251s0.89216-1.5005 1.5364-2.0849c1.2696-1.2214 3.1558-1.8924 4.9071-1.87l8 2.2471e-5c1.7402-0.025877 3.6023 0.64637 4.8546 1.87 1.2793 1.2015 2.0626 2.8482 2.1927 4.61h1.3178c0.946-0.01712 1.8609 0.3419 2.5484 1 0.3479 0.31321 0.625 0.69864 0.8125 1.13 0.1875 0.43142 0.2809 0.89867 0.274 1.37v5.47c0.0037 0.4767-0.091 0.9488-0.2781 1.3863-0.1871 0.4374-0.376 0.8397-0.7219 1.163-0.3346 0.3376-0.5167 0.5951-0.9542 0.7753-0.4376 0.1803-1.2083 0.2298-1.6807 0.2247h-3.3624v-12.049c0.0051-0.65734-0.1179-1.3092-0.362-1.9184-0.2441-0.60915-0.6044-1.1636-1.0603-1.6316-0.4213-0.48067-0.9384-0.86553-1.5173-1.1292-0.5788-0.26371-1.4283-0.27101-2.0631-0.27147h-8c-0.6475-0.00706-1.4451-0.00352-2.0378 0.26031-0.59274 0.26384-1.1231 0.65262-1.5556 1.1404-0.45594 0.46802-0.81626 1.0225-1.0603 1.6316-0.24406 0.60916-0.35129 1.261-0.34623 1.9184zm-3.4717-2h1.4717v-8.4793h-1.4717c-0.92847 0-1.5283 0.49-1.5283 1.48v5.47c0.01975 0.99 0.59981 1.5293 1.5283 1.5293zm26.472-6.9993c8e-3 -0.84434-0.6554-1.4883-1.5049-1.48h-1.4478v8.4793h1.4478c0.8319 0.0109 1.5175-0.722 1.5049-1.5293z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

@ -15,7 +15,7 @@
// These properties have JSDoc documentation in HMDScriptingInterface.h.
class AbstractHMDScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(bool active READ isHMDMode NOTIFY mountedChanged)
Q_PROPERTY(bool active READ isHMDMode NOTIFY displayModeChanged)
Q_PROPERTY(float ipd READ getIPD)
Q_PROPERTY(float eyeHeight READ getEyeHeight)
Q_PROPERTY(float playerHeight READ getPlayerHeight)

View file

@ -0,0 +1,244 @@
//
// simplifiedStatusIndicator.js
//
// Created by Robin Wilson on 2019-05-17
// Copyright 2019 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 simplifiedStatusIndicator(properties) {
var that = this;
var DEBUG = false;
// #region HEARTBEAT
// Send heartbeat with updates to database
// When this stops, the database will set status to offline
var HEARTBEAT_TIMEOUT_MS = 5000,
heartbeat;
function startHeartbeatTimer() {
if (heartbeat) {
Script.clearTimeout(heartbeat);
heartbeat = false;
}
heartbeat = Script.setTimeout(function() {
heartbeat = false;
getStatus(setStatus);
}, HEARTBEAT_TIMEOUT_MS);
}
// #endregion HEARTBEAT
// #region SEND/GET STATUS REQUEST
function setStatusExternally(newStatus) {
if (!newStatus) {
return;
}
setStatus(newStatus);
}
var request = Script.require('request').request,
REQUEST_URL = "https://highfidelity.co/api/statusIndicator/";
function setStatus(newStatus) {
if (heartbeat) {
Script.clearTimeout(heartbeat);
heartbeat = false;
}
if (newStatus && currentStatus !== newStatus) {
currentStatus = newStatus;
that.statusChanged();
}
var displayNameToSend = MyAvatar.sessionDisplayName;
queryParamString += currentStatus;
if (displayNameToSend === "") {
displayNameToSend = MyAvatar.displayName;
}
var queryParamString = "type=heartbeat";
queryParamString += "&username=" + AccountServices.username;
queryParamString += "&displayName=" + displayNameToSend;
queryParamString += "&status=";
queryParamString += currentStatus;
var uri = REQUEST_URL + "?" + queryParamString;
if (DEBUG) {
console.log("setStatus: " + uri);
}
request({
uri: uri
}, function (error, response) {
startHeartbeatTimer();
if (error || !response || response.status !== "success") {
console.error("Error with setStatus: " + JSON.stringify(response));
return;
}
});
}
// Get status from database
function getStatus(callback) {
var queryParamString = "type=getStatus";
queryParamString += "&username=" + AccountServices.username;
var uri = REQUEST_URL + "?" + queryParamString;
if (DEBUG) {
console.log("getStatus: " + uri);
}
request({
uri: uri
}, function (error, response) {
if (error || !response || response.status !== "success") {
console.error("Error with getStatus: " + JSON.stringify(response));
} else if (response.data.userStatus.toLowerCase() !== "offline") {
if (response.data.userStatus !== currentStatus) {
currentStatus = response.data.userStatus;
that.statusChanged();
}
}
if (callback) {
callback();
}
});
}
function getLocalStatus() {
return currentStatus;
}
// #endregion SEND/GET STATUS REQUEST
// #region SIGNALS
var currentStatus = "available"; // Default is available
function toggleStatus() {
if (currentStatus === "busy") {
currentStatus = "available";
// Else if current status is "available" OR anything else (custom)
} else {
currentStatus = "busy";
}
that.statusChanged();
setStatus();
}
// When avatar becomes active from being away
// Set status back to previousStatus
function onWentActive() {
if (currentStatus !== previousStatus) {
currentStatus = previousStatus;
that.statusChanged();
}
setStatus();
}
// When avatar goes away, set status to busy
var previousStatus;
function onWentAway() {
previousStatus = currentStatus;
if (currentStatus !== "busy") {
currentStatus = "busy";
that.statusChanged();
}
setStatus();
}
// Domain changed update avatar location
function onDomainChanged() {
var queryParamString = "type=updateEmployee";
queryParamString += "&username=" + AccountServices.username;
queryParamString += "&location=unknown";
var uri = REQUEST_URL + "?" + queryParamString;
if (DEBUG) {
console.log("simplifiedStatusIndicator onDomainChanged: " + uri);
}
request({
uri: uri
}, function (error, response) {
if (error || !response || response.status !== "success") {
console.error("Error with onDomainChanged: " + JSON.stringify(response));
} else {
// successfully sent updateLocation
if (DEBUG) {
console.log("Successfully updated location after domain change.");
}
}
});
}
function statusChanged() {
}
// #endregion SIGNALS
// #region APP LIFETIME
// Creates the app button and sets up signals and hearbeat
function startup() {
MyAvatar.wentAway.connect(onWentAway);
MyAvatar.wentActive.connect(onWentActive);
MyAvatar.displayNameChanged.connect(setStatus);
Window.domainChanged.connect(onDomainChanged);
getStatus(setStatus);
}
// Cleans up timeouts, signals, and overlays
function unload() {
MyAvatar.wentAway.disconnect(onWentAway);
MyAvatar.wentActive.disconnect(onWentActive);
MyAvatar.displayNameChanged.disconnect(setStatus);
Window.domainChanged.disconnect(onDomainChanged);
if (heartbeat) {
Script.clearTimeout(heartbeat);
heartbeat = false;
}
}
// #endregion APP LIFETIME
that.startup = startup;
that.unload = unload;
that.toggleStatus = toggleStatus;
that.setStatus = setStatus;
that.getLocalStatus = getLocalStatus;
that.statusChanged = statusChanged;
// Overwrite with the given properties
var overwriteableKeys = ["statusChanged"];
Object.keys(properties).forEach(function (key) {
if (overwriteableKeys.indexOf(key) > -1) {
that[key] = properties[key];
}
});
}
module.exports = simplifiedStatusIndicator;

View file

@ -276,6 +276,24 @@ function setOutputMuted(outputMuted) {
}
var WAIT_FOR_TOP_BAR_MS = 1000;
function sendLocalStatusToQml() {
var currentStatus = si.getLocalStatus();
if (topBarWindow && currentStatus) {
topBarWindow.sendToQml({
"source": "simplifiedUI.js",
"method": "updateStatusButton",
"data": {
"currentStatus": currentStatus
}
});
} else {
Script.setTimeout(sendLocalStatusToQml, WAIT_FOR_TOP_BAR_MS);
}
}
var TOP_BAR_MESSAGE_SOURCE = "SimplifiedTopBar.qml";
function onMessageFromTopBar(message) {
if (message.source !== TOP_BAR_MESSAGE_SOURCE) {
@ -295,6 +313,10 @@ function onMessageFromTopBar(message) {
setOutputMuted(message.data.outputMuted);
break;
case "toggleStatus":
si.toggleStatus();
break;
default:
console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message));
break;
@ -346,13 +368,19 @@ function loadSimplifiedTopBar() {
topBarWindow.fromQml.connect(onMessageFromTopBar);
topBarWindow.closed.connect(onTopBarClosed);
topBarWindow.sendToQml({
"source": "simplifiedUI.js",
"method": "updateOutputMuted",
"data": {
"outputMuted": isOutputMuted()
}
})
// The eventbridge takes a nonzero time to initialize, so we have to wait a bit
// for the QML to load and for that to happen before updating the UI.
Script.setTimeout(function() {
topBarWindow.sendToQml({
"source": "simplifiedUI.js",
"method": "updateOutputMuted",
"data": {
"outputMuted": isOutputMuted()
}
});
sendLocalStatusToQml();
}, WAIT_FOR_TOP_BAR_MS);
}
@ -435,7 +463,15 @@ function ensureFirstPersonCameraInHMD(isHMDMode) {
}
}
function onStatusChanged() {
sendLocalStatusToQml();
}
var simplifiedNametag = Script.require("./simplifiedNametag/simplifiedNametag.js");
var SimplifiedStatusIndicator = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js");
var si;
var oldShowAudioTools;
var oldShowBubbleTools;
var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false);
@ -456,6 +492,10 @@ function startup() {
loadSimplifiedTopBar();
simplifiedNametag.create();
si = new SimplifiedStatusIndicator({
statusChanged: onStatusChanged
});
si.startup();
updateInputDeviceMutedOverlay(Audio.muted);
updateOutputDeviceMutedOverlay(isOutputMuted());
Audio.mutedDesktopChanged.connect(onDesktopInputDeviceMutedChanged);
@ -506,6 +546,7 @@ function shutdown() {
maybeDeleteOutputDeviceMutedOverlay();
simplifiedNametag.destroy();
si.unload();
Audio.mutedDesktopChanged.disconnect(onDesktopInputDeviceMutedChanged);
Window.geometryChanged.disconnect(onGeometryChanged);