diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index c9ded2d6fb..1f3f770867 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) { AnimPose& absPose = absPoses[i]; if (data.rotation != absPose.rot()) { data.rotation = absPose.rot(); - data.rotationSet = true; + data.rotationIsDefaultPose = false; } AnimPose& relPose = poses[i]; if (data.translation != relPose.trans()) { data.translation = relPose.trans(); - data.translationSet = true; + data.translationIsDefaultPose = false; } } diff --git a/interface/resources/icons/tablet-icons/EmoteAppIcon.svg b/interface/resources/icons/tablet-icons/EmoteAppIcon.svg new file mode 100644 index 0000000000..340f0fcd2f --- /dev/null +++ b/interface/resources/icons/tablet-icons/EmoteAppIcon.svg @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/interface/resources/qml/CurrentAPI.qml b/interface/resources/qml/CurrentAPI.qml index 45cf09925a..37984965d9 100644 --- a/interface/resources/qml/CurrentAPI.qml +++ b/interface/resources/qml/CurrentAPI.qml @@ -16,10 +16,9 @@ import "controls-uit" as HifiControls Item { id: root width: parent.width - height: parent.height + height: parent.height property var hideQtMethods: true - property var maxUpdateValues: 20 property var maxReloadValues: 200 property var apiMembers: [] @@ -30,7 +29,7 @@ Item { property Component keyboard Rectangle { - color: "white" + color: hifi.colors.baseGray width: parent.width height: parent.height } @@ -51,32 +50,22 @@ Item { Row { id: topBar anchors.left: parent.left - anchors.leftMargin: 8 + anchors.leftMargin: 30 + anchors.top: parent.top + anchors.topMargin: 30 width: parent.width - height: 50 - HifiControls.GlyphButton { - id: search - enabled: true - glyph: hifi.glyphs.search - color: hifi.colors.text - size: 48 - width: 50 - height: 50 - onClicked: { - addListElements(searchBar.text); - focus = true; - } - } + height: 40 HifiControls.GlyphButton { id: back; enabled: true; + color: hifi.buttons.black glyph: hifi.glyphs.backward - color: hifi.colors.text - size: 48 - width: 30 - height: 50 - anchors.margins: 2 + size: 40 + width: 40 + height: 40 + anchors.left: search.right + anchors.leftMargin: 12 onClicked: { var text = searchBar.text; var chain = text.split("."); @@ -99,17 +88,20 @@ Item { } } - TextField { - id: searchBar + HifiControls.TextField { + id: searchBar focus: true - font.pixelSize: 16 - width: 2*(parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3 - height: parent.height - font.family: ralewayRegular.name + isSearchField: true + width: parent.width - 112 + height: 40 + colorScheme: hifi.colorSchemes.dark + anchors.left: back.right + anchors.leftMargin: 10 + font.family: firaSansSemiBold.name placeholderText: "Search" onAccepted: { console.log("Enter Pressed"); - search.clicked(); + addListElements(searchBar.text); } onActiveFocusChanged: { if (activeFocus && HMD.mounted) { @@ -119,15 +111,27 @@ Item { } } - } + } + } - HifiControls.Button { + Row { + id: topBar2 + anchors.left: parent.left + anchors.leftMargin: 30 + anchors.top: topBar.bottom + anchors.topMargin: 30 + width: parent.width -60 + height: 40 + + HifiControls.GlyphButton { id: addMember; enabled: true; - text: "+" - width: 50 - height: 50 - anchors.margins: 2 + color: hifi.buttons.black + glyph: hifi.glyphs.maximize + width: 40 + height: 40 + anchors.top: parent.top + anchors.left: parent.left onClicked: { addNewMember(); updateList.start(); @@ -138,36 +142,48 @@ Item { HifiControls.Button { id: evaluate; enabled: true; + color: hifi.buttons.black text: "Eval" - width: 50 - height: 50 - anchors.margins: 2 + width: 40 + height: 40 + anchors.left: addMember.right + anchors.leftMargin: 12 onClicked: { evaluateMember(); focus = true; } } - TextField { - id: valueBar - focus: true + + HifiControls.TextField { + id: valueBar + isSearchField: false font.pixelSize: 16 - width: (parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3 - height: parent.height - font.family: ralewayRegular.name + width: parent.width - 208 + height: 40 + colorScheme: hifi.colorSchemes.dark + font.family: firaSansSemiBold.name placeholderText: "Value" - textColor: "#4466DD" - anchors.margins: 2 + anchors.left: evaluate.right + anchors.leftMargin: 12 + onActiveFocusChanged: { + if (activeFocus && HMD.mounted) { + keyboard.raised = true; + } else { + keyboard.raised = false; + } + } } HifiControls.GlyphButton { id: reload; enabled: false; + color: hifi.buttons.black glyph: hifi.glyphs.reload - color: hifi.colors.text - size: 48 - width: 50 - height: 50 - anchors.margins: 2 + size: 40 + width: 40 + height: 40 + anchors.right: update.left + anchors.rightMargin: 12 onClicked: { reloadListValues(); focus = true; @@ -177,11 +193,12 @@ Item { HifiControls.GlyphButton { id: update; enabled: false; + color: hifi.buttons.black glyph: hifi.glyphs.playback_play - size: 48 - width: 50 - height: 50 - anchors.margins: 2 + size: 40 + width: 40 + height: 40 + anchors.right: parent.right onClicked: { if (isReloading) { update.glyph = hifi.glyphs.playback_play @@ -196,71 +213,104 @@ Item { } } } - - ListModel { - id: memberModel - } - Component { - id: memberDelegate - - Row { - id: memberRow - property var isMainKey: apiType === "class"; - spacing: 10 - Rectangle { - width: isMainKey ? 20 : 40; - height: parent.height - } - - RalewayRegular { - text: apiMember - size: !isMainKey ? 16 : 22 - MouseArea { - width: list.width - height: parent.height - onClicked: { - searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember; - valueBar.text = !apiValue ? "" : apiValue; - list.currentIndex = index; - evaluatingIdx = index; - } - onDoubleClicked: { - if (apiType === "class") { - addListElements(apiMember+"."); - } else { - isolateElement(evaluatingIdx); - } - - } - } - } - - - RalewayRegular { - text: apiType - size: 14 - color: hifi.colors.baseGrayHighlight - } - - RalewayRegular { - text: !apiValue ? "" : apiValue; - size: 16 - color: "#4466DD" - } - } - } - - Rectangle { id: membersBackground anchors { - left: parent.left; right: parent.right; top: topBar.bottom; bottom: parent.bottom; - margins: hifi.dimensions.contentMargin.x - bottomMargin: hifi.dimensions.contentSpacing.y + 40 + left: parent.left; right: parent.right; top: topBar2.bottom; bottom: bottomBar.top; + margins: 30 + } + color: hifi.colors.tableBackgroundDark + border.color: hifi.colors.lightGray + border.width: 2 + radius: 5 + + ListModel { + id: memberModel + } + + Component { + id: memberDelegate + Item { + id: item + width: parent.width + anchors.left: parent.left + height: 26 + clip: true + + Rectangle { + width: parent.width + height: parent.height + color: index % 2 == 0 ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd + anchors.verticalCenter: parent.verticalCenter + + Row { + id: memberRow + anchors.bottom: parent.bottom + anchors.verticalCenter: parent.verticalCenter + spacing: 10 + + FiraSansSemiBold { + property var isMainKey: apiType === "class"; + text: apiMember + size: isMainKey ? 17 : 15 + font.bold: true + anchors.verticalCenter: parent.verticalCenter + color: isMainKey ? hifi.colors.faintGray : hifi.colors.lightGrayText + MouseArea { + width: list.width + height: parent.height + onClicked: { + searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember; + valueBar.text = !apiValue ? "" : apiValue; + list.currentIndex = index; + evaluatingIdx = index; + } + onDoubleClicked: { + if (apiType === "class") { + addListElements(apiMember+"."); + } else { + isolateElement(evaluatingIdx); + } + } + } + } + + FiraSansRegular { + text: apiType + anchors.left: apiMember.right + anchors.verticalCenter: parent.verticalCenter + size: 13 + color: hifi.colors.lightGrayText + } + + FiraSansRegular { + text: !apiValue ? "" : apiValue; + anchors.left: apiType.right + anchors.verticalCenter: parent.verticalCenter + size: 14 + color: hifi.colors.primaryHighlight + } + } + } + } + } + + Component { + id: highlight + Rectangle { + anchors { + left: list.left + right: scrollBar.left + leftMargin: 2 + rightMargin: 2 + } + color: hifi.colors.primaryHighlight + radius: 4 + z: 10 + opacity: 0.5 + } } - color: "white" - radius: 4 ListView { id: list @@ -269,23 +319,16 @@ Item { left: parent.left right: scrollBar.left bottom: parent.bottom - margins: 4 + topMargin: 2 + leftMargin: 2 + bottomMargin: 2 } clip: true cacheBuffer: 4000 model: memberModel delegate: memberDelegate highlightMoveDuration: 0 - - highlight: Rectangle { - anchors { - left: parent ? parent.left : undefined - right: parent ? parent.right : undefined - leftMargin: hifi.dimensions.borderWidth - rightMargin: hifi.dimensions.borderWidth - } - color: "#BBDDFF" - } + highlight: highlight onMovementStarted: { scrollSlider.manual = true; } @@ -310,12 +353,11 @@ Item { top: parent.top right: parent.right bottom: parent.bottom - topMargin: 4 - bottomMargin: 4 + margins: 2 } - width: scrolling ? 18 : 0 - radius: 4 - color: hifi.colors.baseGrayShadow + width: 22 + height: parent.height - 4 + color: hifi.colors.tableScrollBackgroundDark MouseArea { anchors.fill: parent @@ -344,14 +386,12 @@ Item { y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1); } - anchors { - right: parent.right - rightMargin: 3 - } - width: 12 - height: (list.height / list.contentHeight) * list.height - radius: width / 4 - color: "white" + anchors.right: parent.right + anchors.margins: 2 + width: 18 + height: ((list.height / list.contentHeight) * list.height) < 15 ? 15 : (list.height / list.contentHeight) * list.height + radius: 5 + color: hifi.colors.tableScrollHandleDark visible: scrollBar.scrolling; @@ -373,66 +413,75 @@ Item { } } } - - HifiControls.GlyphButton { - id: clipboard; - enabled: true; - glyph: hifi.glyphs.scriptNew - size: 38 - width: 50 - height: 50 + + Row { + id: bottomBar anchors.left: parent.left + anchors.leftMargin: 30 anchors.bottom: parent.bottom - anchors.margins: 2 - anchors.leftMargin: 8 - onClicked: { - var buffer = ""; - for (var i = 0; i < memberModel.count; i++) { - var datarow = memberModel.get(i); - buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue; - } - Window.copyToClipboard(buffer); - focus = true; - } - } - - HifiControls.Button { - id: debug; - enabled: true; - text: "Debug Script" - width: 120 - height: 50 - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 2 - anchors.rightMargin: 8 - onClicked: { - sendToScript({type: "selectScript"}); - } - } + anchors.bottomMargin: 30 + width: parent.width + height: 40 - HifiControls.CheckBox { - id: hideQt - boxSize: 25 - boxRadius: 3 - checked: true - anchors.left: clipboard.right - anchors.leftMargin: 8 - anchors.verticalCenter: clipboard.verticalCenter - anchors.margins: 2 - onClicked: { - hideQtMethods = checked; - addListElements(); + HifiControls.GlyphButton { + id: clipboard; + enabled: true; + color: hifi.buttons.black + glyph: hifi.glyphs.scriptNew + size: 25 + width: 40 + height: 40 + anchors.left: parent.left + onClicked: { + var buffer = ""; + for (var i = 0; i < memberModel.count; i++) { + var datarow = memberModel.get(i); + buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue; + } + Window.copyToClipboard(buffer); + focus = true; + } } - } - HifiControls.Label { - id: hideLabel - anchors.left: hideQt.right - anchors.verticalCenter: clipboard.verticalCenter - anchors.margins: 2 - font.pixelSize: 15 - text: "Hide Qt Methods" + HifiControls.CheckBox { + id: hideQt + colorScheme: hifi.checkbox.dark + boxSize: 25 + boxRadius: 3 + checked: true + anchors.left: clipboard.right + anchors.leftMargin: 10 + anchors.verticalCenter: clipboard.verticalCenter + onClicked: { + hideQtMethods = checked; + addListElements(); + } + } + + HifiControls.Label { + id: hideLabel + anchors.left: hideQt.right + anchors.verticalCenter: clipboard.verticalCenter + anchors.margins: 2 + font.pixelSize: 15 + text: "Hide Qt Methods" + } + + HifiControls.Button { + id: debug; + enabled: true; + color: hifi.buttons.black + text: "Debug Script" + width: 120 + height: 40 + anchors.right: parent.right + anchors.rightMargin: 60 + anchors.bottom: parent.bottom + + onClicked: { + sendToScript({type: "selectScript"}); + } + } } HifiControls.Keyboard { @@ -639,4 +688,4 @@ Item { } signal sendToScript(var message); -} \ No newline at end of file +} diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index b942c8b4ab..a21d1f8efd 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -24,10 +24,13 @@ TextField { property bool isSearchField: false property string label: "" property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0) + property bool hasDefocusedBorder: true; property bool hasRoundedBorder: false + property int roundedBorderRadius: 4 property bool error: false; property bool hasClearButton: false; - property string leftPlaceholderGlyph: ""; + property string leftPermanentGlyph: ""; + property string centerPlaceholderGlyph: ""; placeholderText: textField.placeholderText @@ -101,12 +104,12 @@ TextField { } } border.color: textField.error ? hifi.colors.redHighlight : - (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) + (textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color)) border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0 - radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0) + radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0) HiFiGlyphs { - text: textField.leftPlaceholderGlyph; + text: textField.leftPermanentGlyph; color: textColor; size: hifi.fontSizes.textFieldSearchIcon; anchors.left: parent.left; @@ -115,6 +118,15 @@ TextField { visible: text; } + HiFiGlyphs { + text: textField.centerPlaceholderGlyph; + color: textColor; + size: parent.height; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.verticalCenter: parent.verticalCenter; + visible: text && !textField.focus && textField.text === ""; + } + HiFiGlyphs { text: hifi.glyphs.search color: textColor @@ -145,7 +157,7 @@ TextField { placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight - padding.left: ((isSearchField || textField.leftPlaceholderGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding + padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index da67569bc3..ae42b8e3e1 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -389,7 +389,7 @@ Rectangle { // Item { id: tabButtonsContainer; - visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange"; + visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendMoneyStep"; property int numTabs: 5; // Size width: root.width; diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml index c2d9ef3b19..33cd43bb05 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml @@ -29,13 +29,13 @@ Item { property string userName; property string profilePicUrl; - height: 65; + height: 75; width: parent.width; Rectangle { id: mainContainer; // Style - color: root.isSelected ? hifi.colors.faintGray : hifi.colors.white; + color: root.isSelected ? hifi.colors.faintGray80 : hifi.colors.white; // Size anchors.left: parent.left; anchors.right: parent.right; @@ -49,7 +49,7 @@ Item { anchors.verticalCenter: parent.verticalCenter; anchors.left: parent.left; anchors.leftMargin: 36; - height: root.height - 15; + height: 50; width: visible ? height : 0; clip: true; Image { @@ -83,15 +83,15 @@ Item { RalewaySemiBold { id: userName; anchors.left: avatarImage.right; - anchors.leftMargin: 16; + anchors.leftMargin: 12; anchors.top: parent.top; anchors.bottom: parent.bottom; anchors.right: chooseButton.visible ? chooseButton.left : parent.right; anchors.rightMargin: chooseButton.visible ? 10 : 0; // Text size - size: 20; + size: 18; // Style - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; text: root.userName; elide: Text.ElideRight; // Alignment @@ -107,9 +107,9 @@ Item { colorScheme: hifi.colorSchemes.dark; anchors.verticalCenter: parent.verticalCenter; anchors.right: parent.right; - anchors.rightMargin: 24; - height: root.height - 20; - width: 110; + anchors.rightMargin: 28; + height: 35; + width: 100; text: "CHOOSE"; onClicked: { var msg = { method: 'chooseConnection', userName: root.userName, profilePicUrl: root.profilePicUrl }; diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml index 267cf0ed51..43636d47ca 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml @@ -29,6 +29,7 @@ Item { property string displayName; property string userName; property string profilePic; + property string textColor: hifi.colors.white; Item { visible: root.isDisplayingNearby; @@ -46,7 +47,7 @@ Item { // Text size size: 18; // Style - color: hifi.colors.baseGray; + color: root.textColor; verticalAlignment: Text.AlignBottom; elide: Text.ElideRight; } @@ -63,7 +64,7 @@ Item { // Text size size: 16; // Style - color: hifi.colors.baseGray; + color: root.textColor; verticalAlignment: Text.AlignTop; elide: Text.ElideRight; } @@ -108,7 +109,7 @@ Item { // Text size size: 16; // Style - color: hifi.colors.baseGray; + color: root.textColor; verticalAlignment: Text.AlignVCenter; elide: Text.ElideRight; } diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml index 9164ba7a8c..f66781c919 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml @@ -29,8 +29,9 @@ Item { property int parentAppNavBarHeight; property string currentActiveView: "sendMoneyHome"; property string nextActiveView: ""; - property bool isCurrentlyFullScreen: chooseRecipientConnection.visible || - chooseRecipientNearby.visible || sendMoneyStep.visible || paymentSuccess.visible || paymentFailure.visible; + property bool shouldShowTopAndBottomOfWallet: chooseRecipientConnection.visible || + chooseRecipientNearby.visible || paymentSuccess.visible || paymentFailure.visible; + property bool shouldShowTopOfWallet: sendMoneyStep.visible; property bool isCurrentlySendingMoney: false; // This object is always used in a popup or full-screen Wallet section. @@ -38,9 +39,9 @@ Item { // able to click on a button/mouseArea underneath the popup/section. MouseArea { x: 0; - y: root.isCurrentlyFullScreen ? 0 : root.parentAppTitleBarHeight; + y: (root.shouldShowTopAndBottomOfWallet && !root.shouldShowTopOfWallet) ? 0 : root.parentAppTitleBarHeight; width: parent.width; - height: root.isCurrentlyFullScreen ? parent.height : parent.height - root.parentAppTitleBarHeight - root.parentAppNavBarHeight; + height: (root.shouldShowTopAndBottomOfWallet || root.shouldShowTopOfWallet) ? parent.height : parent.height - root.parentAppTitleBarHeight - root.parentAppNavBarHeight; propagateComposedEvents: false; } @@ -105,7 +106,8 @@ Item { // Send Money Home BEGIN Item { id: sendMoneyHome; - visible: root.currentActiveView === "sendMoneyHome"; + z: 996; + visible: root.currentActiveView === "sendMoneyHome" || root.currentActiveView === "chooseRecipientConnection" || root.currentActiveView === "chooseRecipientNearby"; anchors.fill: parent; anchors.topMargin: root.parentAppTitleBarHeight; anchors.bottomMargin: root.parentAppNavBarHeight; @@ -194,6 +196,17 @@ Item { anchors.right: parent.right; anchors.bottom: parent.bottom; height: 440; + + + LinearGradient { + anchors.fill: parent; + start: Qt.point(0, 0); + end: Qt.point(0, height); + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.white } + GradientStop { position: 1.0; color: hifi.colors.faintGray } + } + } RalewaySemiBold { id: sendMoneyText; @@ -223,12 +236,13 @@ Item { Image { anchors.top: parent.top; - source: "../images/wallet-bg.jpg"; - height: 60; + source: "./images/connection.svg"; + height: 70; width: parent.width; fillMode: Image.PreserveAspectFit; horizontalAlignment: Image.AlignHCenter; verticalAlignment: Image.AlignTop; + mipmap: true; } RalewaySemiBold { @@ -248,6 +262,7 @@ Item { anchors.fill: parent; onClicked: { root.nextActiveView = "chooseRecipientConnection"; + filterBar.text = ""; } } } @@ -264,12 +279,13 @@ Item { Image { anchors.top: parent.top; - source: "../images/wallet-bg.jpg"; - height: 60; + source: "./images/nearby.svg"; + height: 70; width: parent.width; fillMode: Image.PreserveAspectFit; horizontalAlignment: Image.AlignHCenter; verticalAlignment: Image.AlignTop; + mipmap: true; } RalewaySemiBold { @@ -299,9 +315,18 @@ Item { // Choose Recipient Connection BEGIN Rectangle { id: chooseRecipientConnection; + z: 997; visible: root.currentActiveView === "chooseRecipientConnection"; anchors.fill: parent; - color: "#AAAAAA"; + color: "#80000000"; + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } ListModel { id: connectionsModel; @@ -314,6 +339,8 @@ Item { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; + radius: 8; RalewaySemiBold { id: chooseRecipientText_connections; @@ -322,11 +349,11 @@ Item { anchors.top: parent.top; anchors.topMargin: 26; anchors.left: parent.left; - anchors.leftMargin: 20; + anchors.leftMargin: 36; width: paintedWidth; height: 30; // Text size - size: 22; + size: 18; // Style color: hifi.colors.baseGray; } @@ -334,6 +361,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_connections; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -363,7 +391,7 @@ Item { height: 40; // Anchors anchors.left: parent.left; - anchors.leftMargin: 16; + anchors.leftMargin: 36; anchors.right: parent.right; anchors.rightMargin: 16; anchors.top: chooseRecipientText_connections.bottom; @@ -374,8 +402,10 @@ Item { colorScheme: hifi.colorSchemes.faintGray; hasClearButton: true; hasRoundedBorder: true; + hasDefocusedBorder: false; + roundedBorderRadius: filterBar.height/2; anchors.fill: parent; - placeholderText: "filter recipients"; + centerPlaceholderGlyph: hifi.glyphs.search; onTextChanged: { buildFilteredConnectionsModel(); @@ -461,17 +491,28 @@ Item { // Choose Recipient Nearby BEGIN Rectangle { id: chooseRecipientNearby; + z: 997; + color: "#80000000"; property string selectedRecipient; visible: root.currentActiveView === "chooseRecipientNearby"; anchors.fill: parent; - color: "#AAAAAA"; + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } Rectangle { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; + radius: 8; RalewaySemiBold { text: "Choose Recipient:"; @@ -483,7 +524,7 @@ Item { width: paintedWidth; height: 30; // Text size - size: 22; + size: 18; // Style color: hifi.colors.baseGray; } @@ -491,6 +532,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_nearby; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -512,163 +554,142 @@ Item { } } - Item { - id: selectionInstructionsContainer; - visible: chooseRecipientNearby.selectedRecipient === ""; - anchors.fill: parent; - - RalewaySemiBold { - id: selectionInstructions_deselected; - text: "Click/trigger on an avatar nearby to select them..."; - // Anchors - anchors.bottom: parent.bottom; - anchors.bottomMargin: 200; - anchors.left: parent.left; - anchors.leftMargin: 58; - anchors.right: parent.right; - anchors.rightMargin: anchors.leftMargin; - height: paintedHeight; - // Text size - size: 20; - // Style - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignHCenter; - wrapMode: Text.Wrap; - } + RalewaySemiBold { + id: selectionInstructions; + text: chooseRecipientNearby.selectedRecipient === "" ? "Trigger or click on\nsomeone nearby to select them" : + "Trigger or click on\nsomeone else to select again"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 100; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: anchors.leftMargin; + height: paintedHeight; + // Text size + size: 20; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + wrapMode: Text.Wrap; } - Item { + Image { + anchors.top: selectionInstructions.bottom; + anchors.topMargin: 20; + anchors.bottom: selectionMadeContainer.top; + anchors.bottomMargin: 30; + source: "./images/p2p-nearby-unselected.svg"; + width: parent.width; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignVCenter; + mipmap: true; + } + + Rectangle { id: selectionMadeContainer; - visible: !selectionInstructionsContainer.visible; - anchors.fill: parent; + visible: chooseRecipientNearby.selectedRecipient !== ""; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 190; + color: "#F3F3F3"; + radius: 8; + + // Used to square off the top left and top right edges of this container + Rectangle { + color: "#F3F3F3"; + height: selectionMadeContainer.radius; + anchors.top: selectionMadeContainer.top; + anchors.left: selectionMadeContainer.left; + anchors.right: selectionMadeContainer.right; + } RalewaySemiBold { id: sendToText; - text: "Send To:"; + text: "Send to:"; // Anchors anchors.top: parent.top; - anchors.topMargin: 120; + anchors.topMargin: 36; anchors.left: parent.left; - anchors.leftMargin: 12; + anchors.leftMargin: 36; width: paintedWidth; height: paintedHeight; // Text size - size: 20; + size: 18; // Style color: hifi.colors.baseGray; } + Image { + id: selectedImage; + anchors.top: parent.top; + anchors.topMargin: 24; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 32; + anchors.left: sendToText.right; + anchors.leftMargin: 4; + source: "./images/p2p-nearby-selected.svg"; + width: 50; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignVCenter; + mipmap: true; + } + RalewaySemiBold { id: avatarDisplayName; text: '"' + AvatarList.getAvatar(chooseRecipientNearby.selectedRecipient).sessionDisplayName + '"'; // Anchors - anchors.top: sendToText.bottom; - anchors.topMargin: 60; - anchors.left: parent.left; - anchors.leftMargin: 30; + anchors.top: parent.top; + anchors.topMargin: 34; + anchors.left: selectedImage.right; + anchors.leftMargin: 10; anchors.right: parent.right; - anchors.rightMargin: 30; + anchors.rightMargin: 10; height: paintedHeight; // Text size - size: 22; + size: 20; // Style - horizontalAlignment: Text.AlignHCenter; - color: hifi.colors.baseGray; - } - - RalewaySemiBold { - id: avatarNodeID; - text: chooseRecipientNearby.selectedRecipient; - // Anchors - anchors.top: avatarDisplayName.bottom; - anchors.topMargin: 6; - anchors.left: parent.left; - anchors.leftMargin: 30; - anchors.right: parent.right; - anchors.rightMargin: 30; - height: paintedHeight; - // Text size - size: 14; - // Style - horizontalAlignment: Text.AlignHCenter; - color: hifi.colors.lightGrayText; + color: hifi.colors.blueAccent; } RalewaySemiBold { id: avatarUserName; text: sendMoneyStep.selectedRecipientUserName; // Anchors - anchors.top: avatarNodeID.bottom; - anchors.topMargin: 12; - anchors.left: parent.left; - anchors.leftMargin: 30; + anchors.top: avatarDisplayName.bottom; + anchors.topMargin: 16; + anchors.left: selectedImage.right; + anchors.leftMargin: 10; anchors.right: parent.right; - anchors.rightMargin: 30; + anchors.rightMargin: 10; height: paintedHeight; // Text size - size: 22; + size: 18; // Style - horizontalAlignment: Text.AlignHCenter; color: hifi.colors.baseGray; } - RalewaySemiBold { - id: selectionInstructions_selected; - text: "Click/trigger on another avatar nearby to select them...\n\nor press 'Next' to continue."; - // Anchors + // "CHOOSE" button + HifiControlsUit.Button { + id: chooseButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.horizontalCenter: parent.horizontalCenter; anchors.bottom: parent.bottom; - anchors.bottomMargin: 200; - anchors.left: parent.left; - anchors.leftMargin: 58; - anchors.right: parent.right; - anchors.rightMargin: anchors.leftMargin; - height: paintedHeight; - // Text size - size: 20; - // Style - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignHCenter; - wrapMode: Text.Wrap; - } - } + anchors.bottomMargin: 20; + height: 40; + width: 110; + text: "CHOOSE"; + onClicked: { + sendMoneyStep.referrer = "nearby"; + sendMoneyStep.selectedRecipientNodeID = chooseRecipientNearby.selectedRecipient; + chooseRecipientNearby.selectedRecipient = ""; - // "Cancel" button - HifiControlsUit.Button { - id: cancelButton; - color: hifi.buttons.noneBorderless; - colorScheme: hifi.colorSchemes.dark; - anchors.left: parent.left; - anchors.leftMargin: 60; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 80; - height: 50; - width: 120; - text: "Cancel"; - onClicked: { - root.nextActiveView = "sendMoneyHome"; - resetSendMoneyData(); - } - } - - // "Next" button - HifiControlsUit.Button { - id: nextButton; - enabled: chooseRecipientNearby.selectedRecipient !== ""; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.rightMargin: 60; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 80; - height: 50; - width: 120; - text: "Next"; - onClicked: { - sendMoneyStep.referrer = "nearby"; - sendMoneyStep.selectedRecipientNodeID = chooseRecipientNearby.selectedRecipient; - chooseRecipientNearby.selectedRecipient = ""; - - root.nextActiveView = "sendMoneyStep"; + root.nextActiveView = "sendMoneyStep"; + } } } } @@ -676,9 +697,9 @@ Item { // Choose Recipient Nearby END // Send Money Screen BEGIN - Rectangle { + Item { id: sendMoneyStep; - z: 997; + z: 996; property string referrer; // either "connections" or "nearby" property string selectedRecipientNodeID; @@ -688,284 +709,333 @@ Item { visible: root.currentActiveView === "sendMoneyStep"; anchors.fill: parent; - color: "#AAAAAA"; + anchors.topMargin: root.parentAppTitleBarHeight; - Rectangle { - anchors.centerIn: parent; - width: parent.width - 30; - height: parent.height - 30; + RalewaySemiBold { + id: sendMoneyText_sendMoneyStep; + text: "Send Money"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: paintedWidth; + height: 30; + // Text size + size: 22; + // Style + color: hifi.colors.white; + } + + Item { + id: sendToContainer; + anchors.top: sendMoneyText_sendMoneyStep.bottom; + anchors.topMargin: 20; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; RalewaySemiBold { - id: sendMoneyText_sendMoneyStep; - text: "Send Money To:"; + id: sendToText_sendMoneyStep; + text: "Send to:"; // Anchors anchors.top: parent.top; - anchors.topMargin: 26; anchors.left: parent.left; - anchors.leftMargin: 20; - width: paintedWidth; - height: 30; + anchors.bottom: parent.bottom; + width: 90; // Text size - size: 22; + size: 18; // Style - color: hifi.colors.baseGray; + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; } - Item { - id: sendToContainer; - anchors.top: sendMoneyText_sendMoneyStep.bottom; - anchors.topMargin: 20; - anchors.left: parent.left; - anchors.leftMargin: 20; + RecipientDisplay { + anchors.top: parent.top; + anchors.left: sendToText_sendMoneyStep.right; + anchors.right: changeButton.left; + anchors.rightMargin: 12; + height: parent.height; + + displayName: sendMoneyStep.selectedRecipientDisplayName; + userName: sendMoneyStep.selectedRecipientUserName; + profilePic: sendMoneyStep.selectedRecipientProfilePic !== "" ? ((0 === sendMoneyStep.selectedRecipientProfilePic.indexOf("http")) ? + sendMoneyStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendMoneyStep.selectedRecipientProfilePic)) : ""; + isDisplayingNearby: sendMoneyStep.referrer === "nearby"; + } + + // "CHANGE" button + HifiControlsUit.Button { + id: changeButton; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; anchors.right: parent.right; - anchors.rightMargin: 20; - height: 80; - - RalewaySemiBold { - id: sendToText_sendMoneyStep; - text: "Send To:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.bottom: parent.bottom; - width: 90; - // Text size - size: 18; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignVCenter; - } - - RecipientDisplay { - anchors.top: parent.top; - anchors.left: sendToText_sendMoneyStep.right; - anchors.right: changeButton.left; - anchors.rightMargin: 12; - height: parent.height; - - displayName: sendMoneyStep.selectedRecipientDisplayName; - userName: sendMoneyStep.selectedRecipientUserName; - profilePic: sendMoneyStep.selectedRecipientProfilePic !== "" ? ((0 === sendMoneyStep.selectedRecipientProfilePic.indexOf("http")) ? - sendMoneyStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendMoneyStep.selectedRecipientProfilePic)) : ""; - isDisplayingNearby: sendMoneyStep.referrer === "nearby"; - } - - // "CHANGE" button - HifiControlsUit.Button { - id: changeButton; - color: hifi.buttons.black; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - height: 35; - width: 120; - text: "CHANGE"; - onClicked: { - if (sendMoneyStep.referrer === "connections") { - root.nextActiveView = "chooseRecipientConnection"; - } else if (sendMoneyStep.referrer === "nearby") { - root.nextActiveView = "chooseRecipientNearby"; - } - resetSendMoneyData(); + anchors.verticalCenter: parent.verticalCenter; + height: 35; + width: 100; + text: "CHANGE"; + onClicked: { + if (sendMoneyStep.referrer === "connections") { + root.nextActiveView = "chooseRecipientConnection"; + } else if (sendMoneyStep.referrer === "nearby") { + root.nextActiveView = "chooseRecipientNearby"; } + resetSendMoneyData(); + } + } + } + + Item { + id: amountContainer; + anchors.top: sendToContainer.bottom; + anchors.topMargin: 2; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: amountText; + text: "Amount:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; + } + + HifiControlsUit.TextField { + id: amountTextField; + colorScheme: hifi.colorSchemes.dark; + inputMethodHints: Qt.ImhDigitsOnly; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: amountText.right; + anchors.right: parent.right; + height: 50; + // Style + leftPermanentGlyph: hifi.glyphs.hfc; + activeFocusOnPress: true; + activeFocusOnTab: true; + + validator: IntValidator { bottom: 0; } + + onAccepted: { + optionalMessage.focus = true; } } - Item { - id: amountContainer; - anchors.top: sendToContainer.bottom; + FiraSansSemiBold { + visible: amountTextFieldError.text === ""; + text: "Balance: "; + // Anchors + anchors.top: amountTextField.bottom; anchors.topMargin: 2; - anchors.left: parent.left; - anchors.leftMargin: 20; - anchors.right: parent.right; - anchors.rightMargin: 20; - height: 80; - - RalewaySemiBold { - id: amountText; - text: "Amount:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.bottom: parent.bottom; - width: 90; - // Text size - size: 18; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignVCenter; - } - - HifiControlsUit.TextField { - id: amountTextField; - colorScheme: hifi.colorSchemes.light; - inputMethodHints: Qt.ImhDigitsOnly; - // Anchors - anchors.verticalCenter: parent.verticalCenter; - anchors.left: amountText.right; - anchors.right: parent.right; - height: 50; - // Style - leftPlaceholderGlyph: hifi.glyphs.hfc; - activeFocusOnPress: true; - activeFocusOnTab: true; - - validator: IntValidator { bottom: 0; } - - onAccepted: { - optionalMessage.focus = true; - } - } - - RalewaySemiBold { - id: amountTextFieldError; - // Anchors - anchors.top: amountTextField.bottom; - anchors.topMargin: 2; - anchors.left: amountTextField.left; - anchors.right: amountTextField.right; - height: 40; - // Text size - size: 16; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignTop; - horizontalAlignment: Text.AlignRight; - } + anchors.left: amountTextField.left; + anchors.right: sendMoneyBalanceText_HFC.left; + width: paintedWidth; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + HiFiGlyphs { + id: sendMoneyBalanceText_HFC; + visible: amountTextFieldError.text === ""; + text: hifi.glyphs.hfc; + // Size + size: 16; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.right: sendMoneyBalanceText.left; + width: paintedWidth; + height: 40; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + FiraSansSemiBold { + id: sendMoneyBalanceText; + visible: amountTextFieldError.text === ""; + text: balanceText.text; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.right: amountTextField.right; + width: paintedWidth; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; } - Item { - id: messageContainer; - anchors.top: amountContainer.bottom; - anchors.topMargin: 16; - anchors.left: parent.left; - anchors.leftMargin: 20; - anchors.right: parent.right; - anchors.rightMargin: 20; - height: 140; + RalewaySemiBold { + id: amountTextFieldError; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.left: amountTextField.left; + anchors.right: amountTextField.right; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + } + + Item { + id: messageContainer; + anchors.top: amountContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 140; - FontLoader { id: firaSansSemiBold; source: "../../../../../fonts/FiraSans-SemiBold.ttf"; } - TextArea { - id: optionalMessage; - property int maximumLength: 70; - property string previousText: text; - placeholderText: "Optional Message"; - font.family: firaSansSemiBold.name; - font.pixelSize: 20; - // Anchors + FontLoader { id: firaSansSemiBold; source: "../../../../../fonts/FiraSans-SemiBold.ttf"; } + TextArea { + id: optionalMessage; + property int maximumLength: 72; + property string previousText: text; + placeholderText: "Optional Message (" + maximumLength + " character limit)"; + font.family: firaSansSemiBold.name; + font.pixelSize: 20; + // Anchors + anchors.fill: parent; + // Style + background: Rectangle { anchors.fill: parent; - // Style - background: Rectangle { - anchors.fill: parent; - color: optionalMessage.activeFocus ? hifi.colors.white : hifi.colors.textFieldLightBackground; - border.width: optionalMessage.activeFocus ? 1 : 0; - border.color: optionalMessage.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground; - } - color: hifi.colors.black; - textFormat: TextEdit.PlainText; - wrapMode: TextEdit.Wrap; - activeFocusOnPress: true; - activeFocusOnTab: true; - // Workaround for no max length on TextAreas - onTextChanged: { - if (text.length > maximumLength) { - var cursor = cursorPosition; - text = previousText; - if (cursor > text.length) { - cursorPosition = text.length; - } else { - cursorPosition = cursor-1; - } - } - previousText = text; - } + color: optionalMessage.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow; + border.width: optionalMessage.activeFocus ? 1 : 0; + border.color: optionalMessage.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground; } - RalewaySemiBold { - id: optionalMessageCharacterCount; - text: optionalMessage.text.length + "/" + optionalMessage.maximumLength; - // Anchors - anchors.top: optionalMessage.bottom; - anchors.topMargin: 2; - anchors.right: optionalMessage.right; - height: paintedHeight; - // Text size - size: 16; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignTop; - horizontalAlignment: Text.AlignRight; + color: hifi.colors.white; + textFormat: TextEdit.PlainText; + wrapMode: TextEdit.Wrap; + activeFocusOnPress: true; + activeFocusOnTab: true; + // Workaround for no max length on TextAreas + onTextChanged: { + if (text.length > maximumLength) { + var cursor = cursorPosition; + text = previousText; + if (cursor > text.length) { + cursorPosition = text.length; + } else { + cursorPosition = cursor-1; + } + } + previousText = text; + } + } + FiraSansSemiBold { + id: optionalMessageCharacterCount; + text: optionalMessage.text.length + "/" + optionalMessage.maximumLength; + // Anchors + anchors.top: optionalMessage.bottom; + anchors.topMargin: 4; + anchors.right: optionalMessage.right; + height: paintedHeight; + // Text size + size: 16; + // Style + color: optionalMessage.text.length === optionalMessage.maximumLength ? "#ea89a5" : hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + } + + HifiControlsUit.CheckBox { + id: sendPubliclyCheckbox; + visible: false; // FIXME ONCE PARTICLE EFFECTS ARE IN + text: "Send Publicly" + // Anchors + anchors.top: messageContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 16; + boxSize: 24; + } + + Item { + id: bottomBarContainer; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 20; + height: 60; + + // "CANCEL" button + HifiControlsUit.Button { + id: cancelButton_sendMoneyStep; + color: hifi.buttons.noneBorderlessWhite; + colorScheme: hifi.colorSchemes.dark; + anchors.left: parent.left; + anchors.leftMargin: 24; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: 150; + text: "CANCEL"; + onClicked: { + resetSendMoneyData(); + root.nextActiveView = "sendMoneyHome"; } } - Item { - id: bottomBarContainer; - anchors.top: messageContainer.bottom; - anchors.topMargin: 30; - anchors.left: parent.left; - anchors.leftMargin: 20; + // "SEND" button + HifiControlsUit.Button { + id: sendButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; anchors.right: parent.right; - anchors.rightMargin: 20; - height: 80; - - HifiControlsUit.CheckBox { - id: sendPubliclyCheckbox; - visible: false; // FIXME ONCE PARTICLE EFFECTS ARE IN - text: "Send Publicly" - // Anchors - anchors.verticalCenter: parent.verticalCenter; - anchors.left: parent.left; - anchors.right: cancelButton_sendMoneyStep.left; - anchors.rightMargin: 16; - boxSize: 24; - } - - // "CANCEL" button - HifiControlsUit.Button { - id: cancelButton_sendMoneyStep; - color: hifi.buttons.noneBorderless; - colorScheme: hifi.colorSchemes.dark; - anchors.right: sendButton.left; - anchors.rightMargin: 16; - anchors.verticalCenter: parent.verticalCenter; - height: 35; - width: 100; - text: "CANCEL"; - onClicked: { - resetSendMoneyData(); - root.nextActiveView = "sendMoneyHome"; - } - } - - // "SEND" button - HifiControlsUit.Button { - id: sendButton; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - height: 35; - width: 100; - text: "SEND"; - onClicked: { - if (parseInt(amountTextField.text) > parseInt(balanceText.text)) { - amountTextField.focus = true; - amountTextField.error = true; - amountTextFieldError.text = "amount exceeds available funds"; - } else if (amountTextField.text === "" || parseInt(amountTextField.text) < 1) { - amountTextField.focus = true; - amountTextField.error = true; - amountTextFieldError.text = "invalid amount"; - } else { - amountTextFieldError.text = ""; - amountTextField.error = false; - root.isCurrentlySendingMoney = true; - amountTextField.focus = false; - optionalMessage.focus = false; - if (sendMoneyStep.referrer === "connections") { - Commerce.transferHfcToUsername(sendMoneyStep.selectedRecipientUserName, parseInt(amountTextField.text), optionalMessage.text); - } else if (sendMoneyStep.referrer === "nearby") { - Commerce.transferHfcToNode(sendMoneyStep.selectedRecipientNodeID, parseInt(amountTextField.text), optionalMessage.text); - } + anchors.rightMargin: 24; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: 150; + text: "SUBMIT"; + onClicked: { + if (parseInt(amountTextField.text) > parseInt(balanceText.text)) { + amountTextField.focus = true; + amountTextField.error = true; + amountTextFieldError.text = "amount exceeds available funds"; + } else if (amountTextField.text === "" || parseInt(amountTextField.text) < 1) { + amountTextField.focus = true; + amountTextField.error = true; + amountTextFieldError.text = "invalid amount"; + } else { + amountTextFieldError.text = ""; + amountTextField.error = false; + root.isCurrentlySendingMoney = true; + amountTextField.focus = false; + optionalMessage.focus = false; + if (sendMoneyStep.referrer === "connections") { + Commerce.transferHfcToUsername(sendMoneyStep.selectedRecipientUserName, parseInt(amountTextField.text), optionalMessage.text); + } else if (sendMoneyStep.referrer === "nearby") { + Commerce.transferHfcToNode(sendMoneyStep.selectedRecipientNodeID, parseInt(amountTextField.text), optionalMessage.text); } } } @@ -981,7 +1051,7 @@ Item { visible: root.isCurrentlySendingMoney; anchors.fill: parent; - color: Qt.rgba(0.0, 0.0, 0.0, 0.5); + color: Qt.rgba(0.0, 0.0, 0.0, 0.8); // This object is always used in a popup or full-screen Wallet section. // This MouseArea is used to prevent a user from being @@ -993,11 +1063,10 @@ Item { AnimatedImage { id: sendingMoneyImage; - source: "../../../../../icons/profilePicLoading.gif" - width: 160; + source: "./images/loader.gif" + width: 96; height: width; - anchors.top: parent.top; - anchors.topMargin: 185; + anchors.verticalCenter: parent.verticalCenter; anchors.horizontalCenter: parent.horizontalCenter; } @@ -1005,11 +1074,11 @@ Item { text: "Sending"; // Anchors anchors.top: sendingMoneyImage.bottom; - anchors.topMargin: 22; + anchors.topMargin: 4; anchors.horizontalCenter: parent.horizontalCenter; width: paintedWidth; // Text size - size: 24; + size: 26; // Style color: hifi.colors.white; verticalAlignment: Text.AlignVCenter; @@ -1018,17 +1087,17 @@ Item { // Sending Money Overlay END // Payment Success BEGIN - Rectangle { + Item { id: paymentSuccess; visible: root.currentActiveView === "paymentSuccess"; anchors.fill: parent; - color: "#AAAAAA"; Rectangle { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; RalewaySemiBold { id: paymentSentText; @@ -1049,6 +1118,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_paymentSuccess; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -1100,6 +1170,7 @@ Item { anchors.left: sendToText_paymentSuccess.right; anchors.right: parent.right; height: parent.height; + textColor: hifi.colors.blueAccent; displayName: sendMoneyStep.selectedRecipientDisplayName; userName: sendMoneyStep.selectedRecipientUserName; @@ -1145,10 +1216,10 @@ Item { anchors.verticalCenter: parent.verticalCenter; height: 50; // Style - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; } - RalewaySemiBold { + FiraSansSemiBold { id: amountSentText; text: amountTextField.text; // Anchors @@ -1159,7 +1230,7 @@ Item { height: 50; // Style size: 22; - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; } } @@ -1177,7 +1248,7 @@ Item { // Text size size: 22; // Style - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; wrapMode: Text.Wrap; verticalAlignment: Text.AlignTop; } @@ -1203,17 +1274,17 @@ Item { // Payment Success END // Payment Failure BEGIN - Rectangle { + Item { id: paymentFailure; visible: root.currentActiveView === "paymentFailure"; anchors.fill: parent; - color: "#AAAAAA"; Rectangle { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; RalewaySemiBold { id: paymentFailureText; @@ -1234,6 +1305,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_paymentFailure; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -1303,6 +1375,7 @@ Item { anchors.left: sentToText_paymentFailure.right; anchors.right: parent.right; height: parent.height; + textColor: hifi.colors.baseGray; displayName: sendMoneyStep.selectedRecipientDisplayName; userName: sendMoneyStep.selectedRecipientUserName; @@ -1351,7 +1424,7 @@ Item { color: hifi.colors.baseGray; } - RalewaySemiBold { + FiraSansSemiBold { id: amountSentText_paymentFailure; text: amountTextField.text; // Anchors diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg new file mode 100644 index 0000000000..7c5403fda3 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif new file mode 100644 index 0000000000..0536bd1884 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif differ diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg new file mode 100644 index 0000000000..dec87e658d --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg new file mode 100644 index 0000000000..59635a99b1 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg new file mode 100644 index 0000000000..bba07b9567 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2e3e76a7ab..4cf09a56c3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -521,7 +521,13 @@ public: } if (message->message == WM_DEVICECHANGE) { - Midi::USBchanged(); // re-scan the MIDI bus + const float MIN_DELTA_SECONDS = 2.0f; // de-bounce signal + static float lastTriggerTime = 0.0f; + const float deltaSeconds = secTimestampNow() - lastTriggerTime; + lastTriggerTime = secTimestampNow(); + if (deltaSeconds > MIN_DELTA_SECONDS) { + Midi::USBchanged(); // re-scan the MIDI bus + } } } return false; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c9ae9e5d6a..2e633986a0 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2019,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) { _smoothOrientationTimer = 0.0f; } - getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); - + Head* head = getHead(); auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); if (headPose.isValid()) { glm::quat localOrientation = headPose.rotation * Quaternions::Y_180; @@ -2032,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) { head->setBaseYaw(YAW(euler)); head->setBasePitch(PITCH(euler)); head->setBaseRoll(ROLL(euler)); + } else { + head->setBaseYaw(0.0f); + head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); + head->setBaseRoll(0.0f); } } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index d29ee93e62..4804a2db3d 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -564,9 +564,9 @@ void ModelOverlay::animate() { rotationMat * fbxJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); - jointData.translationSet = true; + jointData.translationIsDefaultPose = false; jointData.rotation = glmExtractRotation(finalMat); - jointData.rotationSet = true; + jointData.rotationIsDefaultPose = false; } } // Set the data in the model diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 1c9d9a3447..b1b41775a8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1705,16 +1705,16 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { // rotations are in absolute rig frame. glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot(); data.rotation = _internalPoseSet._absolutePoses[i].rot(); - data.rotationSet = !isEqual(data.rotation, defaultAbsRot); + data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot); // translations are in relative frame but scaled so that they are in meters, // instead of geometry units. glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans(); data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans(); - data.translationSet = !isEqual(data.translation, defaultRelTrans); + data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans); } else { - data.translationSet = false; - data.rotationSet = false; + data.translationIsDefaultPose = true; + data.rotationIsDefaultPose = true; } } } @@ -1739,11 +1739,11 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); for (int i = 0; i < numJoints; i++) { const JointData& data = jointDataVec.at(i); - if (data.rotationSet) { + if (data.rotationIsDefaultPose) { + rotations.push_back(absoluteDefaultPoses[i].rot()); + } else { // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame rotations.push_back(rigToGeometryRot * data.rotation); - } else { - rotations.push_back(absoluteDefaultPoses[i].rot()); } } @@ -1759,11 +1759,11 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { const JointData& data = jointDataVec.at(i); _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; _internalPoseSet._relativePoses[i].rot() = rotations[i]; - if (data.translationSet) { + if (data.translationIsDefaultPose) { + _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); + } else { // JointData translations are in scaled relative-frame so we scale back to regular relative-frame _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation; - } else { - _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); } } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2c491645be..8ebbd66e76 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "AvatarLogging.h" @@ -77,6 +78,16 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) { return totalSize; } +size_t AvatarDataPacket::maxJointDefaultPoseFlagsSize(size_t numJoints) { + const size_t bitVectorSize = calcBitVectorSize((int)numJoints); + size_t totalSize = sizeof(uint8_t); // numJoints + + // one set of bits for rotation and one for translation + const size_t NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION = 2; + totalSize += NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION * bitVectorSize; + + return totalSize; +} AvatarData::AvatarData() : SpatiallyNestable(NestableType::Avatar, QUuid()), @@ -272,6 +283,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent bool hasFaceTrackerInfo = false; bool hasJointData = false; + bool hasJointDefaultPoseFlags = false; if (sendPALMinimum) { hasAudioLoudness = true; @@ -290,6 +302,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime)); hasJointData = sendAll || !sendMinimum; + hasJointDefaultPoseFlags = hasJointData; } @@ -314,7 +327,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent | (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0) | (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0) | (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0) - | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0); + | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0) + | (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0); memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); @@ -541,14 +555,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData[i]; + const JointData& last = lastSentJointData[i]; - // The dot product for smaller rotations is a smaller number. - // So if the dot() is less than the value, then the rotation is a larger angle of rotation - bool largeEnoughRotation = fabsf(glm::dot(data.rotation, lastSentJointData[i].rotation)) < minRotationDOT; + if (!data.rotationIsDefaultPose) { + if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) { - if (sendAll || lastSentJointData[i].rotation != data.rotation) { - if (sendAll || !cullSmallChanges || largeEnoughRotation) { - if (data.rotationSet) { + bool largeEnoughRotation = true; + if (cullSmallChanges) { + // The dot product for smaller rotations is a smaller number. + // So if the dot() is less than the value, then the rotation is a larger angle of rotation + largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT; + } + + if (sendAll || !cullSmallChanges || largeEnoughRotation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG rotationSentCount++; @@ -557,8 +576,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (sentJointDataOut) { localSentJointDataOut[i].rotation = data.rotation; + localSentJointDataOut[i].rotationIsDefaultPose = false; } - } } } @@ -588,11 +607,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent float maxTranslationDimension = 0.0; for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData[i]; - if (sendAll || lastSentJointData[i].translation != data.translation) { - if (sendAll || - !cullSmallChanges || - glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { - if (data.translationSet) { + + if (!data.translationIsDefaultPose) { + if (sendAll || lastSentJointData[i].translation != data.translation) { + if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG translationSentCount++; @@ -606,8 +624,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (sentJointDataOut) { localSentJointDataOut[i].translation = data.translation; + localSentJointDataOut[i].translationIsDefaultPose = false; } - } } } @@ -655,6 +673,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } + if (hasJointDefaultPoseFlags) { + auto startSection = destinationBuffer; + QReadLocker readLock(&_jointDataLock); + + // write numJoints + int numJoints = _jointData.size(); + *destinationBuffer++ = (uint8_t)numJoints; + + // write rotationIsDefaultPose bits + destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { + return _jointData[i].rotationIsDefaultPose; + }); + + // write translationIsDefaultPose bits + destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { + return _jointData[i].translationIsDefaultPose; + }); + + if (outboundDataRateOut) { + size_t numBytes = destinationBuffer - startSection; + outboundDataRateOut->jointDefaultPoseFlagsRate.increment(numBytes); + } + } + int avatarDataSize = destinationBuffer - startPosition; if (avatarDataSize > (int)byteArraySize) { @@ -664,6 +706,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent return avatarDataByteArray.left(avatarDataSize); } + // NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation void AvatarData::doneEncoding(bool cullSmallChanges) { // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. @@ -674,7 +717,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) { if (_lastSentJointData[i].rotation != data.rotation) { if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { - if (data.rotationSet) { + if (!data.rotationIsDefaultPose) { _lastSentJointData[i].rotation = data.rotation; } } @@ -682,7 +725,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) { if (_lastSentJointData[i].translation != data.translation) { if (!cullSmallChanges || glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) { - if (data.translationSet) { + if (!data.translationIsDefaultPose) { _lastSentJointData[i].translation = data.translation; } } @@ -730,6 +773,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa // read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { + // lazily allocate memory for HeadData in case we're not an Avatar instance lazyInitHeadData(); @@ -745,18 +789,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { #define HAS_FLAG(B,F) ((B & F) == F) - bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION); - bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX); - bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION); - bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE); - bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION); - bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS); - bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX); - bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS); - bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO); - bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION); - bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO); - bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA); + bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION); + bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX); + bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION); + bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE); + bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION); + bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS); + bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX); + bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS); + bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO); + bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION); + bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO); + bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA); + bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS); quint64 now = usecTimestampNow(); @@ -1055,7 +1100,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validRotations[i]) { sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation); _hasNewJointData = true; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } } @@ -1090,7 +1135,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validTranslations[i]) { sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); _hasNewJointData = true; - data.translationSet = true; + data.translationIsDefaultPose = false; } } @@ -1110,6 +1155,32 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _jointDataUpdateRate.increment(); } + if (hasJointDefaultPoseFlags) { + auto startSection = sourceBuffer; + + QWriteLocker writeLock(&_jointDataLock); + + PACKET_READ_CHECK(JointDefaultPoseFlagsNumJoints, sizeof(uint8_t)); + int numJoints = (int)*sourceBuffer++; + + _jointData.resize(numJoints); + + size_t bitVectorSize = calcBitVectorSize(numJoints); + PACKET_READ_CHECK(JointDefaultPoseFlagsRotationFlags, bitVectorSize); + sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) { + _jointData[i].rotationIsDefaultPose = value; + }); + + PACKET_READ_CHECK(JointDefaultPoseFlagsTranslationFlags, bitVectorSize); + sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) { + _jointData[i].translationIsDefaultPose = value; + }); + + int numBytesRead = sourceBuffer - startSection; + _jointDefaultPoseFlagsRate.increment(numBytesRead); + _jointDefaultPoseFlagsUpdateRate.increment(); + } + int numBytesRead = sourceBuffer - startPosition; _averageBytesReceived.updateAverage(numBytesRead); @@ -1146,6 +1217,8 @@ float AvatarData::getDataRate(const QString& rateName) const { return _faceTrackerRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "jointData") { return _jointDataRate.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "jointDefaultPoseFlagsRate") { + return _jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "globalPositionOutbound") { return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "localPositionOutbound") { @@ -1170,6 +1243,8 @@ float AvatarData::getDataRate(const QString& rateName) const { return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "jointDataOutbound") { return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "jointDefaultPoseFlagsOutbound") { + return _outboundDataRate.jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT; } return 0.0f; } @@ -1236,9 +1311,9 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v } JointData& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; } void AvatarData::clearJointData(int index) { @@ -1294,7 +1369,8 @@ void AvatarData::setJointData(const QString& name, const glm::quat& rotation, co auto& jointData = _jointData[index]; jointData.rotation = rotation; jointData.translation = translation; - jointData.rotationSet = jointData.translationSet = true; + jointData.rotationIsDefaultPose = false; + jointData.translationIsDefaultPose = false; }); } @@ -1304,7 +1380,7 @@ void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation writeLockWithNamedJointIndex(name, [&](int index) { auto& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; }); } @@ -1314,7 +1390,7 @@ void AvatarData::setJointTranslation(const QString& name, const glm::vec3& trans writeLockWithNamedJointIndex(name, [&](int index) { auto& data = _jointData[index]; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; }); } @@ -1328,7 +1404,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) { } JointData& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { @@ -1341,7 +1417,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { } JointData& data = _jointData[index]; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; } void AvatarData::clearJointData(const QString& name) { @@ -1397,7 +1473,7 @@ void AvatarData::setJointRotations(const QVector& jointRotations) { for (int i = 0; i < size; ++i) { auto& data = _jointData[i]; data.rotation = jointRotations[i]; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } } @@ -1419,7 +1495,7 @@ void AvatarData::setJointTranslations(const QVector& jointTranslation for (int i = 0; i < size; ++i) { auto& data = _jointData[i]; data.translation = jointTranslations[i]; - data.translationSet = true; + data.translationIsDefaultPose = false; } } @@ -1996,9 +2072,9 @@ JointData jointDataFromJsonValue(const QJsonValue& json) { if (json.isArray()) { QJsonArray array = json.toArray(); result.rotation = quatFromJsonValue(array[0]); - result.rotationSet = true; + result.rotationIsDefaultPose = false; result.translation = vec3FromJsonValue(array[1]); - result.translationSet = true; + result.translationIsDefaultPose = false; } return result; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 00cc760658..92c505f5c3 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -113,18 +113,19 @@ namespace AvatarDataPacket { // Packet State Flags - we store the details about the existence of other records in this bitset: // AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of using HasFlags = uint16_t; - const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; - const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; - const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2; - const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3; - const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4; - const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5; - const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6; - const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7; - const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8; - const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; - const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; - const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; + const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; + const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2; + const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3; + const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4; + const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5; + const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6; + const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7; + const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8; + const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; + const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; + const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12; const size_t AVATAR_HAS_FLAGS_SIZE = 2; using SixByteQuat = uint8_t[6]; @@ -256,6 +257,15 @@ namespace AvatarDataPacket { }; */ size_t maxJointDataSize(size_t numJoints); + + /* + struct JointDefaultPoseFlags { + uint8_t numJoints; + uint8_t rotationIsDefaultPoseBits[ceil(numJoints / 8)]; + uint8_t translationIsDefaultPoseBits[ceil(numJoints / 8)]; + }; + */ + size_t maxJointDefaultPoseFlagsSize(size_t numJoints); } const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation @@ -321,6 +331,7 @@ public: RateCounter<> parentInfoRate; RateCounter<> faceTrackerRate; RateCounter<> jointDataRate; + RateCounter<> jointDefaultPoseFlagsRate; }; class AvatarPriority { @@ -810,6 +821,7 @@ protected: RateCounter<> _parentInfoRate; RateCounter<> _faceTrackerRate; RateCounter<> _jointDataRate; + RateCounter<> _jointDefaultPoseFlagsRate; // Some rate data for incoming data updates RateCounter<> _parseBufferUpdateRate; @@ -825,6 +837,7 @@ protected: RateCounter<> _parentInfoUpdateRate; RateCounter<> _faceTrackerUpdateRate; RateCounter<> _jointDataUpdateRate; + RateCounter<> _jointDefaultPoseFlagsUpdateRate; // Some rate data for outgoing data AvatarDataRate _outboundDataRate; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index fc1688974c..2e39e2e498 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1050,7 +1050,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - QVector jointsData; + QVector jointsData; const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 8996665262..a615beefa9 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -452,7 +452,7 @@ void ModelEntityItem::resizeJointArrays(int newSize) { }); } -void ModelEntityItem::setAnimationJointsData(const QVector& jointsData) { +void ModelEntityItem::setAnimationJointsData(const QVector& jointsData) { resizeJointArrays(jointsData.size()); _jointDataLock.withWriteLock([&] { for (auto index = 0; index < jointsData.size(); ++index) { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 6169039f52..c2109ba51f 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -124,7 +124,7 @@ public: virtual void setJointTranslations(const QVector& translations); virtual void setJointTranslationsSet(const QVector& translationsSet); - virtual void setAnimationJointsData(const QVector& jointsData); + virtual void setAnimationJointsData(const QVector& jointsData); QVector getJointRotations() const; QVector getJointRotationsSet() const; @@ -150,7 +150,7 @@ protected: bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations struct ModelJointData { - JointData joint; + EntityJointData joint; bool rotationDirty { false }; bool translationDirty { false }; }; diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 69c35c4a20..1f190111f2 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -163,7 +163,7 @@ void Midi::sendRawMessage(int device, int raw) { void Midi::sendMessage(int device, int channel, int type, int note, int velocity) { int message = (channel - 1) | (type << MIDI_SHIFT_STATUS); if (broadcastEnabled) { - for (int i = 0; i < midihout.size(); i++) { + for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth) if (midihout[i] != NULL) { midiOutShortMsg(midihout[i], message | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY)); } @@ -174,9 +174,9 @@ void Midi::sendMessage(int device, int channel, int type, int note, int velocity } void Midi::sendNote(int status, int note, int velocity) { - for (int i = 0; i < midihout.size(); i++) { + for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth) if (midihout[i] != NULL) { - midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (velocity << MIDI_SHIFT_VELOCITY)); + midiOutShortMsg(midihout[i], status | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY)); } } } @@ -283,9 +283,6 @@ void Midi::midiHardwareChange() { Midi::Midi() { instance = this; -#if defined Q_OS_WIN32 - midiOutExclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing (Lags) -#endif MidiSetup(); } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 711aeb2cc2..c48c6bfc0b 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::UpdatedMannequinDefaultAvatar); + return static_cast(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 458666571c..0f69691bd5 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -247,7 +247,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { AvatarIdentitySequenceFront, IsReplicatedInAvatarIdentity, AvatarIdentityLookAtSnapping, - UpdatedMannequinDefaultAvatar + UpdatedMannequinDefaultAvatar, + AvatarJointDefaultPoseFlags }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/shared/src/BitVectorHelpers.h b/libraries/shared/src/BitVectorHelpers.h new file mode 100644 index 0000000000..71826036eb --- /dev/null +++ b/libraries/shared/src/BitVectorHelpers.h @@ -0,0 +1,58 @@ +// +// BitVectorHelpers.h +// libraries/shared/src +// +// Created by Anthony Thibault on 1/19/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 +// + +#ifndef hifi_BitVectorHelpers_h +#define hifi_BitVectorHelpers_h + +size_t calcBitVectorSize(int numBits) { + return ((numBits - 1) >> 3) + 1; +} + +// func should be of type bool func(int index) +template +size_t writeBitVector(uint8_t* destinationBuffer, int numBits, const F& func) { + size_t totalBytes = ((numBits - 1) >> 3) + 1; + uint8_t* cursor = destinationBuffer; + uint8_t byte = 0; + uint8_t bit = 0; + + for (int i = 0; i < numBits; i++) { + if (func(i)) { + byte |= (1 << bit); + } + if (++bit == BITS_IN_BYTE) { + *cursor++ = byte; + byte = 0; + bit = 0; + } + } + return totalBytes; +} + +// func should be of type 'void func(int index, bool value)' +template +size_t readBitVector(const uint8_t* sourceBuffer, int numBits, const F& func) { + size_t totalBytes = ((numBits - 1) >> 3) + 1; + const uint8_t* cursor = sourceBuffer; + uint8_t bit = 0; + + for (int i = 0; i < numBits; i++) { + bool value = (bool)(*cursor & (1 << bit)); + func(i, value); + if (++bit == BITS_IN_BYTE) { + cursor++; + bit = 0; + } + } + return totalBytes; +} + +#endif diff --git a/libraries/shared/src/JointData.h b/libraries/shared/src/JointData.h index a05b5c649a..f4c8b89e7a 100644 --- a/libraries/shared/src/JointData.h +++ b/libraries/shared/src/JointData.h @@ -5,14 +5,27 @@ #include #include -// Used by the avatar mixer to describe a single joint -// These are relative to their parent and translations are in meters -class JointData { +class EntityJointData { public: glm::quat rotation; + glm::vec3 translation; bool rotationSet = false; - glm::vec3 translation; // meters bool translationSet = false; }; +// Used by the avatar mixer to describe a single joint +// Translations relative to their parent and are in meters. +// Rotations are absolute (i.e. not relative to parent) and are in rig space. +class JointData { +public: + glm::quat rotation; + glm::vec3 translation; + + // This indicates that the rotation or translation is the same as the defaultPose for the avatar. + // if true, it also means that the rotation or translation value in this structure is not valid and + // should be replaced by the avatar's actual default pose value. + bool rotationIsDefaultPose = true; + bool translationIsDefaultPose = true; +}; + #endif diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 1ecf86f484..0191baca5e 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -31,6 +31,8 @@ const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; const QString TabletScriptingInterface::QML = "hifi/tablet/TabletRoot.qml"; +const QString BUTTON_SORT_ORDER_KEY = "sortOrder"; +const int DEFAULT_BUTTON_SORT_ORDER = 100; static QString getUsername() { QString username = "Unknown user"; @@ -74,11 +76,21 @@ QVariant TabletButtonListModel::data(const QModelIndex& index, int role) const { } TabletButtonProxy* TabletButtonListModel::addButton(const QVariant& properties) { - auto tabletButtonProxy = QSharedPointer(new TabletButtonProxy(properties.toMap())); + QVariantMap newProperties = properties.toMap(); + if (newProperties.find(BUTTON_SORT_ORDER_KEY) == newProperties.end()) { + newProperties[BUTTON_SORT_ORDER_KEY] = DEFAULT_BUTTON_SORT_ORDER; + } + int index = computeNewButtonIndex(newProperties); + auto button = QSharedPointer(new TabletButtonProxy(newProperties)); beginResetModel(); - _buttons.push_back(tabletButtonProxy); + int numButtons = (int)_buttons.size(); + if (index < numButtons) { + _buttons.insert(_buttons.begin() + index, button); + } else { + _buttons.push_back(button); + } endResetModel(); - return tabletButtonProxy.data(); + return button.data(); } void TabletButtonListModel::removeButton(TabletButtonProxy* button) { @@ -92,6 +104,20 @@ void TabletButtonListModel::removeButton(TabletButtonProxy* button) { endResetModel(); } +int TabletButtonListModel::computeNewButtonIndex(const QVariantMap& newButtonProperties) { + int numButtons = (int)_buttons.size(); + int newButtonSortOrder = newButtonProperties[BUTTON_SORT_ORDER_KEY].toInt(); + if (newButtonSortOrder == DEFAULT_BUTTON_SORT_ORDER) return numButtons; + for (int i = 0; i < numButtons; i++) { + QVariantMap tabletButtonProperties = _buttons[i]->getProperties(); + int tabletButtonSortOrder = tabletButtonProperties[BUTTON_SORT_ORDER_KEY].toInt(); + if (newButtonSortOrder <= tabletButtonSortOrder) { + return i; + } + } + return numButtons; +} + TabletButtonsProxyModel::TabletButtonsProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 56e3ae257b..34827117f0 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -115,6 +115,7 @@ protected: friend class TabletProxy; TabletButtonProxy* addButton(const QVariant& properties); void removeButton(TabletButtonProxy* button); + int computeNewButtonIndex(const QVariantMap& newButtonProperties); using List = std::list>; static QHash _roles; static Qt::ItemFlags _flags; diff --git a/scripts/developer/tests/dynamics/dynamicsTests.js b/scripts/developer/tests/dynamics/dynamicsTests.js index c0b001eab3..e9262c9308 100644 --- a/scripts/developer/tests/dynamics/dynamicsTests.js +++ b/scripts/developer/tests/dynamics/dynamicsTests.js @@ -22,8 +22,7 @@ var button = tablet.addButton({ icon: Script.resolvePath("dynamicsTests.svg"), - text: "Dynamics", - sortOrder: 15 + text: "Dynamics" }); diff --git a/scripts/developer/utilities/render/debugHighlight.js b/scripts/developer/utilities/render/debugHighlight.js index e70565cec2..c2173f6e2a 100644 --- a/scripts/developer/utilities/render/debugHighlight.js +++ b/scripts/developer/utilities/render/debugHighlight.js @@ -32,8 +32,7 @@ var button = tablet.addButton({ text: TABLET_BUTTON_NAME, icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL, - sortOrder: 1 + activeIcon: ACTIVE_ICON_URL }); var hasEventBridge = false; diff --git a/scripts/developer/utilities/render/lod.js b/scripts/developer/utilities/render/lod.js index dc0b99edc2..307e509d39 100644 --- a/scripts/developer/utilities/render/lod.js +++ b/scripts/developer/utilities/render/lod.js @@ -30,8 +30,7 @@ var button = tablet.addButton({ text: TABLET_BUTTON_NAME, icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL, - sortOrder: 1 + activeIcon: ACTIVE_ICON_URL }); var hasEventBridge = false; diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index 1e2ac1261f..6482c884ff 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -31,8 +31,7 @@ var button = tablet.addButton({ text: TABLET_BUTTON_NAME, icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL, - sortOrder: 1 + activeIcon: ACTIVE_ICON_URL }); var hasEventBridge = false; diff --git a/scripts/developer/utilities/tools/currentAPI.js b/scripts/developer/utilities/tools/currentAPI.js index 175b84b8d9..6cab6a5710 100644 --- a/scripts/developer/utilities/tools/currentAPI.js +++ b/scripts/developer/utilities/tools/currentAPI.js @@ -28,8 +28,8 @@ var window = new OverlayWindow({ title: 'API Debugger', source: qml, - width: 1200, - height: 500 + width: 500, + height: 700 }); window.closed.connect(function () { diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 0cb414e23c..b0a2e114a3 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -945,8 +945,7 @@ tabletButton = tablet.addButton({ icon: tabletButtonIcon, activeIcon: tabletButtonActiveIcon, - text: tabletButtonName, - sortOrder: 0 + text: tabletButtonName }); Messages.subscribe(channelName); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index f93d6714f1..ad864622ed 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -593,10 +593,16 @@ getConnectionData(false); break; case 'enable_ChooseRecipientNearbyMode': - Script.update.connect(updateOverlays); + if (!isUpdateOverlaysWired) { + Script.update.connect(updateOverlays); + isUpdateOverlaysWired = true; + } break; case 'disable_ChooseRecipientNearbyMode': - Script.update.disconnect(updateOverlays); + if (isUpdateOverlaysWired) { + Script.update.disconnect(updateOverlays); + isUpdateOverlaysWired = false; + } removeOverlays(); break; default: @@ -681,14 +687,18 @@ } } var isWired = false; + var isUpdateOverlaysWired = false; function off() { if (isWired) { // It is not ok to disconnect these twice, hence guard. Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Script.update.disconnect(updateOverlays); Controller.mousePressEvent.disconnect(handleMouseEvent); Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); isWired = false; } + if (isUpdateOverlaysWired) { + Script.update.disconnect(updateOverlays); + isUpdateOverlaysWired = false; + } triggerMapping.disable(); // It's ok if we disable twice. triggerPressMapping.disable(); // see above removeOverlays(); diff --git a/scripts/system/emote.js b/scripts/system/emote.js index f1f739c126..139870fd63 100644 --- a/scripts/system/emote.js +++ b/scripts/system/emote.js @@ -43,7 +43,7 @@ var activeTimer = false; // used to cancel active timer if a user plays an amima var activeEmote = false; // to keep track of the currently playing emote button = tablet.addButton({ - //icon: "icons/tablet-icons/emote.svg", // TODO - we need graphics for this + icon: "icons/tablet-icons/EmoteAppIcon.svg", text: EMOTE_LABEL, sortOrder: EMOTE_APP_SORT_ORDER }); diff --git a/scripts/system/generalSettings.js b/scripts/system/generalSettings.js index 082528ffc5..d3848da7d0 100644 --- a/scripts/system/generalSettings.js +++ b/scripts/system/generalSettings.js @@ -37,8 +37,7 @@ tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ icon: "icons/tablet-icons/goto-i.svg", - text: buttonName, - sortOrder: 8 + text: buttonName }); } diff --git a/scripts/system/goto.js b/scripts/system/goto.js index d364bf579e..5cc5bad844 100644 --- a/scripts/system/goto.js +++ b/scripts/system/goto.js @@ -41,8 +41,7 @@ if (Settings.getValue("HUDUIEnabled")) { button = tablet.addButton({ icon: "icons/tablet-icons/goto-i.svg", activeIcon: "icons/tablet-icons/goto-a.svg", - text: buttonName, - sortOrder: 8 + text: buttonName }); } diff --git a/scripts/system/html/EmoteApp.html b/scripts/system/html/EmoteApp.html index 30ef3e17a1..0a423b9b6c 100644 --- a/scripts/system/html/EmoteApp.html +++ b/scripts/system/html/EmoteApp.html @@ -44,11 +44,11 @@ input[type=button] { font-family: 'Raleway'; font-weight: bold; - font-size: 13px; + font-size: 20px; text-transform: uppercase; vertical-align: top; - height: 28px; - min-width: 120px; + height: 105px; + min-width: 190px; padding: 0px 18px; margin-right: 6px; border-radius: 5px; @@ -98,14 +98,14 @@

Click an emotion to Emote:

-

-

-

-

-

-

-

-

+

+

+

+

+

+

+

+

diff --git a/scripts/system/run.js b/scripts/system/run.js index 054cca6d9c..c34271b18d 100644 --- a/scripts/system/run.js +++ b/scripts/system/run.js @@ -10,8 +10,7 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ icon: Script.resolvePath("assets/images/run.svg"), - text: "RUN", - sortOrder: 15 + text: "RUN" }); function onClicked() { diff --git a/scripts/system/tablet-users.js b/scripts/system/tablet-users.js index 6f37cd55eb..6181173818 100644 --- a/scripts/system/tablet-users.js +++ b/scripts/system/tablet-users.js @@ -37,8 +37,7 @@ var button = tablet.addButton({ icon: "icons/tablet-icons/users-i.svg", activeIcon: "icons/tablet-icons/users-a.svg", - text: "USERS", - sortOrder: 11 + text: "USERS" }); var onUsersScreen = false; diff --git a/unpublishedScripts/marketplace/clap/clapApp.js b/unpublishedScripts/marketplace/clap/clapApp.js index b2d8ce55db..7118b3623c 100644 --- a/unpublishedScripts/marketplace/clap/clapApp.js +++ b/unpublishedScripts/marketplace/clap/clapApp.js @@ -31,8 +31,7 @@ var activeButton = tablet.addButton({ icon: whiteIcon, activeIcon: blackIcon, text: APP_NAME, - isActive: isActive, - sortOrder: 11 + isActive: isActive }); if (isActive) { diff --git a/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js b/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js index 7bc65722cd..0bd23552e4 100644 --- a/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js +++ b/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js @@ -32,8 +32,7 @@ var button = tablet.addButton({ icon: ICONS.icon, activeIcon: ICONS.activeIcon, - text: TABLET_BUTTON_NAME, - sortOrder: 1 + text: TABLET_BUTTON_NAME }); var hasEventBridge = false;