implement adding bookmark

improve comparision of attachments/wearables
This commit is contained in:
Alexander Ivash 2018-05-14 00:48:52 +03:00
parent 18a3901d29
commit 10f2fe5fe6
6 changed files with 182 additions and 67 deletions

View file

@ -36,11 +36,11 @@ Rectangle {
property bool isAvatarInFavorites: currentAvatar ? allAvatars.findAvatar(currentAvatar.name) !== undefined : false
property int avatarWearablesCount: currentAvatar ? currentAvatar.wearables.count : 0
property var currentAvatar: null;
function setCurrentAvatar(avatar) {
function setCurrentAvatar(avatar, bookmarkName) {
var avatarThumbnailUrl = allAvatars.makeThumbnailUrl(avatar.avatarUrl);
var currentAvatarObject = {
'name' : '',
'name' : bookmarkName,
'scale' : avatar.avatarScale,
'url' : avatarThumbnailUrl,
'wearables' : avatar.avatarEntites ? avatar.avatarEntites : [],
@ -63,7 +63,7 @@ Rectangle {
} else if(message.method === 'wearableUpdated') {
adjustWearables.refreshWearable(message.entityID, message.wearableIndex, message.properties);
} else if(message.method === 'wearablesUpdated') {
var wearablesModel = currentAvatarModel.get(0).wearables;
var wearablesModel = currentAvatar.wearables;
console.debug('handling wearablesUpdated, new wearables count:', message.wearables.length, ': old wearables: ');
for(var i = 0; i < wearablesModel.count; ++i) {
@ -82,11 +82,13 @@ Rectangle {
adjustWearables.refresh(currentAvatar);
} else if(message.method === 'bookmarkLoaded') {
setCurrentAvatar(message.reply.currentAvatar);
selectedAvatarName = message.reply.name;
var avatarIndex = allAvatars.findAvatarIndex(selectedAvatarName);
setCurrentAvatar(message.reply.currentAvatar, message.reply.name);
var avatarIndex = allAvatars.findAvatarIndex(currentAvatar.name);
allAvatars.move(avatarIndex, 0, 1);
view.setPage(0);
} else if(message.method === 'bookmarkAdded') {
var avatarIndex = allAvatars.addAvatarEntry(message.bookmark, message.bookmarkName);
updateCurrentAvatarInBookmarks(currentAvatar);
} else if(message.method === 'bookmarkDeleted') {
pageOfAvatars.isUpdating = true;
@ -115,10 +117,12 @@ Rectangle {
} else if(message.method === getAvatarsMethod) {
var getAvatarsReply = message.reply;
allAvatars.populate(getAvatarsReply.bookmarks);
setCurrentAvatar(getAvatarsReply.currentAvatar);
setCurrentAvatar(getAvatarsReply.currentAvatar, '');
console.debug('currentAvatar: ', JSON.stringify(currentAvatar, null, '\t'));
updateCurrentAvatarInBookmarks(currentAvatar);
} else if(message.method === 'updateAvatarInBookmarks') {
updateCurrentAvatarInBookmarks(currentAvatar);
} else if(message.method === 'selectAvatarEntity') {
adjustWearables.selectWearableByID(message.entityID);
}
@ -130,7 +134,6 @@ Rectangle {
if(bookmarkAvatarIndex === -1) {
console.debug('bookmarkAvatarIndex = -1, avatar is not favorite')
avatar.name = '';
selectedAvatarName = '';
view.setPage(0);
} else {
console.debug('bookmarkAvatarIndex = ', bookmarkAvatarIndex, 'avatar is among favorites!')
@ -140,11 +143,6 @@ Rectangle {
}
}
property string selectedAvatarName: ''
onSelectedAvatarNameChanged: {
console.debug('selectedAvatarId: ', selectedAvatarName)
}
property bool isInManageState: false
Component.onCompleted: {
@ -217,9 +215,6 @@ Rectangle {
}
onAdjustWearablesClosed: {
emitSendToScript({'method' : 'adjustWearablesClosed', 'save' : status, 'avatarName' : avatarName});
if(status) {
updateCurrentAvatarInBookmarks(currentAvatar);
}
}
onWearableSelected: {
emitSendToScript({'method' : 'selectWearable', 'entityID' : id});
@ -356,14 +351,11 @@ Rectangle {
}
entry.avatarEntites = wearables;
currentAvatar.name = createFavorite.favoriteNameText;
console.debug('became: ', JSON.stringify(entry, 0, 4));
/*
var newAvatar = JSON.parse(JSON.stringify(currentAvatar));
newAvatar.name = createFavorite.favoriteNameText;
allAvatars.append(newAvatar);
view.selectAvatar(newAvatar);
*/
emitSendToScript({'method': 'addAvatar', 'name' : currentAvatar.name});
createFavorite.close();
}
@ -523,7 +515,11 @@ Rectangle {
id: view
anchors.fill: parent
interactive: false;
currentIndex: (selectedAvatarName !== '' && !pageOfAvatars.isUpdating) ? pageOfAvatars.findAvatarIndex(selectedAvatarName) : -1
currentIndex: currentAvatarIndexInBookmarksPage();
function currentAvatarIndexInBookmarksPage() {
return (currentAvatar && currentAvatar.name !== '' && !pageOfAvatars.isUpdating) ? pageOfAvatars.findAvatarIndex(currentAvatar.name) : -1;
}
property int horizontalSpacing: 18
property int verticalSpacing: 44
@ -549,9 +545,7 @@ Rectangle {
property int currentPage: 0;
onCurrentPageChanged: {
console.debug('currentPage: ', currentPage)
currentIndex = Qt.binding(function() {
return (selectedAvatarName !== '' && !pageOfAvatars.isUpdating) ? pageOfAvatars.findAvatar(selectedAvatarName) : -1
})
currentIndex = Qt.binding(currentAvatarIndexInBookmarksPage);
}
property bool hasNext: currentPage < (totalPages - 1)

View file

@ -15,8 +15,8 @@ Rectangle {
signal wearableSelected(var id);
signal wearableDeleted(string avatarName, var id);
signal adjustWearablesOpened();
signal adjustWearablesClosed(bool status);
signal adjustWearablesOpened(var avatarName);
signal adjustWearablesClosed(bool status, var avatarName);
property bool modified: false;
Component.onCompleted: {
@ -32,9 +32,10 @@ Rectangle {
property var wearablesModel;
function open(avatar) {
adjustWearablesOpened();
console.debug('AdjustWearables.qml: open: ', JSON.stringify(avatar, null, '\t'));
adjustWearablesOpened(avatar.name);
visible = true;
avatarName = avatar.name;
wearablesModel = avatar.wearables;
@ -103,7 +104,7 @@ Rectangle {
function close(status) {
visible = false;
adjustWearablesClosed(status);
adjustWearablesClosed(status, avatarName);
}
HifiConstants { id: hifi }

View file

@ -16,20 +16,35 @@ ListModel {
return avatarThumbnailUrl;
}
function makeAvatarEntry(avatar, avatarName) {
console.debug('makeAvatarEntry: ', avatarName, JSON.stringify(avatar));
var avatarThumbnailUrl = makeThumbnailUrl(avatar.avatarUrl);
return {
'name' : avatarName,
'scale' : avatar.avatarScale,
'url' : avatarThumbnailUrl,
'wearables' : avatar.avatarEntites ? avatar.avatarEntites : [],
'attachments' : avatar.attachments ? avatar.attachments : [],
'entry' : avatar,
'getMoreAvatars' : false
};
}
function addAvatarEntry(avatar, avatarName) {
console.debug('addAvatarEntry: ', avatarName);
var avatarEntry = makeAvatarEntry(avatar, avatarName);
append(avatarEntry);
return allAvatars.count - 1;
}
function populate(bookmarks) {
for(var avatarName in bookmarks) {
var avatar = bookmarks[avatarName];
var avatarThumbnailUrl = makeThumbnailUrl(avatar.avatarUrl);
var avatarEntry = {
'name' : avatarName,
'scale' : avatar.avatarScale,
'url' : avatarThumbnailUrl,
'wearables' : avatar.avatarEntites ? avatar.avatarEntites : [],
'attachments' : avatar.attachments ? avatar.attachments : [],
'entry' : avatar,
'getMoreAvatars' : false
};
var avatarEntry = makeAvatarEntry(avatar, avatarName);
append(avatarEntry);
}
@ -62,11 +77,20 @@ ListModel {
for(var i = 0; i < m1.count; ++i) {
var e1 = m1.get(i);
var e2 = m2.get(i);
console.debug('comparing ', JSON.stringify(e1), JSON.stringify(e2));
var allDifferent = true;
if(!comparer(e1, e2)) {
// it turns out order of wearables can randomly change so make position-independent comparison here
for(var j = 0; j < m2.count; ++j) {
var e2 = m2.get(j);
if(comparer(e1, e2)) {
allDifferent = false;
break;
}
}
if(allDifferent) {
return false;
}
}
@ -74,10 +98,47 @@ ListModel {
return true;
}
function compareArrays(a1, a2, props) {
for(var prop in props) {
if(JSON.stringify(a1[prop]) !== JSON.stringify(a2[prop])) {
function compareNumericObjects(o1, o2) {
if(o1 === undefined && o2 !== undefined)
return false;
if(o1 !== undefined && o2 === undefined)
return false;
for(var prop in o1) {
var v1 = o1[prop];
var v2 = o2[prop];
if(v1 !== v2 && Math.round(v1 * 1000) != Math.round(v2 * 1000))
return false;
}
return true;
}
function compareObjects(o1, o2, props, arrayProp) {
console.debug('compare ojects: o1 = ', JSON.stringify(o1, null, 4), 'o2 = ', JSON.stringify(o2, null, 4));
for(var i = 0; i < props.length; ++i) {
var prop = props[i];
var propertyName = prop.propertyName;
var comparer = prop.comparer;
var o1Value = arrayProp ? o1[arrayProp][propertyName] : o1[propertyName];
var o2Value = arrayProp ? o2[arrayProp][propertyName] : o2[propertyName];
console.debug('compare values for key: ', propertyName, comparer ? 'using specified comparer' : 'using default comparer', ', o1 = ', JSON.stringify(o1Value, null, 4), 'o2 = ', JSON.stringify(o2Value, null, 4));
if(comparer) {
if(comparer(o1Value, o2Value) === false) {
console.debug('not equal');
return false;
} else {
console.debug('equal');
}
} else {
if(JSON.stringify(o1Value) !== JSON.stringify(o2Value)) {
return false;
}
}
}
@ -85,11 +146,22 @@ ListModel {
}
function compareWearables(w1, w2) {
return compareArrays(w1, w2, ['modelUrl', 'parentJointIndex', 'marketplaceID', 'itemName', 'script', 'rotation'])
return compareObjects(w1, w2, [{'propertyName' : 'modelURL'},
{'propertyName' : 'parentJointIndex'},
{'propertyName' : 'marketplaceID'},
{'propertyName' : 'itemName'},
{'propertyName' : 'script'},
{'propertyName' : 'rotation', 'comparer' : compareNumericObjects},
{'propertyName' : 'localPosition', 'comparer' : compareNumericObjects},
{'propertyName' : 'localRotation', 'comparer' : compareNumericObjects},
{'propertyName' : 'dimensions', 'comparer' : compareNumericObjects}], 'properties')
}
function compareAttachments(a1, a2) {
return compareAttachments(a1, a2, ['position', 'orientation', 'parentJointIndex', 'modelurl'])
return compareObjects(a1, a2, [{'propertyName' : 'position', 'comparer' : compareNumericObjects},
{'propertyName' : 'orientation'},
{'propertyName' : 'parentJointIndex'},
{'propertyName' : 'modelurl'}])
}
function findAvatarIndexByValue(avatar) {

View file

@ -108,6 +108,8 @@ void AvatarBookmarks::addBookmark(const QString& bookmarkName) {
addBookmarkToMenu(menubar, bookmarkName, bookmark);
insert(bookmarkName, bookmark);
enableMenuItems(true);
emit bookmarkAdded(bookmarkName);
}
void AvatarBookmarks::saveBookmark(const QString& bookmarkName) {
@ -212,6 +214,25 @@ void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
QVariantMap AvatarBookmarks::getBookmark(const QString &bookmarkName)
{
if (QThread::currentThread() != thread()) {
QVariantMap result;
BLOCKING_INVOKE_METHOD(this, "getBookmark", Q_RETURN_ARG(QVariantMap, result), Q_ARG(QString, bookmarkName));
return result;
}
QVariantMap bookmark;
auto bookmarkEntry = _bookmarks.find(bookmarkName);
if (bookmarkEntry != _bookmarks.end()) {
bookmark = bookmarkEntry.value().toMap();
}
return bookmark;
}
void AvatarBookmarks::changeToBookmarkedAvatar() {
QAction* action = qobject_cast<QAction*>(sender());
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();

View file

@ -31,7 +31,7 @@ class AvatarBookmarks: public Bookmarks, public Dependency {
public:
AvatarBookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
Q_INVOKABLE QVariantMap getBookmark(const QString& bookmarkName);
public slots:
/**jsdoc
@ -49,6 +49,7 @@ public slots:
signals:
void bookmarkLoaded(const QString& bookmarkName);
void bookmarkDeleted(const QString& bookmarkName);
void bookmarkAdded(const QString& bookmarkName);
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;

View file

@ -25,6 +25,10 @@ var ENTRY_AVATAR_ENTITIES = "avatarEntites";
var ENTRY_AVATAR_SCALE = "avatarScale";
var ENTRY_VERSION = "version";
function executeLater(callback) {
Script.setTimeout(callback, 300);
}
function getMyAvatarWearables() {
var wearablesArray = MyAvatar.getAvatarEntitiesVariant();
@ -70,8 +74,17 @@ function getMyAvatar() {
return avatar;
}
function updateAvatarWearables(avatar, wearables) {
avatar[ENTRY_AVATAR_ENTITIES] = wearables;
function updateAvatarWearables(avatar, bookmarkAvatarName) {
console.debug('avatarapp.js: scheduling wearablesUpdated notify for', bookmarkAvatarName);
executeLater(function() {
console.debug('avatarapp.js: executing wearablesUpdated notify for', bookmarkAvatarName);
var wearables = getMyAvatarWearables();
avatar[ENTRY_AVATAR_ENTITIES] = wearables;
sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables, 'avatarName' : bookmarkAvatarName})
});
}
var adjustWearables = {
@ -132,19 +145,30 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
console.debug('avatarapp.js: deleting avatar: ', message.name);
AvatarBookmarks.removeBookmark(message.name);
break;
case 'addAvatar':
console.debug('avatarapp.js: saving avatar: ', message.name);
AvatarBookmarks.addBookmark(message.name);
break;
case 'adjustWearablesOpened':
console.debug('avatarapp.js: adjustWearablesOpened');
currentAvatarWearablesBackup = getMyAvatarWearables();
adjustWearables.setOpened(true);
Entities.mousePressOnEntity.connect(onSelectedEntity);
break;
case 'adjustWearablesClosed':
console.debug('avatarapp.js: adjustWearablesClosed');
if(!message.save) {
// revert changes using snapshot of wearables
console.debug('reverting... ');
if(currentAvatarWearablesBackup !== null) {
AvatarBookmarks.updateAvatarEntities(currentAvatarWearablesBackup);
sendToQml({'method' : 'wearablesUpdated', 'wearables' : getMyAvatarWearables(), 'avatarName' : message.avatarName});
updateAvatarWearables(currentAvatar, message.avatarName);
}
} else {
console.debug('saving... ');
sendToQml({'method' : 'updateAvatarInBookmarks'});
}
adjustWearables.setOpened(false);
@ -155,18 +179,9 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
ensureWearableSelected(message.entityID);
break;
case 'deleteWearable':
console.debug('before deleting: wearables.length: ', getMyAvatarWearables().length);
console.debug(JSON.stringify(Entities.getEntityProperties(message.entityID, ['parentID'])));
Entities.editEntity(message.entityID, { parentID : null }) // 2do: remove this hack when backend will be fixed
console.debug(JSON.stringify(Entities.getEntityProperties(message.entityID, ['parentID'])));
console.debug('avatarapp.js: deleteWearable: ', message.entityID, 'from avatar: ', message.avatarName);
Entities.deleteEntity(message.entityID);
var wearables = getMyAvatarWearables();
console.debug('after deleting: wearables.length: ', wearables.length);
updateAvatarWearables(currentAvatar, wearables);
sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables, 'avatarName' : message.avatarName})
updateAvatarWearables(currentAvatar, message.avatarName);
break;
default:
print('Unrecognized message from AvatarApp.qml:', JSON.stringify(message));
@ -253,9 +268,13 @@ function sendToQml(message) {
}
function onBookmarkLoaded(bookmarkName) {
currentAvatar = getMyAvatar();
console.debug('avatarapp.js: onBookmarkLoaded: ', JSON.stringify(currentAvatar, 0, 4));
sendToQml({'method' : 'bookmarkLoaded', 'reply' : {'name' : bookmarkName, 'currentAvatar' : currentAvatar} });
console.debug('avatarapp.js: scheduling onBookmarkLoaded: ', bookmarkName);
executeLater(function() {
currentAvatar = getMyAvatar();
console.debug('avatarapp.js: executing onBookmarkLoaded: ', JSON.stringify(currentAvatar, 0, 4));
sendToQml({'method' : 'bookmarkLoaded', 'reply' : {'name' : bookmarkName, 'currentAvatar' : currentAvatar} });
});
}
function onBookmarkDeleted(bookmarkName) {
@ -263,6 +282,11 @@ function onBookmarkDeleted(bookmarkName) {
sendToQml({'method' : 'bookmarkDeleted', 'name' : bookmarkName});
}
function onBookmarkAdded(bookmarkName) {
console.debug('avatarapp.js: onBookmarkAdded: ', bookmarkName);
sendToQml({ 'method': 'bookmarkAdded', 'bookmarkName': bookmarkName, 'bookmark': AvatarBookmarks.getBookmark(bookmarkName) });
}
//
// Manage the connection between the button and the window.
//
@ -282,6 +306,7 @@ function startup() {
tablet.screenChanged.connect(onTabletScreenChanged);
AvatarBookmarks.bookmarkLoaded.connect(onBookmarkLoaded);
AvatarBookmarks.bookmarkDeleted.connect(onBookmarkDeleted);
AvatarBookmarks.bookmarkAdded.connect(onBookmarkAdded);
// Window.domainChanged.connect(clearLocalQMLDataAndClosePAL);
// Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL);
@ -384,6 +409,7 @@ function shutdown() {
tablet.screenChanged.disconnect(onTabletScreenChanged);
AvatarBookmarks.bookmarkLoaded.disconnect(onBookmarkLoaded);
AvatarBookmarks.bookmarkDeleted.disconnect(onBookmarkDeleted);
AvatarBookmarks.bookmarkAdded.disconnect(onBookmarkAdded);
// Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL);
// Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL);