From 3ac44376b2a054d1cec8885da5d81b281d9508e4 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 16 May 2018 12:54:42 +0300 Subject: [PATCH] bind Settings page to a backend --- interface/resources/qml/hifi/AvatarApp.qml | 23 +++++- .../resources/qml/hifi/avatarapp/Settings.qml | 41 ++++++++-- interface/src/AvatarBookmarks.cpp | 2 + interface/src/avatar/MyAvatar.cpp | 7 ++ interface/src/avatar/MyAvatar.h | 14 ++++ .../src/avatars-renderer/Avatar.cpp | 2 + .../src/avatars-renderer/Avatar.h | 3 + scripts/system/avatarapp.js | 81 ++++++++++++++++++- 8 files changed, 164 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index d113536442..38439949d1 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -30,6 +30,7 @@ Rectangle { } property var jointNames; + property var currentAvatarSettings; property string avatarName: currentAvatar ? currentAvatar.name : '' property string avatarUrl: currentAvatar ? currentAvatar.thumbnailUrl : null @@ -40,6 +41,7 @@ Rectangle { var currentAvatarObject = allAvatars.makeAvatarObject(avatar, bookmarkName); currentAvatar = currentAvatarModel.makeAvatarEntry(currentAvatarObject); + console.debug('AvatarApp.qml: currentAvatarObject: ', currentAvatarObject, 'currentAvatar: ', currentAvatar, JSON.stringify(currentAvatar.wearables, 0, 4)); console.debug('currentAvatar.wearables: ', currentAvatar.wearables); } @@ -71,6 +73,13 @@ Rectangle { } adjustWearables.refresh(currentAvatar); + } else if(message.method === 'scaleChanged') { + currentAvatar.avatarScale = message.value; + updateCurrentAvatarInBookmarks(currentAvatar); + } else if(message.method === 'settingChanged') { + currentAvatarSettings[message.name] = message.value; + } else if(message.method === 'changeSettings') { + currentAvatarSettings = message.settings; } else if(message.method === 'bookmarkLoaded') { setCurrentAvatar(message.data.currentAvatar, message.data.name); var avatarIndex = allAvatars.findAvatarIndex(currentAvatar.name); @@ -109,6 +118,7 @@ Rectangle { allAvatars.populate(getAvatarsData.bookmarks); setCurrentAvatar(getAvatarsData.currentAvatar, ''); displayNameInput.text = getAvatarsData.displayName; + currentAvatarSettings = getAvatarsData.currentAvatarSettings; console.debug('currentAvatar: ', JSON.stringify(currentAvatar, null, '\t')); updateCurrentAvatarInBookmarks(currentAvatar); @@ -120,6 +130,8 @@ Rectangle { } function updateCurrentAvatarInBookmarks(avatar) { + console.debug('searching avatar in bookmarks... '); + var bookmarkAvatarIndex = allAvatars.findAvatarIndexByValue(avatar); if(bookmarkAvatarIndex === -1) { @@ -167,7 +179,7 @@ Rectangle { avatarIconVisible: mainPageVisible settingsButtonVisible: mainPageVisible onSettingsClicked: { - settings.open(); + settings.open(currentAvatarSettings, currentAvatar.avatarScale); } } @@ -181,6 +193,15 @@ Rectangle { z: 3 onSaveClicked: function() { + var avatarSettings = { + dominantHand : settings.dominantHandIsLeft ? 'left' : 'right', + collisionsEnabled : settings.avatarCollisionsOn, + animGraphUrl : settings.avatarAnimationJSON, + collisionSoundUrl : settings.avatarCollisionSoundUrl + }; + + emitSendToScript({'method' : 'saveSettings', 'settings' : avatarSettings, 'avatarScale': settings.scaleValue}) + close(); } onCancelClicked: function() { diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml index d73ed49d4e..562643e685 100644 --- a/interface/resources/qml/hifi/avatarapp/Settings.qml +++ b/interface/resources/qml/hifi/avatarapp/Settings.qml @@ -7,7 +7,7 @@ import "../../controls-uit" as HifiControlsUit import "../../controls" as HifiControls Rectangle { - id: settings + id: root color: 'white' visible: false; @@ -15,7 +15,32 @@ Rectangle { property alias onSaveClicked: dialogButtons.onYesClicked property alias onCancelClicked: dialogButtons.onNoClicked - function open() { + property real scaleValue: scaleSlider.value / 10 + property alias dominantHandIsLeft: leftHandRadioButton.checked + property alias avatarCollisionsOn: collisionsEnabledRadiobutton.checked + property alias avatarAnimationJSON: avatarAnimationUrlInputText.text + property alias avatarCollisionSoundUrl: avatarCollisionSoundUrlInputText.text + + function open(settings, avatarScale) { + console.debug('Settings.qml: open: ', JSON.stringify(settings, 0, 4)); + + scaleSlider.value = Math.round(avatarScale * 10); + + if(settings.dominantHand === 'left') { + leftHandRadioButton.checked = true; + } else { + rightHandRadioButton.checked = true; + } + + if(settings.collisionsEnabled) { + collisionsEnabledRadiobutton.checked = true; + } else { + collisionsDisabledRadioButton.checked = true; + } + + avatarAnimationJSON = settings.animGraphUrl; + avatarCollisionSoundUrl = settings.collisionSoundUrl; + visible = true; } @@ -71,9 +96,9 @@ Rectangle { } HifiControlsUit.Slider { - id: slider - from: 0 - to: 100 + id: scaleSlider + from: 1 + to: 30 anchors.verticalCenter: parent.verticalCenter Layout.fillWidth: true } @@ -172,7 +197,7 @@ Rectangle { } HifiControlsUit.RadioButton { - id: onRadioButton + id: collisionsEnabledRadiobutton Layout.row: 1 Layout.column: 1 @@ -191,7 +216,7 @@ Rectangle { } HifiControlsUit.RadioButton { - id: offRadioButton + id: collisionsDisabledRadioButton Layout.row: 1 Layout.column: 2 @@ -220,6 +245,7 @@ Rectangle { } InputTextStyle4 { + id: avatarAnimationUrlInputText anchors.left: parent.left anchors.right: parent.right placeholderText: 'user\\file\\dir' @@ -243,6 +269,7 @@ Rectangle { } InputTextStyle4 { + id: avatarCollisionSoundUrlInputText anchors.left: parent.left anchors.right: parent.right placeholderText: 'https://hifi-public.s3.amazonaws.com/sounds/Collisions-' diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index d17fa6889a..8fb3529928 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -160,6 +160,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { QVariantMap bookmark = bookmarkEntry.value().toMap(); if (!bookmark.empty()) { auto myAvatar = DependencyManager::get()->getMyAvatar(); + myAvatar->blockSignals(true); myAvatar->removeAvatarEntities(); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); @@ -174,6 +175,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList(); addAvatarEntities(avatarEntities); + myAvatar->blockSignals(false); emit bookmarkLoaded(bookmarkName); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 77814dc461..16917aab38 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1902,6 +1902,8 @@ void MyAvatar::clearScriptableSettings() { } void MyAvatar::setCollisionSoundURL(const QString& url) { + qDebug() << "setCollisionSoundURL: " << url << _collisionSoundURL; + if (url != _collisionSoundURL) { _collisionSoundURL = url; @@ -1983,6 +1985,7 @@ QUrl MyAvatar::getAnimGraphUrl() const { } void MyAvatar::setAnimGraphUrl(const QUrl& url) { + qDebug() << "setAnimGraphUrl" << url.toString(); if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setAnimGraphUrl", Q_ARG(QUrl, url)); @@ -1992,6 +1995,9 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) { if (_currentAnimGraphUrl.get() == url) { return; } + + emit animGraphUrlChanged(url); + destroyAnimGraph(); _skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render. @@ -2816,6 +2822,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) { } _characterController.setCollisionless(!enabled); + emit collisionsEnabledChanged(enabled); } bool MyAvatar::getCollisionsEnabled() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 58a82240db..5c828b0b66 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1294,6 +1294,20 @@ signals: */ void collisionWithEntity(const Collision& collision); + /**jsdoc + * @function MyAvatar.collisionsEnabledChanged + * @param {boolean} enabled + * @returns {Signal} + */ + void collisionsEnabledChanged(bool enabled); + + /**jsdoc + * @function MyAvatar.animGraphUrlChanged + * @param {url} url + * @returns {Signal} + */ + void animGraphUrlChanged(const QUrl& url); + /**jsdoc * @function MyAvatar.energyChanged * @param {number} energy diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 843235c0e1..dd2828eb25 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -212,6 +212,8 @@ void Avatar::setTargetScale(float targetScale) { _targetScale = newValue; _scaleChanged = usecTimestampNow(); _isAnimatingScale = true; + + emit targetScaleChanged(targetScale); } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index bb9d6d8cc9..fe9a347c20 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -361,6 +361,9 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override; +signals: + void targetScaleChanged(float targetScale); + public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 400951287b..a1a9bf7029 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -60,6 +60,15 @@ function getMyAvatar() { return avatar; } +function getMyAvatarSettings() { + return { + dominantHand: MyAvatar.getDominantHand(), + collisionsEnabled : MyAvatar.getCollisionsEnabled(), + collisionSoundUrl : MyAvatar.collisionSoundURL, + animGraphUrl : MyAvatar.getAnimGraphUrl(), + } +} + function updateAvatarWearables(avatar, bookmarkAvatarName) { console.debug('avatarapp.js: scheduling wearablesUpdated notify for', bookmarkAvatarName); @@ -97,6 +106,48 @@ var adjustWearables = { var currentAvatarWearablesBackup = null; var currentAvatar = null; +var currentAvatarSettings = getMyAvatarSettings(); + +function onTargetScaleChanged() { + console.debug('onTargetScaleChanged: ', MyAvatar.getAvatarScale()); + if(currentAvatar.scale !== MyAvatar.getAvatarScale()) { + currentAvatar.scale = MyAvatar.getAvatarScale(); + sendToQml({'method' : 'scaleChanged', 'value' : currentAvatar.scale}) + } +} + +function onDominantHandChanged(dominantHand) { + console.debug('onDominantHandChanged: ', dominantHand); + if(currentAvatarSettings.dominantHand !== dominantHand) { + currentAvatarSettings.dominantHand = dominantHand; + sendToQml({'method' : 'settingChanged', 'name' : 'dominantHand', 'value' : dominantHand}) + } +} + +function onCollisionsEnabledChanged(enabled) { + console.debug('onCollisionsEnabledChanged: ', enabled); + if(currentAvatarSettings.collisionsEnabled !== enabled) { + currentAvatarSettings.collisionsEnabled = enabled; + sendToQml({'method' : 'settingChanged', 'name' : 'collisionsEnabled', 'value' : enabled}) + } +} + +function onNewCollisionSoundUrl(url) { + console.debug('onNewCollisionSoundUrl: ', url); + if(currentAvatarSettings.collisionSoundUrl !== url) { + currentAvatarSettings.collisionSoundUrl = url; + sendToQml({'method' : 'settingChanged', 'name' : 'collisionSoundUrl', 'value' : url}) + } +} + +function onAnimGraphUrlChanged(url) { + console.debug('onAnimGraphUrlChanged: ', url); + if(currentAvatarSettings.animGraphUrl !== url) { + currentAvatarSettings.animGraphUrl = url; + sendToQml({'method' : 'settingChanged', 'name' : 'animGraphUrl', 'value' : url}) + } +} + var selectedAvatarEntityGrabbable = false; var selectedAvatarEntity = null; @@ -110,12 +161,16 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See switch (message.method) { case 'getAvatars': currentAvatar = getMyAvatar(); + currentAvatarSettings = getMyAvatarSettings(); + message.data = { 'bookmarks' : AvatarBookmarks.getBookmarks(), + 'displayName' : MyAvatar.displayName, 'currentAvatar' : currentAvatar, - 'displayName' : MyAvatar.displayName + 'currentAvatarSettings' : currentAvatarSettings }; + console.debug('avatarapp.js: currentAvatarSettings: ', JSON.stringify(message.data.currentAvatarSettings, null, '\t')) console.debug('avatarapp.js: currentAvatar: ', JSON.stringify(message.data.currentAvatar, null, '\t')) sendToQml(message) break; @@ -195,6 +250,19 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See AddressManager.handleLookupString(message.url, false); } break; + case 'saveSettings': + console.debug('avatarapp.js: saveSettings: ', JSON.stringify(message.settings, 0, 4)); + MyAvatar.setAvatarScale(message.avatarScale); + currentAvatar.avatarScale = message.avatarScale; + + MyAvatar.setDominantHand(message.settings.dominantHand); + MyAvatar.setCollisionsEnabled(message.settings.collisionsEnabled); + MyAvatar.collisionSoundURL = message.settings.collisionSoundUrl; + MyAvatar.setAnimGraphUrl(message.settings.animGraphUrl); + + settings = getMyAvatarSettings(); + console.debug('saveSettings: settings: ', JSON.stringify(settings, 0, 4)); + break; default: print('Unrecognized message from AvatarApp.qml:', JSON.stringify(message)); } @@ -339,12 +407,23 @@ function off() { AvatarBookmarks.bookmarkDeleted.disconnect(onBookmarkDeleted); AvatarBookmarks.bookmarkAdded.disconnect(onBookmarkAdded); + MyAvatar.dominantHandChanged.disconnect(onDominantHandChanged); + MyAvatar.collisionsEnabledChanged.disconnect(onCollisionsEnabledChanged); + MyAvatar.newCollisionSoundURL.disconnect(onNewCollisionSoundUrl); + MyAvatar.animGraphUrlChanged.disconnect(onAnimGraphUrlChanged); + MyAvatar.targetScaleChanged.disconnect(onTargetScaleChanged); } function on() { AvatarBookmarks.bookmarkLoaded.connect(onBookmarkLoaded); AvatarBookmarks.bookmarkDeleted.connect(onBookmarkDeleted); AvatarBookmarks.bookmarkAdded.connect(onBookmarkAdded); + + MyAvatar.dominantHandChanged.connect(onDominantHandChanged); + MyAvatar.collisionsEnabledChanged.connect(onCollisionsEnabledChanged); + MyAvatar.newCollisionSoundURL.connect(onNewCollisionSoundUrl); + MyAvatar.animGraphUrlChanged.connect(onAnimGraphUrlChanged); + MyAvatar.targetScaleChanged.connect(onTargetScaleChanged); } function tabletVisibilityChanged() {