777
interface/resources/avatar/bookmarks/avatarbookmarks.json
Normal file
|
@ -0,0 +1,777 @@
|
|||
{
|
||||
"Anime boy": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 6.915350914001465,
|
||||
"ageAsText": "0 hours 0 minutes 6 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.10961885005235672,
|
||||
"y": -0.19444090127944946,
|
||||
"z": -0.15760529041290283
|
||||
},
|
||||
"center": {
|
||||
"x": 2.6226043701171875e-06,
|
||||
"y": -0.13999652862548828,
|
||||
"z": -0.04999971389770508
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.21924294531345367,
|
||||
"y": 0.10888873785734177,
|
||||
"z": 0.2152111530303955
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.10962409526109695,
|
||||
"y": -0.0855521634221077,
|
||||
"z": 0.057605862617492676
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:27:53Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.21924294531345367,
|
||||
"y": 0.07768379896879196,
|
||||
"z": 0.2055898904800415
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 15,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{5d20c775-a0d7-4163-b158-4e0a784a4625}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "Wear these, and others will respect your authoritah.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Aviators",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306178314655,
|
||||
"lastEditedBy": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": 2.6226043701171875e-06,
|
||||
"y": -0.13999652862548828,
|
||||
"z": -0.04999971389770508
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9969173073768616,
|
||||
"x": -0.07845909893512726,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "40d879ec-93f0-4b4a-8c58-dd6349bdb058",
|
||||
"modelURL": "http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"x": 0.1660931408405304,
|
||||
"y": 0.05885136127471924,
|
||||
"z": 0.15574991703033447
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0,
|
||||
"y": 1.6633577346801758,
|
||||
"z": 0.048884183168411255
|
||||
},
|
||||
"originalTextures": "{\n \"aviator:Eyewear2F\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Diffuse.png\",\n \"aviator:Eyewear2F1\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Specular.png\"\n}\n",
|
||||
"owningAvatarID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
|
||||
"parentID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
|
||||
"parentJointIndex": 66,
|
||||
"position": {
|
||||
"x": 2.6226043701171875e-06,
|
||||
"y": -0.13999652862548828,
|
||||
"z": -0.04999971389770508
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.9313028454780579,
|
||||
"x": -1.4091639518737793,
|
||||
"y": -10.133878707885742,
|
||||
"z": 1.9983724355697632
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 1310720,
|
||||
"verticesCount": 982
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9969173073768616,
|
||||
"x": -0.07845909893512726,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Anime girl": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 19.66267967224121,
|
||||
"ageAsText": "0 hours 0 minutes 19 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.10536206513643265,
|
||||
"y": -0.16647332906723022,
|
||||
"z": -0.12632352113723755
|
||||
},
|
||||
"center": {
|
||||
"x": 0,
|
||||
"y": -0.12999999523162842,
|
||||
"z": -0.030000001192092896
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.2107241302728653,
|
||||
"y": 0.07294666767120361,
|
||||
"z": 0.1926470398902893
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.10536206513643265,
|
||||
"y": -0.09352666139602661,
|
||||
"z": 0.06632351875305176
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-05T00:10:37Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.2107241302728653,
|
||||
"y": 0.07294666767120361,
|
||||
"z": 0.1926470398902893
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 5,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{1586b83a-2af7-4532-9bfb-82fe3f5d5ce9}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "moam_00",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "Perfect for side-glancin'.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Blacker Fem Glasses",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528157470041658,
|
||||
"lastEditedBy": "{425df1a8-289b-42fc-819c-c3b2a12d7165}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": 0,
|
||||
"y": -0.12999999523162842,
|
||||
"z": -0.029999999329447746
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 1,
|
||||
"x": -2.2351741790771484e-08,
|
||||
"y": 3.4924596548080444e-10,
|
||||
"z": 3.725290298461914e-09
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "06781d12-9139-48f4-ac2a-417dde090981",
|
||||
"modelURL": "http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx",
|
||||
"name": "Female Glasses 3 by Mario Andrade",
|
||||
"naturalDimensions": {
|
||||
"x": 0.16209548711776733,
|
||||
"y": 0.05611282214522362,
|
||||
"z": 0.14819003641605377
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0,
|
||||
"y": -7.636845111846924e-08,
|
||||
"z": 0
|
||||
},
|
||||
"originalTextures": "{\n \"file49\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Mixed_AO.jpg\",\n \"file81\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Metallic.jpg\",\n \"file84\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Roughness.jpg\",\n \"file86\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Base_Color.jpg\",\n \"file87\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Normal_DirectX.jpg\"\n}\n",
|
||||
"owningAvatarID": "{1277f725-fbb4-478b-ae79-1241fd90e508}",
|
||||
"parentID": "{1277f725-fbb4-478b-ae79-1241fd90e508}",
|
||||
"parentJointIndex": 66,
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": -0.12999999523162842,
|
||||
"z": -0.029999999329447746
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.8840523958206177,
|
||||
"x": -2.6587564945220947,
|
||||
"y": -10.162277221679688,
|
||||
"z": -0.9548344016075134
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 5,
|
||||
"texturesSize": 0,
|
||||
"verticesCount": 1156
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -2.2351741790771484e-08,
|
||||
"y": 3.4924596548080444e-10,
|
||||
"z": 3.725290298461914e-09
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Last Legends: Male": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011327743530273,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.20154684782028198,
|
||||
"y": 0.03644842654466629,
|
||||
"z": -0.2641940414905548
|
||||
},
|
||||
"center": {
|
||||
"x": -0.030000001192092896,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.3430936932563782,
|
||||
"y": 0.18709957599639893,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.1415468454360962,
|
||||
"y": 0.22354799509048462,
|
||||
"z": 0.12419357895851135
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.33466479182243347,
|
||||
"y": 0.16981728374958038,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 19,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{6b0a2b08-e8e3-4d43-95cc-dfc4f7a4b0c9}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Fedora",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032827319,
|
||||
"lastEditedBy": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
|
||||
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"x": 0.2765824794769287,
|
||||
"y": 0.14034485816955566,
|
||||
"z": 0.320981502532959
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0.000143393874168396,
|
||||
"y": 1.7460365295410156,
|
||||
"z": 0.022502630949020386
|
||||
},
|
||||
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentJointIndex": 64,
|
||||
"position": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.6202316284179688,
|
||||
"x": -0.5601736903190613,
|
||||
"y": -10.668098449707031,
|
||||
"z": -0.8933582305908203
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 327680,
|
||||
"verticesCount": 719
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011027336120605,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.04381517320871353,
|
||||
"y": 0.20789726078510284,
|
||||
"z": -0.0394962802529335
|
||||
},
|
||||
"center": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.08762653172016144,
|
||||
"y": 0.04429228603839874,
|
||||
"z": 0.07901687920093536
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.043811358511447906,
|
||||
"y": 0.2521895468235016,
|
||||
"z": 0.03952059894800186
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.03022606298327446,
|
||||
"y": 0.06644226610660553,
|
||||
"z": 0.07229919731616974
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 58,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{d018c6ea-b2f4-441e-85e1-d17373ae6f34}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A cool scifi watch for your avatar!",
|
||||
"itemLicense": "",
|
||||
"itemName": "Scifi Watch",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032505220,
|
||||
"lastEditedBy": "{b46f9c9e-4cd3-4964-96d6-cf3954abb908}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90",
|
||||
"modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx",
|
||||
"name": "Scifi Watch by Jimi",
|
||||
"naturalDimensions": {
|
||||
"x": 0.023250818252563477,
|
||||
"y": 0.0511094331741333,
|
||||
"z": 0.055614765733480453
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0.6493338942527771,
|
||||
"y": 1.4500460624694824,
|
||||
"z": -0.06031447649002075
|
||||
},
|
||||
"originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentJointIndex": 16,
|
||||
"position": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.3082179129123688,
|
||||
"x": -0.19203892350196838,
|
||||
"y": -10.429610252380371,
|
||||
"z": -0.4076632857322693
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 5,
|
||||
"texturesSize": 786432,
|
||||
"verticesCount": 273
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Last legends Female": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/8d823be5-6197-4418-b984-eb94160ed956-v1/LLFemale_Clothes.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Matthew": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Priscilla": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst",
|
||||
"version": 3
|
||||
},
|
||||
"Woody": {
|
||||
"attachments": [
|
||||
],
|
||||
"avatarEntites": [
|
||||
],
|
||||
"avatarScale": 1,
|
||||
"avatarUrl": "http://mpassets.highfidelity.com/ad348528-de38-420c-82bb-054cb22163f5-v1/mannequin.fst",
|
||||
"version": 3
|
||||
}
|
||||
}
|
|
@ -35,8 +35,6 @@
|
|||
<glyph glyph-name="diclosure-expand" unicode="B" d="M239 187c-3 0-6 1-8 3-5 5-5 12 0 17l42 42-43 44c-5 4-5 12 0 17 4 4 12 4 17 0l60-61-60-59c-2-2-5-3-8-3z"/>
|
||||
<glyph glyph-name="reload-small" unicode="a" d="M334 253c-9 0-18-7-18-16-3-27-25-47-52-47-29 0-52 24-52 52 0 11 2 27 14 38 6 5 14 9 24 12-5-7-4-16 2-22 3-3 8-4 12-4 5 0 9 1 13 5l28 28c1 2 3 4 3 7 3 6 1 13-3 18l-29 29c-3 3-7 5-12 5-5 0-9-2-12-5-4-3-5-8-5-12 0-5 1-9 5-13l0 0c-20-3-37-11-50-23-16-16-25-38-25-63 0-47 39-86 86-86 45 0 82 33 87 78 1 9-6 18-16 19z"/>
|
||||
<glyph glyph-name="close-small" unicode="C" d="M291 259l43 44c8 7 8 19 0 27-7 7-19 7-26 0l-44-44-44 44c-7 7-19 7-26 0-8-8-8-20 0-27l43-44-43-43c-8-8-8-20 0-27 7-7 19-7 26 0l44 44 44-44c7-7 19-7 26 0 8 8 8 19 0 27z"/>
|
||||
<glyph glyph-name="backward" unicode="E" d="M292 349c-5 3-12 3-18-1l-94-71c-4-3-7-8-7-13 0-5 2-10 6-14l95-80c3-2 7-4 11-4 2 0 5 1 7 2 6 3 10 9 10 15l0 151c0 6-4 12-10 15"/>
|
||||
<glyph glyph-name="reload" unicode="F" d="M365 261c-9 1-17-5-18-15-4-45-43-80-89-80-49 0-89 40-89 89 0 19 4 45 25 65 16 15 39 23 68 25l-15-16c-6-6-6-17 0-24 4-3 8-4 12-4 4 0 9 1 12 5l43 44c2 2 3 4 4 6 2 6 1 13-4 18l-44 44c-6 6-17 6-23 0-7-7-7-17 0-24l15-15c-38-2-69-14-91-35-23-21-36-53-36-88 0-68 55-123 123-123 64 0 116 47 122 110 1 9-5 18-15 18"/>
|
||||
<glyph glyph-name="minimize" unicode="I" d="M154 282l198 0c10 0 18-8 18-18 0-10-8-18-18-18l-198 0c-10 0-18 8-18 18 0 10 8 18 18 18"/>
|
||||
<glyph glyph-name="maximize" unicode="J" d="M157 244l77 0 0-75c0-9 8-17 17-17 9 0 17 8 17 17l0 75 75 0c10 0 17 8 17 17 0 10-7 18-17 18l-75 0 0 76c0 10-8 17-17 17-9 0-17-7-17-17l0-76-77 0c-10 0-17-8-17-18 0-9 8-17 17-17z"/>
|
||||
<glyph glyph-name="maximize-inverted" unicode="K" d="M251 434c-96 0-173-78-173-173 0-96 77-173 173-173 95 0 173 77 173 173 0 95-78 173-173 173z m93-190l-77 0 0-76c0-10-7-17-16-17-9 0-16 7-16 17l0 76-77 0c-10 0-17 8-17 17 0 9 7 17 17 17l77 0 0 76c0 9 7 17 16 17 9 0 16-8 16-17l0-76 77 0c9 0 17-8 17-17 0-9-8-17-17-17z"/>
|
||||
|
@ -48,7 +46,6 @@
|
|||
<glyph glyph-name="script-new" unicode="Q" d="M298 80l-145 0c-30 0-55 15-72 43-12 20-16 40-16 41l-3 16 267 0 0-12c0-1 1-15 9-29 10-18 26-27 49-27 13 0 22 4 29 12 16 18 16 54 14 66l0 1 0 206-269 0c-7 0-13 6-13 13 0 7 6 13 13 13l295 0 0-230c1-8 5-57-21-86-12-14-28-21-48-21-41 0-62 23-72 41-5 10-8 19-10 28l-210 0c2-7 5-12 8-18 13-20 29-30 50-30l145 0c7 0 13-7 13-14 0-7-6-13-13-13z m95 260l-180 0c-7 0-14 6-14 14 0 8 7 14 14 14l180 0c7 0 14-6 14-14 0-8-7-14-14-14z m0-59l-135 0c-8 0-14 7-14 15 0 7 6 14 14 14l135 0c7 0 14-7 14-14 0-8-7-15-14-15z m0-58l-180 0c-7 0-14 7-14 14 0 8 7 15 14 15l180 0c7 0 14-7 14-15 0-7-7-14-14-14z m-250 90l0 53c0 9-7 16-16 16-9 0-16-7-16-16l0-53-54 0c-9 0-16-8-16-17 0-9 8-17 16-17l54 0 0-53c0-9 7-16 16-16 9 0 16 8 16 17l0 52 54 0c9 0 16 8 16 17 0 9-8 16-17 16z"/>
|
||||
<glyph glyph-name="hifi-forum" unicode="2" d="M265 410c-83 0-150-68-150-150 0-24 6-47 16-67l-27-79 80 20c23-16 51-25 81-25 83 0 150 68 150 150 0 83-67 151-150 151z m38-248c-9 0-17 7-17 17 0 7 4 14 12 16l0 46-74 33 0-56c7-2 12-8 12-16 0-10-7-18-17-18-10 0-19 8-19 18 0 7 6 13 10 16l0 111c-4 2-10 9-10 16 0 10 9 17 19 17 9 0 17-7 17-17 0-8-5-14-12-16l0-41 74-33 0 51c-8 3-12 9-12 16 0 10 7 18 17 18 10 0 17-8 17-18 0-7-5-14-12-16l0-110c7-3 12-9 12-17 0-10-7-17-17-17z"/>
|
||||
<glyph glyph-name="hifi-logo-small" unicode="S" d="M374 374c-32 32-74 49-119 49-46 0-88-17-120-49-32-32-49-74-49-119 0-45 17-87 49-118 32-32 74-49 119-49 45 0 88 17 119 49 32 32 49 73 49 118 1 45-17 87-48 119z m-17-221c-28-28-65-43-103-43-39 0-75 15-103 43-27 27-42 64-42 102 0 39 15 75 42 103 28 28 64 43 103 43 38 0 75-15 103-43 27-28 42-64 42-103 0-39-15-76-42-102z m-145 47c-5 0-9 3-9 6l0 126c0 3 4 7 9 7 6 0 10-4 10-7l0-125c0-4-4-7-10-7z m0 118c-5 0-10 2-14 6-7 7-7 20 0 27 5 5 9 6 14 6 6 0 11-2 14-6 4-4 5-8 5-14 0-5-2-10-5-13-4-4-8-6-14-6z m0-144c-5 0-10 2-14 5-4 5-5 9-5 14 0 6 2 11 5 14 5 4 9 5 14 5 6 0 11-2 14-5 4-4 5-8 5-14 0-5-2-10-5-14-4-3-8-5-14-5z m85 2c-6 0-10 3-10 7l0 121c0 4 4 7 10 7 5 0 9-3 9-7l0-121c0-5-4-7-9-7z m0 120c-6 0-11 2-14 5-8 8-8 20 0 28 4 4 8 5 14 5 5 0 10-2 14-5 4-4 5-9 5-14 0-5-2-11-5-14-4-3-9-5-14-5z m0-144c-6 0-11 2-14 5-8 7-8 20 0 28 4 4 8 5 14 5 5 0 10-2 14-5 4-5 5-9 5-14 0-5-2-11-5-14-4-3-9-5-14-5z m1 73l-85 40 1 18 86-40z"/>
|
||||
<glyph glyph-name="avatar-1" unicode="T" d="M293 71c-1 0-1 0-1 0-14-1-14-1-16 12-6 43-11 86-16 128-1 2-1 4-1 6-3 0-6 0-9 0-2-10-3-20-4-31-4-36-8-72-12-109-1-7-2-8-9-8-2 0-5 0-7 0 0 74 1 181 1 254-1-1-33 0-44 1-15 0-62 1-79 1-8 0-14 3-18 10-1 2-2 4-4 7 8 0 15 0 22 1 35 1 99 8 100 12 14 11 23 10 36 10 15 0 31 0 46-1 11 0 24 3 37-10 20-10 81-10 123-11 0 0 1 0 1 0-4-12-12-17-25-17-29-1-77-3-127-1 2-73 4-181 6-254z m-32 371c16-6 14-20 13-32 0-5-1-10-1-14-2-11-10-18-20-18-11 0-19 8-20 18-1 6-1 13-2 20-1 7 3 13 11 15 10 3 10 3 19 11z"/>
|
||||
<glyph glyph-name="placemark" unicode="U" d="M134 98c31-32 73-49 119-49l1 0c45 0 86 16 117 47 31 30 48 71 48 114 1 46-16 88-46 120-9 9-20 17-31 24-3-7-6-15-10-22 5-4 11-8 16-13 15-14 28-32 33-46l1-1c0-1 0-2-1-3l-1 0c-12-7-25-10-38-12-2-1-4-1-7-2l-1 0c0 0-1 0-2 1 0 0-1 1-1 1l0 1c-3 15-6 31-12 46l-7-15c4-11 6-21 8-32l0-1c0-1 0-1 0-2-1-1-1-1-2-1l-1 0-55-3-4 0c0 0-1 0-1 0-1 1-1 2-1 2l0 74c-5 9-8 17-11 26 0-1 0-1 0-1l-1 0 0-97c0 0 0-1 0-2 0-1-1-2-2-2l-1 0c0 0-1 0-1 0l-51 3c-1 0-2 0-2 1-1 1-1 2 0 2l0 1c5 27 13 58 35 83 6 6 12 13 20 16 0 0 1 0 1 0-3 7-6 15-9 22-37-4-71-20-99-47-30-30-47-70-48-113-1-46 16-88 47-120z m198 72c0 4 1 7 1 10l0 0c0 7 1 14 1 21 0 9 0 18 0 27 0 3 0 6 0 9l0 1c-1 1-1 6 5 7 11 3 23 5 34 8l1 0c3 1 7 2 11 2l1 1c1 0 3-1 3-2l0-1c0-1 1-1 1-2 1-3 1-5 2-8 3-14 4-27 3-40-2-12-8-21-18-25-8-3-17-5-25-8-3 0-7-1-10-2-1-1-3-1-4-1-1 0-1 0-2 0l-1-1c0 0 0 0 0 0-1 0-2 1-2 1-1 1-1 1-1 2z m2-17c12 4 23 7 35 10l14 4c1 0 2 0 2-1 1 0 1-2 1-2l-1-2c-7-18-17-34-30-47-13-13-28-24-46-31l-2-1c0 0-1 0-1 0-1 0-1 0-2 0 0 1-1 2-1 3l1 1c0 1 0 1 0 1 0 1 0 2 1 3 6 9 15 24 22 55 1 3 2 6 7 7z m-78 83c0 1 1 3 2 3l3 0 41 2 13 1c0 0 1 0 1 0l1 0c0 0 0-1 1-1l1 0c1 0 2-1 2-3 0-1 0-3 0-5 0-2 0-4 0-6l0-5c1-7 1-15 0-22l0-4c0-5 0-11-1-17 0-1 0-3 0-5 0-1 0-3 0-4 0-2-1-7-7-8-10 0-20-1-30-2l-2 0c-3-1-8-1-12-1-5-1-7-1-8-1-1 0-1 0-2 0l-2 1c-1 1-1 2-1 3l0 74z m0-92c0 1 1 3 2 3l3 0 51 3c1 0 2 0 2-1 1 0 1-1 1-2l0-1c0 0 0-1 0-1 0 0 0 0-1-1 0 0 0-1 0-1l0-1c0-1-1-3-1-4-1-2-2-5-3-7l-1-3c-3-9-7-18-11-27-4-8-9-14-14-19-5-6-13-9-23-10l-3 0c-1 0-1 0-2 1 0 0 0 1 0 2l0 69z m-52-59c-1-1-1-2-2-2 0 0-1 1-1 1l-2 1c-27 6-64 40-78 79l-1 1c0 1 0 2 1 3 1 1 2 1 3 1l13-4c12-4 24-7 36-11 3-1 6-2 7-7 5-24 12-42 23-57l1-2c1-1 1-3 0-3z m40-10c0-1-1-1-1-2-1 0-2 0-2 0 0 0-1 0-1 0-1 0-14 4-20 11-15 19-23 41-29 61 0 0 0 0 0 1l0 1c0 1 0 1 1 2 0 1 1 1 2 1l1 0 29-2 17-1c2-1 3-2 3-3l0-69z m-21 165l18-2c2 0 3-1 3-2l0-75c0-1-1-2-1-2-1-1-2-1-2-1 0 0 0 0-1 0 0 0-14 2-21 2-8 1-17 2-26 3-4 0-6 4-6 7-2 18-3 35-3 47 0 5-1 22-1 22 0 1 1 2 1 2 1 1 1 1 2 1z m-98 32l1 1c12 31 42 59 76 71l3 1c1 0 2 0 3-1 0-1 0-2 0-3l-2-3c-18-25-25-53-31-79l0-1c0-1 0-2-1-2 0 0-1-1-1-1-1 0-1 0-1 0l-1 1c-5 1-11 3-17 4-8 2-18 4-27 8l0 1c-2 0-2 2-2 3z m-8-18l1 1c0 1 1 2 3 2l49-13c1 0 2-1 2-3l2-71c0-1 0-2-1-2-1-1-1-1-2-1 0 0 0 0 0 0l-1 0c-1 0-2 1-3 1l0 0c-2 0-4 1-7 1-16 4-29 9-39 17-6 4-9 8-9 15-1 19 1 37 5 53z m178 42c-8 16-14 31-21 46-8 17-17 35-24 52-14 31 3 65 36 71 28 5 56-16 59-44 0-9-1-18-5-26-14-32-29-64-44-97 0 0-1-1-1-2z m27 117c0 15-12 28-27 28-15 0-27-12-28-27 0-16 13-28 28-28 15 0 27 12 27 27z"/>
|
||||
<glyph glyph-name="box" unicode="V" d="M318 74l126 89 15-22-126-88z m-137 101l0-99 27 0 0 96z m145-125c-1 0-1 0-2 0l-262 26c-7 1-12 7-12 13l0 263c0 4 1 7 4 10 3 2 7 4 10 3l263-26c7 0 12-6 12-13l0-262c0-4-2-8-4-10-3-3-6-4-9-4z m-250 51l236-23 0 236-236 23z m377 326l-263 26c-3 1-7-1-10-3-3-3-4-6-4-10l0-21c3 2 7 3 11 3 0 0 0 0 1 0 3 2 7 4 11 4 2 0 3 0 5-1l234-23 0-236c0-7-3-10-10-14 1-2 1-5 1-7 1-2 0-3 0-5l21-2c1 0 1 0 2 0 3 0 6 1 8 4 3 2 5 6 5 10l0 262c0 7-5 13-12 13z m-397-64l125 88 16-22-126-88z m262-26l126 88 15-22-126-88z m-256-123l2 27 263-26-3-26z m146 37l0 91-27 0 0-88c9-1 18-2 27-3z"/>
|
||||
<glyph glyph-name="community" unicode="0" d="M50 175c-4 0-8 2-11 6-4 6-2 14 4 18l24 16 69 48 89-64c6-4 7-13 3-19-5-6-13-7-19-3l-74 53-53-37-24-16c-3-1-5-2-8-2z m130-10l-44 32-47-32-22-14 0-63 135 0 0 60z m120 10c-4 0-9 2-11 6-4 6-3 14 3 18l25 16 68 48 89-64c6-4 7-13 3-19-4-6-13-7-19-3l-73 53-54-37-24-16c-2-1-5-2-7-2z m129-10l-46 32-45-32-22-14 0-63 135 0 0 60z m-256 202c-4 0-9 2-11 5-4 7-3 15 4 19l24 16 68 48 89-65c6-4 7-12 3-18-4-6-12-7-18-3l-74 53-53-37-25-16c-2-2-5-2-7-2z m129-11l-46 32-45-31-22-15 0-62 135 0 0 60z"/>
|
||||
|
@ -103,7 +100,6 @@
|
|||
<glyph glyph-name="lock" unicode="" d="M389 233l0 62c0 68-55 124-123 124-69 0-124-56-124-124l0-62c-24-4-44-26-44-52l0-74c0-29 24-52 52-52l230 0c29 0 53 23 53 52l0 74c0 26-18 48-44 52z m-123 129c37 0 67-30 67-67l0-61-135 0 0 61c0 37 31 67 68 67z"/>
|
||||
<glyph glyph-name="visible" unicode="" d="M258 116c-55 0-106 17-147 51-31 25-47 51-47 52-4 7-4 16 1 23 2 4 66 98 195 96 133-3 192-93 195-97 4-6 4-15 0-22 0-1-15-27-46-53-29-23-79-50-151-50 0 0 0 0 0 0z m-148 113c7-7 17-18 30-29 34-27 73-40 118-40 0 0 0 0 0 0 47 0 88 13 122 40 13 10 23 21 29 29-7 7-16 16-30 26-34 25-74 38-119 38-81 2-130-42-150-64z m-27 1z m227-4c0-25-21-46-47-46-26 0-47 21-47 46 0 26 21 47 47 47 26 0 47-21 47-47z"/>
|
||||
<glyph glyph-name="model" unicode="" d="M494 395c-2 5-8 8-13 7l-90-16 45 72c3 5 2 11-1 15-4 4-10 5-15 3l-213-98c-15 5-72 27-111 43 0 0-1 0-1 0 0 0 0 0 0 0 0 0-1 0-1 1 0 0-1 0-1 0 0 0-1 0-1 0 0 0 0 0 0 0-1 0-1 0-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0 0 0-1-1 0-1 0-2 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0-1 0-1-1 0-2 0-3-1 0 0 0 0 0 0 0-1-1-1-1-1 0 0 0 0 0 0 0 0 0-1 0-1-1 0-1 0-1 0 0 0 0 0 0-1 0 0 0 0-1-1l-27-52-33-40c-3-4-3-10 0-15 2-3 6-5 10-5 1 0 2 0 4 1l50 17 40 2-26-51c-3-4-2-9 1-13 1-1 26-30 52-58 15-17 28-30 38-40 6-6 11-11 15-14l-16-61-46-18c-6-3-9-10-6-16 2-5 6-8 11-8 1 0 3 1 4 1l45 18 17-15c2-3 5-4 8-4 4 0 7 2 9 5 5 5 4 12-1 17l-17 15 16 61 76-90c2-2 6-4 9-4 1 0 2 0 3 0l85 23c5 2 8 6 9 11 0 5-2 9-7 12l-136 72 45 91 178 123c5 3 6 9 4 15z m-200-117l-122 55 41 21 181 83z m-148 73l-24 33c16-6 39-15 54-21z m-59-6l-9 13 15 29 27-38 2-2z m36-77l23 44c18-45 35-91 47-121-19 20-45 49-70 77z m194-194l-57 68 105-55z m-94 101c-5 14-42 104-30 77 0-2-21 49-28 66l121-59-48-95z m108 120l43 63 70 16z"/>
|
||||
<glyph glyph-name="forward" unicode="D" d="M330 278l-95 70c-5 4-12 5-18 2-5-3-9-9-9-16l0-150c0-7 4-13 10-16 2-1 5-2 7-2 4 0 8 2 11 5l95 79c4 4 6 9 6 14-1 5-3 10-7 14"/>
|
||||
<glyph glyph-name="avatar-2" unicode="" d="M256 88c-93 0-169 75-169 168 0 93 76 169 169 169 93 0 169-76 169-169 0-93-76-168-169-168z m0 316c-81 0-148-66-148-148 0-81 67-147 148-147 81 0 148 66 148 147 0 82-67 148-148 148z m97-90l-1 1c-3 3-7 4-10 4-1 0-61-9-86-9-1 0-1 0-2 0-25 0-87 10-87 10-5 0-10-2-13-6l-1-2c-2-3-2-7-1-10 1-4 3-6 6-8 12-5 49-20 60-22 2 0 5 0 6-7 1-8-3-46-7-65-5-17-13-40-13-41-2-6 1-13 7-15l8-3c3-1 6-1 9 1 3 1 5 4 6 7l21 65 20-67c1-3 3-6 6-7 2-1 4-1 5-1 2 0 3 0 5 0l7 3c5 2 8 8 7 14 0 0-6 24-11 44-3 12-4 30-5 45 0 9-1 16-2 22 0 1 0 4 5 5 0 0 1 0 2 0l55 22c4 2 6 5 7 9 1 4 0 8-3 11z m-68 37c0-16-13-29-29-29-16 0-29 13-29 29 0 16 13 29 29 29 16 0 29-13 29-29z"/>
|
||||
<glyph glyph-name="arrow-dn" unicode="5" d="M258 219l-43 55 86 0z"/>
|
||||
<glyph glyph-name="arrow-up" unicode="6" d="M258 283l43-55-86 0z"/>
|
||||
|
@ -154,4 +150,8 @@
|
|||
<glyph glyph-name="uninstall" unicode="" d="M83 227c-7 0-13-6-13-13l0-78c0-19 16-35 35-35l297 0c19 0 35 16 35 35l0 78c0 7-6 13-13 13-7 0-13-6-13-13l0-78c0-5-4-9-9-9l-297 0c-5 0-9 4-9 9l0 78c0 7-6 13-13 13z m191 47l50 50c5 5 5 14 0 19-5 5-13 5-19 0l-50-50-50 50c-5 5-13 5-19 0-5-5-5-14 0-19l50-50-50-50c-5-5-5-14 0-19 5-5 14-5 19 0l50 50 50-50c5-5 14-5 19 0 5 5 5 14 0 19z"/>
|
||||
<glyph glyph-name="install" unicode="" d="M83 227c-7 0-13-6-13-13l0-78c0-19 16-35 35-35l297 0c19 0 35 16 35 35l0 78c0 7-6 13-13 13-7 0-13-6-13-13l0-78c0-5-4-9-9-9l-297 0c-5 0-9 4-9 9l0 78c0 7-6 13-13 13z m170 171l0-155-33 27c-6 5-14 5-19-1-4-5-4-14 2-18l54-48c3-2 5-3 8-3 3 0 7 1 9 3l54 48c5 4 6 13 1 18-4 5-13 6-18 1l-32-27 0 154c0 8-6 13-13 13-7 0-13-5-13-12z"/>
|
||||
<glyph glyph-name="ellipsis-vertical" unicode="" d="M276 178c0-14-11-24-24-24-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24z m0 78c0-14-11-24-24-24-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24z m0 78c0-14-11-24-24-24-14 0-25 10-25 24 0 13 11 24 25 24 13 0 24-11 24-24z"/>
|
||||
<glyph glyph-name="backward" unicode="E" d="M292 349c-5 3-12 3-18-1l-94-71c-4-3-7-8-7-13 0-5 2-10 6-14l95-80c3-2 7-4 11-4 2 0 5 1 7 2 6 3 10 9 10 15l0 151c0 6-4 12-10 15"/>
|
||||
<glyph glyph-name="40-reload" unicode="F" d="M365 261c-9 1-17-5-18-15-4-45-43-80-89-80-49 0-89 40-89 89 0 19 4 45 25 65 16 15 39 23 68 25l-15-16c-6-6-6-17 0-24 4-3 8-4 12-4 4 0 9 1 12 5l43 44c2 2 3 4 4 6 2 6 1 13-4 18l-44 44c-6 6-17 6-23 0-7-7-7-17 0-24l15-15c-38-2-69-14-91-35-23-21-36-53-36-88 0-68 55-123 123-123 64 0 116 47 122 110 1 9-5 18-15 18"/>
|
||||
<glyph glyph-name="forward" unicode="D" d="M330 278l-95 70c-5 4-12 5-18 2-5-3-9-9-9-16l0-150c0-7 4-13 10-16 2-1 5-2 7-2 4 0 8 2 11 5l95 79c4 4 6 9 6 14-1 5-3 10-7 14"/>
|
||||
<glyph glyph-name="avatar-1" unicode="T" d="M396 344l-2 2c-4 4-9 5-15 5-1 0-88-13-124-14-1 0-2 0-3 0-37 0-126 15-127 15-7 1-14-2-18-8l-2-4c-3-4-3-9-2-14 2-5 5-9 10-11 16-7 69-22 85-29 3-1 10-4 10-14 1-11-4-67-10-93-7-26-18-60-19-60-3-9 2-19 11-22l11-4c4-2 9-1 13 1 5 2 8 6 9 10l31 94 28-96c2-5 5-9 9-11 3-1 5-2 8-2 2 0 4 0 7 1l10 4c8 3 12 12 10 20 0 1-8 36-16 65-4 17-6 43-7 64-1 13-1 21-3 30 0 1 2 11 10 14 10 4 81 29 80 28 6 2 10 7 11 13 1 6-1 12-5 16z m-98 54c0-24-19-43-43-43-24 0-43 19-43 43 0 23 19 42 43 42 24 0 43-19 43-42z"/>
|
||||
</font></defs></svg>
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
@ -127,14 +127,6 @@
|
|||
<div class="icon icon-close-small"></div>
|
||||
<input type="text" readonly="readonly" value="close-small">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-backward"></div>
|
||||
<input type="text" readonly="readonly" value="backward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-reload"></div>
|
||||
<input type="text" readonly="readonly" value="reload">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-minimize"></div>
|
||||
<input type="text" readonly="readonly" value="minimize">
|
||||
|
@ -179,10 +171,6 @@
|
|||
<div class="icon icon-hifi-logo-small"></div>
|
||||
<input type="text" readonly="readonly" value="hifi-logo-small">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-avatar-1"></div>
|
||||
<input type="text" readonly="readonly" value="avatar-1">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-placemark"></div>
|
||||
<input type="text" readonly="readonly" value="placemark">
|
||||
|
@ -399,10 +387,6 @@
|
|||
<div class="icon icon-model"></div>
|
||||
<input type="text" readonly="readonly" value="model">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-forward"></div>
|
||||
<input type="text" readonly="readonly" value="forward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-avatar-2"></div>
|
||||
<input type="text" readonly="readonly" value="avatar-2">
|
||||
|
@ -603,6 +587,22 @@
|
|||
<div class="icon icon-ellipsis-vertical"></div>
|
||||
<input type="text" readonly="readonly" value="ellipsis-vertical">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-backward"></div>
|
||||
<input type="text" readonly="readonly" value="backward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-40-reload"></div>
|
||||
<input type="text" readonly="readonly" value="40-reload">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-forward"></div>
|
||||
<input type="text" readonly="readonly" value="forward">
|
||||
</li>
|
||||
<li>
|
||||
<div class="icon icon-avatar-1"></div>
|
||||
<input type="text" readonly="readonly" value="avatar-1">
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Character mapping</h2>
|
||||
<ul class="glyphs character-mapping">
|
||||
|
@ -718,14 +718,6 @@
|
|||
<div data-icon="C" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="C">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="E" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="E">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="F" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="F">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="I" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="I">
|
||||
|
@ -770,10 +762,6 @@
|
|||
<div data-icon="S" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="S">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="T" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="T">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="U" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="U">
|
||||
|
@ -990,10 +978,6 @@
|
|||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe008;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="D" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="D">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe009;">
|
||||
|
@ -1194,6 +1178,22 @@
|
|||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe034;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="E" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="E">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="F" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="F">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="D" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="D">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="T" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="T">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>(function() {
|
|
@ -122,12 +122,6 @@
|
|||
.icon-close-small:before {
|
||||
content: "\43";
|
||||
}
|
||||
.icon-backward:before {
|
||||
content: "\45";
|
||||
}
|
||||
.icon-reload:before {
|
||||
content: "\46";
|
||||
}
|
||||
.icon-minimize:before {
|
||||
content: "\49";
|
||||
}
|
||||
|
@ -161,9 +155,6 @@
|
|||
.icon-hifi-logo-small:before {
|
||||
content: "\53";
|
||||
}
|
||||
.icon-avatar-1:before {
|
||||
content: "\54";
|
||||
}
|
||||
.icon-placemark:before {
|
||||
content: "\55";
|
||||
}
|
||||
|
@ -326,9 +317,6 @@
|
|||
.icon-model:before {
|
||||
content: "\e008";
|
||||
}
|
||||
.icon-forward:before {
|
||||
content: "\44";
|
||||
}
|
||||
.icon-avatar-2:before {
|
||||
content: "\e009";
|
||||
}
|
||||
|
@ -479,3 +467,15 @@
|
|||
.icon-ellipsis-vertical:before {
|
||||
content: "\e034";
|
||||
}
|
||||
.icon-backward:before {
|
||||
content: "\45";
|
||||
}
|
||||
.icon-40-reload:before {
|
||||
content: "\46";
|
||||
}
|
||||
.icon-forward:before {
|
||||
content: "\44";
|
||||
}
|
||||
.icon-avatar-1:before {
|
||||
content: "\54";
|
||||
}
|
14
interface/resources/icons/tablet-icons/avatar-a.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M78.3,31.3l-0.4-0.4c-0.8-0.8-2-1.2-3.1-1c-0.2,0-18.6,2.7-26.2,2.8c-0.2,0-0.3,0-0.4,0c-7.8,0-26.6-3-26.8-3.1
|
||||
c-1.5-0.2-3,0.4-3.7,1.7l-0.5,0.8c-0.6,0.9-0.7,2-0.4,3c0.3,1,1.1,1.8,2,2.3c3.4,1.5,14.9,6.3,18.2,6.7c0.6,0.1,1.7,0.2,1.8,2.2
|
||||
c0.2,2.4-0.8,14.1-2.2,19.7c-1.4,5.4-3.8,12.5-3.9,12.6c-0.7,1.9,0.3,4,2.2,4.7l2.3,0.8c0.9,0.3,2,0.3,2.8-0.2
|
||||
c0.9-0.4,1.6-1.2,1.9-2.2l6.4-19.7l6,20.3c0.3,1,1,1.8,1.9,2.3c0.5,0.2,1.1,0.4,1.6,0.4c0.5,0,0.9-0.1,1.3-0.2l2.1-0.8
|
||||
c1.7-0.7,2.7-2.5,2.2-4.3c0-0.1-1.8-7.4-3.3-13.5c-0.9-3.7-1.3-9.2-1.6-13.6c-0.2-2.7-0.3-5-0.6-6.8c-0.1-0.3-0.2-1.1,1.3-1.4
|
||||
c0.3,0,0.4-0.1,0.6-0.2l16.9-6.7c1.2-0.5,2-1.5,2.3-2.7C79.5,33.5,79.2,32.2,78.3,31.3z"/>
|
||||
<ellipse cx="48.7" cy="20" rx="9" ry="9"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
17
interface/resources/icons/tablet-icons/avatar-i.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M78.3,31.3l-0.4-0.4c-0.8-0.8-2-1.2-3.1-1c-0.2,0-18.6,2.7-26.2,2.8c-0.2,0-0.3,0-0.4,0
|
||||
c-7.8,0-26.6-3-26.8-3.1c-1.5-0.2-3,0.4-3.7,1.7l-0.5,0.8c-0.6,0.9-0.7,2-0.4,3c0.3,1,1.1,1.8,2,2.3c3.4,1.5,14.9,6.3,18.2,6.7
|
||||
c0.6,0.1,1.7,0.2,1.8,2.2c0.2,2.4-0.8,14.1-2.2,19.7c-1.4,5.4-3.8,12.5-3.9,12.6c-0.7,1.9,0.3,4,2.2,4.7l2.3,0.8
|
||||
c0.9,0.3,2,0.3,2.8-0.2c0.9-0.4,1.6-1.2,1.9-2.2l6.4-19.7l6,20.3c0.3,1,1,1.8,1.9,2.3c0.5,0.2,1.1,0.4,1.6,0.4
|
||||
c0.5,0,0.9-0.1,1.3-0.2l2.1-0.8c1.7-0.7,2.7-2.5,2.2-4.3c0-0.1-1.8-7.4-3.3-13.5c-0.9-3.7-1.3-9.2-1.6-13.6c-0.2-2.7-0.3-5-0.6-6.8
|
||||
c-0.1-0.3-0.2-1.1,1.3-1.4c0.3,0,0.4-0.1,0.6-0.2l16.9-6.7c1.2-0.5,2-1.5,2.3-2.7C79.5,33.5,79.2,32.2,78.3,31.3z"/>
|
||||
<ellipse class="st0" cx="48.7" cy="20" rx="9" ry="9"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
9
interface/resources/images/FavoriteIconActive.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<svg width="24" height="22" viewBox="0 0 24 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Canvas" fill="none">
|
||||
<g id="Vector">
|
||||
<path d="M 4.29197 19.3019C 4.20267 19.3019 4.11337 19.2697 4.02407 19.2052C 3.96826 19.1622 3.81199 19.0548 3.90129 18.6894L 4.21383 17.4427C 4.55987 16.0778 4.89474 14.7236 5.24078 13.3588C 5.43054 12.6064 5.20729 11.9509 4.60452 11.4672C 3.80082 10.8117 2.9748 10.1453 2.18227 9.51126C 1.54601 8.99539 0.898591 8.46878 0.262332 7.95292C -0.0278912 7.72723 -0.0167287 7.57677 0.0167586 7.41556C 0.0390835 7.32958 0.0948956 7.07165 0.53023 7.05015C 2.65109 6.91044 4.51522 6.78147 6.23423 6.66326C 7.02677 6.60952 7.6407 6.19038 7.93092 5.48106C 8.34393 4.51382 8.75694 3.54657 9.15879 2.56858C 9.47134 1.83777 9.78389 1.09621 10.0964 0.365404C 10.2192 0.0752303 10.3643 0.0107472 10.5987 1.02493e-08L 10.6099 1.02493e-08C 10.8108 1.02493e-08 10.9671 0.193449 11.0452 0.365404L 11.8266 2.20317C 12.2954 3.29939 12.7643 4.3956 13.2219 5.50256C 13.5121 6.17963 14.1038 6.59877 14.8628 6.65251C 15.7558 6.71699 16.6488 6.78147 17.5195 6.83521C 18.0664 6.86745 18.6245 6.91044 19.1715 6.94268C 19.2943 6.95343 19.4171 6.96418 19.5399 6.96418C 19.9752 6.99642 20.377 7.01791 20.79 7.07165C 21.1249 7.11464 21.1584 7.46929 21.1584 7.57677L 21.1584 7.60901C 21.1584 7.71648 20.9686 7.87769 20.8905 7.94217C 20.4217 8.31832 19.9417 8.68373 19.4729 9.05988C 18.5129 9.81218 17.5306 10.586 16.5595 11.3383C 15.8563 11.8864 15.5995 12.6064 15.8116 13.4447C 16.1242 14.6377 16.4255 15.8521 16.7269 17.0343C 16.8609 17.5716 16.9948 18.109 17.1288 18.6464C 17.1734 18.8398 17.1511 19.0225 17.0506 19.1515C 16.9725 19.2375 16.872 19.2912 16.7381 19.2912C 16.7158 19.2912 16.6934 19.2912 16.6711 19.2912C 16.6488 19.2912 16.5818 19.2697 16.4702 19.2052C 14.7958 18.2272 13.1438 17.2492 11.5587 16.3035C 11.2238 16.0993 10.8666 16.0026 10.5094 16.0026C 10.1522 16.0026 9.79505 16.11 9.44901 16.3142C 8.24347 17.0343 7.02677 17.7651 5.84355 18.4744L 4.61568 19.2052C 4.49289 19.2697 4.38127 19.3019 4.29197 19.3019Z" transform="translate(1.5 1)" fill="#FFB017"/>
|
||||
<path d="M 4.29197 19.3019C 4.20267 19.3019 4.11337 19.2697 4.02407 19.2052C 3.96826 19.1622 3.81199 19.0548 3.90129 18.6894L 4.21383 17.4427C 4.55987 16.0778 4.89474 14.7236 5.24078 13.3588C 5.43054 12.6064 5.20729 11.9509 4.60452 11.4672C 3.80082 10.8117 2.9748 10.1453 2.18227 9.51126C 1.54601 8.99539 0.898591 8.46878 0.262332 7.95292C -0.0278912 7.72723 -0.0167287 7.57677 0.0167586 7.41556C 0.0390835 7.32958 0.0948956 7.07165 0.53023 7.05015C 2.65109 6.91044 4.51522 6.78147 6.23423 6.66326C 7.02677 6.60952 7.6407 6.19038 7.93092 5.48106C 8.34393 4.51382 8.75694 3.54657 9.15879 2.56858C 9.47134 1.83777 9.78389 1.09621 10.0964 0.365404C 10.2192 0.0752303 10.3643 0.0107472 10.5987 1.02493e-08L 10.6099 1.02493e-08C 10.8108 1.02493e-08 10.9671 0.193449 11.0452 0.365404L 11.8266 2.20317C 12.2954 3.29939 12.7643 4.3956 13.2219 5.50256C 13.5121 6.17963 14.1038 6.59877 14.8628 6.65251C 15.7558 6.71699 16.6488 6.78147 17.5195 6.83521C 18.0664 6.86745 18.6245 6.91044 19.1715 6.94268C 19.2943 6.95343 19.4171 6.96418 19.5399 6.96418C 19.9752 6.99642 20.377 7.01791 20.79 7.07165C 21.1249 7.11464 21.1584 7.46929 21.1584 7.57677L 21.1584 7.60901C 21.1584 7.71648 20.9686 7.87769 20.8905 7.94217C 20.4217 8.31832 19.9417 8.68373 19.4729 9.05988C 18.5129 9.81218 17.5306 10.586 16.5595 11.3383C 15.8563 11.8864 15.5995 12.6064 15.8116 13.4447C 16.1242 14.6377 16.4255 15.8521 16.7269 17.0343C 16.8609 17.5716 16.9948 18.109 17.1288 18.6464C 17.1734 18.8398 17.1511 19.0225 17.0506 19.1515C 16.9725 19.2375 16.872 19.2912 16.7381 19.2912C 16.7158 19.2912 16.6934 19.2912 16.6711 19.2912C 16.6488 19.2912 16.5818 19.2697 16.4702 19.2052C 14.7958 18.2272 13.1438 17.2492 11.5587 16.3035C 11.2238 16.0993 10.8666 16.0026 10.5094 16.0026C 10.1522 16.0026 9.79505 16.11 9.44901 16.3142C 8.24347 17.0343 7.02677 17.7651 5.84355 18.4744L 4.61568 19.2052C 4.49289 19.2697 4.38127 19.3019 4.29197 19.3019Z" stroke-width="1.75" transform="translate(1.5 1)" stroke="#121212"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4 KiB |
8
interface/resources/images/FavoriteIconInActive.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<svg width="24" height="22" viewBox="0 0 24 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Canvas" fill="none">
|
||||
<g id="Vector">
|
||||
<path d="M 4.29197 19.3019C 4.20267 19.3019 4.11337 19.2697 4.02407 19.2052C 3.96826 19.1622 3.81199 19.0548 3.90129 18.6894L 4.21383 17.4427C 4.55987 16.0778 4.89474 14.7236 5.24078 13.3588C 5.43054 12.6064 5.20729 11.9509 4.60452 11.4672C 3.80082 10.8117 2.9748 10.1453 2.18227 9.51126C 1.54601 8.99539 0.898591 8.46878 0.262332 7.95292C -0.0278912 7.72723 -0.0167287 7.57677 0.0167586 7.41556C 0.0390835 7.32958 0.0948956 7.07165 0.53023 7.05015C 2.65109 6.91044 4.51522 6.78147 6.23423 6.66326C 7.02677 6.60952 7.6407 6.19038 7.93092 5.48106C 8.34393 4.51382 8.75694 3.54657 9.15879 2.56858C 9.47134 1.83777 9.78389 1.09621 10.0964 0.365404C 10.2192 0.0752303 10.3643 0.0107472 10.5987 1.02493e-08L 10.6099 1.02493e-08C 10.8108 1.02493e-08 10.9671 0.193449 11.0452 0.365404L 11.8266 2.20317C 12.2954 3.29939 12.7643 4.3956 13.2219 5.50256C 13.5121 6.17963 14.1038 6.59877 14.8628 6.65251C 15.7558 6.71699 16.6488 6.78147 17.5195 6.83521C 18.0664 6.86745 18.6245 6.91044 19.1715 6.94268C 19.2943 6.95343 19.4171 6.96418 19.5399 6.96418C 19.9752 6.99642 20.377 7.01791 20.79 7.07165C 21.1249 7.11464 21.1584 7.46929 21.1584 7.57677L 21.1584 7.60901C 21.1584 7.71648 20.9686 7.87769 20.8905 7.94217C 20.4217 8.31832 19.9417 8.68373 19.4729 9.05988C 18.5129 9.81218 17.5306 10.586 16.5595 11.3383C 15.8563 11.8864 15.5995 12.6064 15.8116 13.4447C 16.1242 14.6377 16.4255 15.8521 16.7269 17.0343C 16.8609 17.5716 16.9948 18.109 17.1288 18.6464C 17.1734 18.8398 17.1511 19.0225 17.0506 19.1515C 16.9725 19.2375 16.872 19.2912 16.7381 19.2912C 16.7158 19.2912 16.6934 19.2912 16.6711 19.2912C 16.6488 19.2912 16.5818 19.2697 16.4702 19.2052C 14.7958 18.2272 13.1438 17.2492 11.5587 16.3035C 11.2238 16.0993 10.8666 16.0026 10.5094 16.0026C 10.1522 16.0026 9.79505 16.11 9.44901 16.3142C 8.24347 17.0343 7.02677 17.7651 5.84355 18.4744L 4.61568 19.2052C 4.49289 19.2697 4.38127 19.3019 4.29197 19.3019Z" stroke-width="1.75" transform="translate(1.5 1)" stroke="#121212"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
BIN
interface/resources/images/avatarapp/AvatarIsland.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
interface/resources/images/avatarapp/BodyMart.PNG
Normal file
After Width: | Height: | Size: 126 KiB |
20
interface/resources/images/avatarapp/guy-in-circle.svg
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 134 134" style="enable-background:new 0 0 134 134;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1FC6A6;}
|
||||
</style>
|
||||
<path class="st0" d="M5,0h124c2.8,0,5,2.2,5,5v124c0,2.8-2.2,5-5,5H5c-2.8,0-5-2.2-5-5V5C0,2.2,2.2,0,5,0z"/>
|
||||
<path d="M100.5,99c-9.2,9.2-20.3,13.8-33.5,13.8S42.6,108.2,33.3,99c-9.2-9.2-13.8-20.3-13.8-33.5s4.6-24.3,13.8-33.5
|
||||
C42.6,22.7,53.9,18,67,18s24.3,4.7,33.5,14.1c9.4,9.2,14.1,20.3,14.1,33.5S109.8,89.9,100.5,99z M96.3,36.3
|
||||
C88.2,28.1,78.4,23.9,67,23.9s-21.3,4.1-29.5,12.4c-8.1,8.1-12.1,17.8-12.1,29.3s4,21.2,12.1,29.3c8.3,8.1,18.1,12.1,29.5,12.1
|
||||
s21.2-4,29.3-12.1c8.3-8.1,12.4-17.8,12.4-29.3S104.5,44.4,96.3,36.3z M94.3,49.3L94,49c-0.8-0.8-1.7-1.1-2.8-1.1l-1.7,0.3
|
||||
c-1.3,0.2-3,0.5-5.1,0.8c-1.9,0.2-3.8,0.4-5.9,0.6c-2.1,0.2-4.2,0.4-6.5,0.6c-2.1,0.2-3.8,0.3-5.1,0.3h-0.6L42,47.6
|
||||
c-1.7,0-2.9,0.6-3.7,1.7L38,49.8c-0.6,0.8-0.7,1.7-0.3,2.8c0.2,0.9,0.8,1.7,1.7,2.3c8.8,3.8,14.4,5.8,16.9,6.2c0.9,0,1.5,0.7,1.7,2
|
||||
c0.4,3.8-0.3,9.8-2,18.3c-0.4,1.3-0.9,3-1.7,5.1c-0.6,1.9-1,3.4-1.4,4.5l-0.6,2c-0.8,2.1-0.1,3.5,2,4.2l2.3,0.8
|
||||
c0.9,0.4,1.8,0.3,2.5-0.3c0.8-0.2,1.3-0.8,1.7-2l5.9-18.3l5.6,18.8c0.4,1.1,0.9,1.8,1.7,2c0.4,0.2,0.8,0.3,1.4,0.3h1.4l2-0.8
|
||||
c1.7-0.8,2.3-2.1,2-3.9l-3.1-12.4c-0.6-2.1-1-6.3-1.4-12.7c0-1.9-0.2-3.9-0.6-6.2c0-0.8,0.5-1.2,1.4-1.4h0.6l15.5-6.2
|
||||
c0.9-0.6,1.6-1.4,2-2.5S95.2,50.2,94.3,49.3z M72.6,33.2c1.7,1.5,2.5,3.4,2.5,5.6s-0.8,4.2-2.5,5.9C71.1,46.3,69.3,47,67,47
|
||||
s-4.2-0.8-5.9-2.3c-1.5-1.7-2.3-3.7-2.3-5.9s0.8-4.1,2.3-5.6c1.7-1.7,3.7-2.5,5.9-2.5S71.1,31.5,72.6,33.2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -19,6 +19,8 @@ Original.Button {
|
|||
|
||||
property int color: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property int fontSize: hifi.fontSizes.buttonLabel
|
||||
property alias implicitTextWidth: buttonText.implicitWidth
|
||||
property string buttonGlyph: "";
|
||||
|
||||
width: hifi.dimensions.buttonWidth
|
||||
|
@ -108,7 +110,7 @@ Original.Button {
|
|||
font.capitalization: Font.AllUppercase
|
||||
color: enabled ? hifi.buttons.textColor[control.color]
|
||||
: hifi.buttons.disabledTextColor[control.colorScheme]
|
||||
size: hifi.fontSizes.buttonLabel
|
||||
size: control.fontSize
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: control.text
|
||||
|
|
|
@ -46,6 +46,7 @@ FocusScope {
|
|||
hoverEnabled: true
|
||||
visible: true
|
||||
height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control.
|
||||
textRole: "text"
|
||||
|
||||
function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; }
|
||||
function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; }
|
||||
|
|
|
@ -25,10 +25,16 @@ Original.RadioButton {
|
|||
property int colorScheme: hifi.colorSchemes.light
|
||||
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
|
||||
|
||||
readonly property int boxSize: 14
|
||||
readonly property int boxRadius: 3
|
||||
readonly property int checkSize: 10
|
||||
readonly property int checkRadius: 2
|
||||
property real letterSpacing: 1
|
||||
property int fontSize: hifi.fontSizes.inputLabel
|
||||
property int boxSize: defaultBoxSize
|
||||
property real scaleFactor: boxSize / defaultBoxSize
|
||||
|
||||
readonly property int defaultBoxSize: 14
|
||||
readonly property int boxRadius: 3 * scaleFactor
|
||||
readonly property int checkSize: 10 * scaleFactor
|
||||
readonly property int checkRadius: 2 * scaleFactor
|
||||
readonly property int indicatorRadius: 7 * scaleFactor
|
||||
|
||||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
|
@ -44,7 +50,7 @@ Original.RadioButton {
|
|||
id: box
|
||||
width: boxSize
|
||||
height: boxSize
|
||||
radius: 7
|
||||
radius: indicatorRadius
|
||||
x: radioButton.leftPadding
|
||||
y: parent.height / 2 - height / 2
|
||||
gradient: Gradient {
|
||||
|
@ -66,7 +72,7 @@ Original.RadioButton {
|
|||
id: check
|
||||
width: checkSize
|
||||
height: checkSize
|
||||
radius: 7
|
||||
radius: indicatorRadius
|
||||
anchors.centerIn: parent
|
||||
color: "#00B4EF"
|
||||
border.width: 1
|
||||
|
@ -77,7 +83,8 @@ Original.RadioButton {
|
|||
|
||||
contentItem: RalewaySemiBold {
|
||||
text: radioButton.text
|
||||
size: hifi.fontSizes.inputLabel
|
||||
size: radioButton.fontSize
|
||||
font.letterSpacing: letterSpacing
|
||||
color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
|
|
@ -27,6 +27,9 @@ SpinBox {
|
|||
property string suffix: ""
|
||||
property string labelInside: ""
|
||||
property color colorLabelInside: hifi.colors.white
|
||||
property color backgroundColor: isLightColorScheme
|
||||
? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray)
|
||||
: (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
|
||||
property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0)
|
||||
property int decimals: 2;
|
||||
property real factor: Math.pow(10, decimals)
|
||||
|
@ -55,10 +58,14 @@ SpinBox {
|
|||
|
||||
onValueModified: realValue = value/factor
|
||||
onValueChanged: realValue = value/factor
|
||||
onRealValueChanged: {
|
||||
var newValue = Math.round(realValue*factor);
|
||||
if(value != newValue) {
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
stepSize: realStepSize*factor
|
||||
value: Math.round(realValue*factor)
|
||||
|
||||
to : realTo*factor
|
||||
from : realFrom*factor
|
||||
|
||||
|
@ -69,9 +76,7 @@ SpinBox {
|
|||
y: spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0
|
||||
|
||||
background: Rectangle {
|
||||
color: isLightColorScheme
|
||||
? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray)
|
||||
: (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
|
||||
color: backgroundColor
|
||||
border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight
|
||||
border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0
|
||||
}
|
||||
|
|
|
@ -26,11 +26,9 @@ Preference {
|
|||
preference.save();
|
||||
}
|
||||
|
||||
Item {
|
||||
Row {
|
||||
id: control
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: Math.max(spinnerLabel.height, spinner.controlHeight)
|
||||
|
@ -40,15 +38,14 @@ Preference {
|
|||
text: root.label + ":"
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: spinner.left
|
||||
rightMargin: hifi.dimensions.labelPadding
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
horizontalAlignment: Text.AlignRight
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
spacing: hifi.dimensions.labelPadding
|
||||
|
||||
SpinBox {
|
||||
id: spinner
|
||||
decimals: preference.decimals
|
||||
|
@ -56,7 +53,6 @@ Preference {
|
|||
maximumValue: preference.max
|
||||
width: 100
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
|
840
interface/resources/qml/hifi/AvatarApp.qml
Normal file
|
@ -0,0 +1,840 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQml.Models 2.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../controls-uit" as HifiControls
|
||||
import "../styles-uit"
|
||||
import "avatarapp"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 480
|
||||
height: 706
|
||||
|
||||
property bool keyboardEnabled: true
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
z: 1000
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
color: style.colors.white
|
||||
property string getAvatarsMethod: 'getAvatars'
|
||||
|
||||
signal sendToScript(var message);
|
||||
function emitSendToScript(message) {
|
||||
sendToScript(message);
|
||||
}
|
||||
|
||||
ListModel { // the only purpose of this model is to convert JS object to ListElement
|
||||
id: currentAvatarModel
|
||||
dynamicRoles: true;
|
||||
function makeAvatarEntry(avatarObject) {
|
||||
clear();
|
||||
append(avatarObject);
|
||||
return get(count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
property var jointNames;
|
||||
property var currentAvatarSettings;
|
||||
|
||||
function fetchAvatarModelName(marketId, avatar) {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
var url = "https://highfidelity.com/api/v1/marketplace/items/" + marketId;
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status === 200) {
|
||||
try {
|
||||
var marketResponse = JSON.parse(xmlhttp.responseText.trim())
|
||||
|
||||
if(marketResponse.status === 'success') {
|
||||
avatar.modelName = marketResponse.data.title;
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
xmlhttp.open("GET", url, true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
|
||||
function getAvatarModelName() {
|
||||
|
||||
if(currentAvatar === null) {
|
||||
return '';
|
||||
}
|
||||
if(currentAvatar.modelName !== undefined) {
|
||||
return currentAvatar.modelName;
|
||||
} else {
|
||||
var marketId = allAvatars.extractMarketId(currentAvatar.avatarUrl);
|
||||
if(marketId !== '') {
|
||||
fetchAvatarModelName(marketId, currentAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
var avatarUrl = currentAvatar.avatarUrl;
|
||||
var splitted = avatarUrl.split('/');
|
||||
|
||||
return splitted[splitted.length - 1];
|
||||
}
|
||||
|
||||
property string avatarName: currentAvatar ? currentAvatar.name : ''
|
||||
property string avatarUrl: currentAvatar ? currentAvatar.thumbnailUrl : null
|
||||
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, bookmarkName) {
|
||||
var currentAvatarObject = allAvatars.makeAvatarObject(avatar, bookmarkName);
|
||||
currentAvatar = currentAvatarModel.makeAvatarEntry(currentAvatarObject);
|
||||
}
|
||||
|
||||
property url externalAvatarThumbnailUrl: '../../images/avatarapp/guy-in-circle.svg'
|
||||
|
||||
function fromScript(message) {
|
||||
if(message.method === 'initialize') {
|
||||
jointNames = message.data.jointNames;
|
||||
emitSendToScript({'method' : getAvatarsMethod});
|
||||
} else if(message.method === 'wearableUpdated') {
|
||||
adjustWearables.refreshWearable(message.entityID, message.wearableIndex, message.properties, message.updateUI);
|
||||
} else if(message.method === 'wearablesUpdated') {
|
||||
var wearablesModel = currentAvatar.wearables;
|
||||
wearablesModel.clear();
|
||||
message.wearables.forEach(function(wearable) {
|
||||
wearablesModel.append(wearable);
|
||||
});
|
||||
adjustWearables.refresh(currentAvatar);
|
||||
} else if(message.method === 'scaleChanged') {
|
||||
currentAvatar.avatarScale = message.value;
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'externalAvatarApplied') {
|
||||
currentAvatar.avatarUrl = message.avatarURL;
|
||||
currentAvatar.thumbnailUrl = allAvatars.makeThumbnailUrl(message.avatarURL);
|
||||
currentAvatar.entry.avatarUrl = currentAvatar.avatarUrl;
|
||||
currentAvatar.modelName = undefined;
|
||||
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);
|
||||
allAvatars.move(avatarIndex, 0, 1);
|
||||
view.setPage(0);
|
||||
} else if(message.method === 'bookmarkAdded') {
|
||||
var avatar = allAvatars.findAvatar(message.bookmarkName);
|
||||
if(avatar !== undefined) {
|
||||
var avatarObject = allAvatars.makeAvatarObject(message.bookmark, message.bookmarkName);
|
||||
for(var prop in avatarObject) {
|
||||
avatar[prop] = avatarObject[prop];
|
||||
}
|
||||
if(currentAvatar.name === message.bookmarkName) {
|
||||
currentAvatar = currentAvatarModel.makeAvatarEntry(avatarObject);
|
||||
}
|
||||
} else {
|
||||
allAvatars.addAvatarEntry(message.bookmark, message.bookmarkName);
|
||||
}
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'bookmarkDeleted') {
|
||||
pageOfAvatars.isUpdating = true;
|
||||
|
||||
var index = pageOfAvatars.findAvatarIndex(message.name);
|
||||
var absoluteIndex = view.currentPage * view.itemsPerPage + index
|
||||
|
||||
allAvatars.remove(absoluteIndex)
|
||||
pageOfAvatars.remove(index);
|
||||
|
||||
var itemsOnPage = pageOfAvatars.count;
|
||||
var newItemIndex = view.currentPage * view.itemsPerPage + itemsOnPage;
|
||||
|
||||
if(newItemIndex <= (allAvatars.count - 1)) {
|
||||
pageOfAvatars.append(allAvatars.get(newItemIndex));
|
||||
} else {
|
||||
if(!pageOfAvatars.hasGetAvatars())
|
||||
pageOfAvatars.appendGetAvatars();
|
||||
}
|
||||
|
||||
pageOfAvatars.isUpdating = false;
|
||||
} else if(message.method === getAvatarsMethod) {
|
||||
var getAvatarsData = message.data;
|
||||
allAvatars.populate(getAvatarsData.bookmarks);
|
||||
setCurrentAvatar(getAvatarsData.currentAvatar, '');
|
||||
displayNameInput.text = getAvatarsData.displayName;
|
||||
currentAvatarSettings = getAvatarsData.currentAvatarSettings;
|
||||
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'updateAvatarInBookmarks') {
|
||||
updateCurrentAvatarInBookmarks(currentAvatar);
|
||||
} else if(message.method === 'selectAvatarEntity') {
|
||||
adjustWearables.selectWearableByID(message.entityID);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCurrentAvatarInBookmarks(avatar) {
|
||||
var bookmarkAvatarIndex = allAvatars.findAvatarIndexByValue(avatar);
|
||||
if(bookmarkAvatarIndex === -1) {
|
||||
avatar.name = '';
|
||||
view.setPage(0);
|
||||
} else {
|
||||
var bookmarkAvatar = allAvatars.get(bookmarkAvatarIndex);
|
||||
avatar.name = bookmarkAvatar.name;
|
||||
view.selectAvatar(bookmarkAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
property bool isInManageState: false
|
||||
|
||||
Component.onCompleted: {
|
||||
}
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
AvatarAppHeader {
|
||||
id: header
|
||||
z: 100
|
||||
|
||||
property string currentPage: "Avatar"
|
||||
property bool mainPageVisible: !settings.visible && !adjustWearables.visible
|
||||
|
||||
Binding on currentPage {
|
||||
when: settings.visible
|
||||
value: "Avatar Settings"
|
||||
}
|
||||
Binding on currentPage {
|
||||
when: adjustWearables.visible
|
||||
value: "Adjust Wearables"
|
||||
}
|
||||
Binding on currentPage {
|
||||
when: header.mainPageVisible
|
||||
value: "Avatar"
|
||||
}
|
||||
|
||||
pageTitle: currentPage
|
||||
avatarIconVisible: mainPageVisible
|
||||
settingsButtonVisible: mainPageVisible
|
||||
onSettingsClicked: {
|
||||
settings.open(currentAvatarSettings, currentAvatar.avatarScale);
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
id: settings
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
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() {
|
||||
emitSendToScript({'method' : 'revertScale', 'avatarScale' : avatarScaleBackup});
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
onScaleChanged: {
|
||||
emitSendToScript({'method' : 'setScale', 'avatarScale' : scale})
|
||||
}
|
||||
}
|
||||
|
||||
AdjustWearables {
|
||||
id: adjustWearables
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
jointNames: root.jointNames
|
||||
onWearableUpdated: {
|
||||
emitSendToScript({'method' : 'adjustWearable', 'entityID' : id, 'wearableIndex' : index, 'properties' : properties})
|
||||
}
|
||||
onWearableDeleted: {
|
||||
emitSendToScript({'method' : 'deleteWearable', 'entityID' : id, 'avatarName' : avatarName});
|
||||
}
|
||||
onAdjustWearablesOpened: {
|
||||
emitSendToScript({'method' : 'adjustWearablesOpened', 'avatarName' : avatarName});
|
||||
}
|
||||
onAdjustWearablesClosed: {
|
||||
emitSendToScript({'method' : 'adjustWearablesClosed', 'save' : status, 'avatarName' : avatarName});
|
||||
}
|
||||
onWearableSelected: {
|
||||
emitSendToScript({'method' : 'selectWearable', 'entityID' : id});
|
||||
}
|
||||
|
||||
z: 3
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mainBlock
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: favoritesBlock.top
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
size: 24;
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 34
|
||||
}
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: displayNameLabel
|
||||
size: 24;
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 25
|
||||
text: 'Display Name'
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: displayNameInput
|
||||
|
||||
font.family: "Fira Sans"
|
||||
font.pixelSize: 15
|
||||
anchors.left: displayNameLabel.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.verticalCenter: displayNameLabel.verticalCenter
|
||||
anchors.right: parent.right
|
||||
width: 232
|
||||
|
||||
text: 'ThisIsDisplayName'
|
||||
|
||||
onEditingFinished: {
|
||||
emitSendToScript({'method' : 'changeDisplayName', 'displayName' : text})
|
||||
focus = false;
|
||||
}
|
||||
}
|
||||
|
||||
ShadowImage {
|
||||
id: avatarImage
|
||||
width: 134
|
||||
height: 134
|
||||
anchors.top: displayNameLabel.bottom
|
||||
anchors.topMargin: 31
|
||||
Binding on source {
|
||||
when: avatarUrl !== ''
|
||||
value: avatarUrl
|
||||
}
|
||||
|
||||
visible: avatarImage.status !== Image.Loading && avatarImage.status !== Image.Error
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
|
||||
ShadowImage {
|
||||
id: customAvatarImage
|
||||
anchors.fill: avatarImage;
|
||||
visible: avatarUrl === '' || avatarImage.status === Image.Error
|
||||
source: externalAvatarThumbnailUrl
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
anchors.fill: avatarImage;
|
||||
color: 'white'
|
||||
visible: avatarImage.status === Image.Loading
|
||||
radius: avatarImage.radius
|
||||
|
||||
dropShadowRadius: avatarImage.dropShadowRadius;
|
||||
dropShadowHorizontalOffset: avatarImage.dropShadowHorizontalOffset
|
||||
dropShadowVerticalOffset: avatarImage.dropShadowVerticalOffset
|
||||
|
||||
Spinner {
|
||||
id: spinner
|
||||
visible: parent.visible
|
||||
anchors.fill: parent;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarWearablesIndicator {
|
||||
anchors.right: avatarImage.right
|
||||
anchors.bottom: avatarImage.bottom
|
||||
anchors.rightMargin: -radius
|
||||
anchors.bottomMargin: 6.08
|
||||
wearablesCount: avatarWearablesCount
|
||||
visible: avatarWearablesCount !== 0
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: star
|
||||
anchors.top: avatarImage.top
|
||||
anchors.topMargin: 11
|
||||
anchors.left: avatarImage.right
|
||||
anchors.leftMargin: 30.5
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 12.3
|
||||
|
||||
Image {
|
||||
width: 21.2
|
||||
height: 19.3
|
||||
source: isAvatarInFavorites ? '../../images/FavoriteIconActive.svg' : '../../images/FavoriteIconInActive.svg'
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
size: 22;
|
||||
Layout.fillWidth: true
|
||||
text: isAvatarInFavorites ? avatarName : "Add to Favorites"
|
||||
elide: Qt.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
enabled: !isAvatarInFavorites
|
||||
anchors.fill: star
|
||||
onClicked: {
|
||||
createFavorite.onSaveClicked = function() {
|
||||
var entry = currentAvatar.entry;
|
||||
|
||||
var wearables = [];
|
||||
for(var i = 0; i < currentAvatar.wearables.count; ++i) {
|
||||
wearables.push(currentAvatar.wearables.get(i));
|
||||
}
|
||||
|
||||
entry.avatarEntites = wearables;
|
||||
currentAvatar.name = createFavorite.favoriteNameText;
|
||||
|
||||
emitSendToScript({'method': 'addAvatar', 'name' : currentAvatar.name});
|
||||
createFavorite.close();
|
||||
}
|
||||
|
||||
var avatarThumbnail = (avatarUrl === '' || avatarImage.status === Image.Error) ?
|
||||
externalAvatarThumbnailUrl : avatarUrl;
|
||||
|
||||
createFavorite.open(root.currentAvatar.wearables.count, avatarThumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
id: avatarNameLabel
|
||||
size: 22;
|
||||
text: getAvatarModelName();
|
||||
elide: Qt.ElideRight
|
||||
|
||||
anchors.right: linkLabel.left
|
||||
anchors.left: avatarImage.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: star.bottom
|
||||
anchors.topMargin: 11
|
||||
property bool hasMarketId: currentAvatar && allAvatars.extractMarketId(currentAvatar.avatarUrl) !== '';
|
||||
|
||||
MouseArea {
|
||||
enabled: avatarNameLabel.hasMarketId
|
||||
anchors.fill: parent;
|
||||
onClicked: emitSendToScript({'method' : 'navigate', 'url' : allAvatars.makeMarketItemUrl(currentAvatar.avatarUrl)})
|
||||
}
|
||||
color: hasMarketId ? style.colors.blueHighlight : 'black'
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
id: wearablesLabel
|
||||
size: 22;
|
||||
anchors.left: avatarImage.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: avatarNameLabel.bottom
|
||||
anchors.topMargin: 16
|
||||
color: 'black'
|
||||
text: 'Wearables'
|
||||
}
|
||||
|
||||
SquareLabel {
|
||||
id: linkLabel
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: avatarNameLabel.verticalCenter
|
||||
glyphText: "."
|
||||
glyphSize: 22
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popup.showSpecifyAvatarUrl(function() {
|
||||
var url = popup.inputText.text;
|
||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||
}, function(link) {
|
||||
Qt.openUrlExternally(link);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SquareLabel {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: wearablesLabel.verticalCenter
|
||||
glyphText: "\ue02e"
|
||||
|
||||
visible: avatarWearablesCount !== 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
size: 22;
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: wearablesLabel.verticalCenter
|
||||
font.underline: true
|
||||
text: "Add"
|
||||
color: 'black'
|
||||
visible: avatarWearablesCount === 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popup.showGetWearables(function() {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : 'hifi://AvatarIsland/11.5848,-8.10862,-2.80195'})
|
||||
}, function(link) {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : link})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: favoritesBlock
|
||||
height: 407
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
color: style.colors.lightGrayBackground
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: favoritesLabel
|
||||
size: 24;
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
text: "Favorites"
|
||||
}
|
||||
|
||||
// TextStyle8
|
||||
RalewaySemiBold {
|
||||
id: manageLabel
|
||||
color: style.colors.blueHighlight
|
||||
size: 20;
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 20
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
text: isInManageState ? "Back" : "Manage"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
isInManageState = isInManageState ? false : true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
anchors.top: favoritesLabel.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
GridView {
|
||||
id: view
|
||||
anchors.fill: parent
|
||||
interactive: false;
|
||||
currentIndex: currentAvatarIndexInBookmarksPage();
|
||||
|
||||
function currentAvatarIndexInBookmarksPage() {
|
||||
return (currentAvatar && currentAvatar.name !== '' && !pageOfAvatars.isUpdating) ? pageOfAvatars.findAvatarIndex(currentAvatar.name) : -1;
|
||||
}
|
||||
|
||||
property int horizontalSpacing: 18
|
||||
property int verticalSpacing: 44
|
||||
property int thumbnailWidth: 92
|
||||
property int thumbnailHeight: 92
|
||||
|
||||
function selectAvatar(avatar) {
|
||||
emitSendToScript({'method' : 'selectAvatar', 'name' : avatar.name})
|
||||
}
|
||||
|
||||
function deleteAvatar(avatar) {
|
||||
emitSendToScript({'method' : 'deleteAvatar', 'name' : avatar.name})
|
||||
}
|
||||
|
||||
AvatarsModel {
|
||||
id: allAvatars
|
||||
}
|
||||
|
||||
property int itemsPerPage: 8
|
||||
property int totalPages: Math.ceil((allAvatars.count + 1) / itemsPerPage)
|
||||
property int currentPage: 0;
|
||||
onCurrentPageChanged: {
|
||||
currentIndex = Qt.binding(currentAvatarIndexInBookmarksPage);
|
||||
}
|
||||
|
||||
property bool hasNext: currentPage < (totalPages - 1)
|
||||
property bool hasPrev: currentPage > 0
|
||||
|
||||
function setPage(pageIndex) {
|
||||
pageOfAvatars.isUpdating = true;
|
||||
pageOfAvatars.clear();
|
||||
var start = pageIndex * itemsPerPage;
|
||||
var end = Math.min(start + itemsPerPage, allAvatars.count);
|
||||
|
||||
for(var itemIndex = 0; start < end; ++start, ++itemIndex) {
|
||||
var avatarItem = allAvatars.get(start)
|
||||
pageOfAvatars.append(avatarItem);
|
||||
}
|
||||
|
||||
if(pageOfAvatars.count !== itemsPerPage)
|
||||
pageOfAvatars.appendGetAvatars();
|
||||
|
||||
currentPage = pageIndex;
|
||||
pageOfAvatars.isUpdating = false;
|
||||
}
|
||||
|
||||
model: AvatarsModel {
|
||||
id: pageOfAvatars
|
||||
|
||||
property bool isUpdating: false;
|
||||
property var getMoreAvatarsEntry: {'thumbnailUrl' : '', 'name' : '', 'getMoreAvatars' : true}
|
||||
|
||||
function appendGetAvatars() {
|
||||
append(getMoreAvatarsEntry);
|
||||
}
|
||||
|
||||
function hasGetAvatars() {
|
||||
return count != 0 && get(count - 1).getMoreAvatars
|
||||
}
|
||||
|
||||
function removeGetAvatars() {
|
||||
if(hasGetAvatars()) {
|
||||
remove(count - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flow: GridView.FlowLeftToRight
|
||||
|
||||
cellHeight: thumbnailHeight + verticalSpacing
|
||||
cellWidth: thumbnailWidth + horizontalSpacing
|
||||
|
||||
delegate: Item {
|
||||
id: delegateRoot
|
||||
height: GridView.view.cellHeight
|
||||
width: GridView.view.cellWidth
|
||||
|
||||
Item {
|
||||
id: container
|
||||
width: 92
|
||||
height: 92
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hovered"
|
||||
when: favoriteAvatarMouseArea.containsMouse;
|
||||
PropertyChanges { target: favoriteAvatarMouseArea; anchors.bottomMargin: -5 }
|
||||
PropertyChanges { target: container; y: -5 }
|
||||
PropertyChanges { target: favoriteAvatarImage; dropShadowRadius: 10 }
|
||||
PropertyChanges { target: favoriteAvatarImage; dropShadowVerticalOffset: 6 }
|
||||
}
|
||||
]
|
||||
|
||||
property bool highlighted: delegateRoot.GridView.isCurrentItem
|
||||
|
||||
AvatarThumbnail {
|
||||
id: favoriteAvatarImage
|
||||
externalAvatarThumbnailUrl: root.externalAvatarThumbnailUrl
|
||||
avatarUrl: thumbnailUrl
|
||||
border.color: container.highlighted ? style.colors.blueHighlight : 'transparent'
|
||||
border.width: container.highlighted ? 4 : 0
|
||||
wearablesCount: {
|
||||
return !getMoreAvatars ? wearables.count : 0
|
||||
}
|
||||
|
||||
visible: !getMoreAvatars
|
||||
|
||||
MouseArea {
|
||||
id: favoriteAvatarMouseArea
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
enabled: !container.highlighted
|
||||
hoverEnabled: enabled
|
||||
|
||||
onClicked: {
|
||||
if(isInManageState) {
|
||||
var currentItem = delegateRoot.GridView.view.model.get(index);
|
||||
popup.showDeleteFavorite(currentItem.name, function() {
|
||||
view.deleteAvatar(currentItem);
|
||||
});
|
||||
} else {
|
||||
if(delegateRoot.GridView.view.currentIndex !== index) {
|
||||
var currentItem = delegateRoot.GridView.view.model.get(index);
|
||||
popup.showLoadFavorite(currentItem.name, function() {
|
||||
view.selectAvatar(currentItem);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: favoriteAvatarImage
|
||||
color: '#AFAFAF'
|
||||
opacity: 0.4
|
||||
radius: 5
|
||||
visible: isInManageState && !container.highlighted && !getMoreAvatars
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.fill: parent
|
||||
text: "{"
|
||||
visible: isInManageState && !container.highlighted && !getMoreAvatars
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
size: 56
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
width: 92
|
||||
height: 92
|
||||
radius: 5
|
||||
color: style.colors.blueHighlight
|
||||
visible: getMoreAvatars && !isInManageState
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.centerIn: parent
|
||||
|
||||
color: 'white'
|
||||
size: 60
|
||||
text: "K"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
popup.showBuyAvatars(function() {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : 'hifi://BodyMart'})
|
||||
}, function(link) {
|
||||
emitSendToScript({'method' : 'navigate', 'url' : link})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TextStyle7
|
||||
FiraSansRegular {
|
||||
id: text
|
||||
size: 18;
|
||||
lineHeightMode: Text.FixedHeight
|
||||
lineHeight: 16.9;
|
||||
width: view.thumbnailWidth
|
||||
height: view.verticalSpacing
|
||||
elide: Qt.ElideRight
|
||||
anchors.top: container.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.horizontalCenter: container.horizontalCenter
|
||||
verticalAlignment: Text.AlignTop
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
text: getMoreAvatars ? 'Get More Avatars' : name
|
||||
visible: !getMoreAvatars || !isInManageState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
color: 'transparent'
|
||||
|
||||
PageIndicator {
|
||||
x: 1
|
||||
hasNext: view.hasNext
|
||||
hasPrev: view.hasPrev
|
||||
onClicked: view.setPage(view.currentPage - 1)
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
width: 40
|
||||
height: 40
|
||||
color: 'transparent'
|
||||
|
||||
PageIndicator {
|
||||
x: -1
|
||||
isPrevious: false
|
||||
hasNext: view.hasNext
|
||||
hasPrev: view.hasPrev
|
||||
onClicked: view.setPage(view.currentPage + 1)
|
||||
}
|
||||
}
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 20
|
||||
}
|
||||
}
|
||||
|
||||
MessageBoxes {
|
||||
id: popup
|
||||
}
|
||||
|
||||
CreateFavoriteDialog {
|
||||
avatars: allAvatars
|
||||
id: createFavorite
|
||||
}
|
||||
}
|
359
interface/resources/qml/hifi/avatarapp/AdjustWearables.qml
Normal file
|
@ -0,0 +1,359 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
visible: false;
|
||||
width: 480
|
||||
height: 706
|
||||
color: 'white'
|
||||
|
||||
signal wearableUpdated(var id, int index, var properties);
|
||||
signal wearableSelected(var id);
|
||||
signal wearableDeleted(string avatarName, var id);
|
||||
|
||||
signal adjustWearablesOpened(var avatarName);
|
||||
signal adjustWearablesClosed(bool status, var avatarName);
|
||||
|
||||
property bool modified: false;
|
||||
Component.onCompleted: {
|
||||
modified = false;
|
||||
}
|
||||
|
||||
property var jointNames;
|
||||
property string avatarName: ''
|
||||
property var wearablesModel;
|
||||
|
||||
function open(avatar) {
|
||||
adjustWearablesOpened(avatar.name);
|
||||
|
||||
visible = true;
|
||||
avatarName = avatar.name;
|
||||
wearablesModel = avatar.wearables;
|
||||
refresh(avatar);
|
||||
}
|
||||
|
||||
function refresh(avatar) {
|
||||
wearablesCombobox.model.clear();
|
||||
for(var i = 0; i < avatar.wearables.count; ++i) {
|
||||
var wearable = avatar.wearables.get(i).properties;
|
||||
for(var j = (wearable.modelURL.length - 1); j >= 0; --j) {
|
||||
if(wearable.modelURL[j] === '/') {
|
||||
wearable.text = wearable.modelURL.substring(j + 1) + ' [%jointIndex%]'.replace('%jointIndex%', jointNames[wearable.parentJointIndex]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
wearablesCombobox.model.append(wearable);
|
||||
}
|
||||
|
||||
wearablesCombobox.currentIndex = 0;
|
||||
}
|
||||
|
||||
function refreshWearable(wearableID, wearableIndex, properties, updateUI) {
|
||||
if(wearableIndex === -1) {
|
||||
wearableIndex = wearablesCombobox.model.findIndexById(wearableID);
|
||||
}
|
||||
|
||||
var wearable = wearablesCombobox.model.get(wearableIndex);
|
||||
|
||||
if(!wearable) {
|
||||
return;
|
||||
}
|
||||
|
||||
var wearableModelItemProperties = wearablesModel.get(wearableIndex).properties;
|
||||
|
||||
for(var prop in properties) {
|
||||
wearable[prop] = properties[prop];
|
||||
wearableModelItemProperties[prop] = wearable[prop];
|
||||
|
||||
if(updateUI) {
|
||||
if(prop === 'localPosition') {
|
||||
position.set(wearable[prop]);
|
||||
} else if(prop === 'localRotationAngles') {
|
||||
rotation.set(wearable[prop]);
|
||||
} else if(prop === 'dimensions') {
|
||||
scalespinner.set(wearable[prop].x / wearable.naturalDimensions.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wearablesModel.setProperty(wearableIndex, 'properties', wearableModelItemProperties);
|
||||
}
|
||||
|
||||
function getCurrentWearable() {
|
||||
return wearablesCombobox.model.get(wearablesCombobox.currentIndex)
|
||||
}
|
||||
|
||||
function selectWearableByID(entityID) {
|
||||
for(var i = 0; i < wearablesCombobox.model.count; ++i) {
|
||||
var wearable = wearablesCombobox.model.get(i);
|
||||
if(wearable.id === entityID) {
|
||||
wearablesCombobox.currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function close(status) {
|
||||
visible = false;
|
||||
adjustWearablesClosed(status, avatarName);
|
||||
}
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
spacing: 20
|
||||
width: parent.width - 30 * 2
|
||||
|
||||
HifiControlsUit.ComboBox {
|
||||
id: wearablesCombobox
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
comboBox.textRole: "text"
|
||||
|
||||
model: ListModel {
|
||||
function findIndexById(id) {
|
||||
|
||||
for(var i = 0; i < count; ++i) {
|
||||
var wearable = get(i);
|
||||
if(wearable.id === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
comboBox.onCurrentIndexChanged: {
|
||||
var currentWearable = getCurrentWearable();
|
||||
|
||||
if(currentWearable) {
|
||||
position.set(currentWearable.localPosition);
|
||||
rotation.set(currentWearable.localRotationAngles);
|
||||
scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x)
|
||||
|
||||
wearableSelected(currentWearable.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 5
|
||||
|
||||
Row {
|
||||
spacing: 20
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
id: positionLabel
|
||||
size: 22;
|
||||
text: "Position"
|
||||
}
|
||||
|
||||
// TextStyle7
|
||||
FiraSansRegular {
|
||||
size: 18;
|
||||
lineHeightMode: Text.FixedHeight
|
||||
lineHeight: 16.9;
|
||||
text: "m"
|
||||
anchors.verticalCenter: positionLabel.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 {
|
||||
id: position
|
||||
backgroundColor: "lightgray"
|
||||
|
||||
function set(localPosition) {
|
||||
notify = false;
|
||||
xvalue = localPosition.x
|
||||
yvalue = localPosition.y
|
||||
zvalue = localPosition.z
|
||||
notify = true;
|
||||
}
|
||||
|
||||
function notifyPositionChanged() {
|
||||
modified = true;
|
||||
var properties = {
|
||||
localPosition: { 'x' : xvalue, 'y' : yvalue, 'z' : zvalue }
|
||||
};
|
||||
|
||||
wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties);
|
||||
}
|
||||
|
||||
property bool notify: false;
|
||||
|
||||
onXvalueChanged: if(notify) notifyPositionChanged();
|
||||
onYvalueChanged: if(notify) notifyPositionChanged();
|
||||
onZvalueChanged: if(notify) notifyPositionChanged();
|
||||
|
||||
decimals: 2
|
||||
realFrom: -10
|
||||
realTo: 10
|
||||
realStepSize: 0.01
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 5
|
||||
|
||||
Row {
|
||||
spacing: 20
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
id: rotationLabel
|
||||
size: 22;
|
||||
text: "Rotation"
|
||||
}
|
||||
|
||||
// TextStyle7
|
||||
FiraSansRegular {
|
||||
size: 18;
|
||||
lineHeightMode: Text.FixedHeight
|
||||
lineHeight: 16.9;
|
||||
text: "deg"
|
||||
anchors.verticalCenter: rotationLabel.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 {
|
||||
id: rotation
|
||||
backgroundColor: "lightgray"
|
||||
|
||||
function set(localRotationAngles) {
|
||||
notify = false;
|
||||
xvalue = localRotationAngles.x
|
||||
yvalue = localRotationAngles.y
|
||||
zvalue = localRotationAngles.z
|
||||
notify = true;
|
||||
}
|
||||
|
||||
function notifyRotationChanged() {
|
||||
modified = true;
|
||||
var properties = {
|
||||
localRotationAngles: { 'x' : xvalue, 'y' : yvalue, 'z' : zvalue }
|
||||
};
|
||||
|
||||
wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties);
|
||||
}
|
||||
|
||||
property bool notify: false;
|
||||
|
||||
onXvalueChanged: if(notify) notifyRotationChanged();
|
||||
onYvalueChanged: if(notify) notifyRotationChanged();
|
||||
onZvalueChanged: if(notify) notifyRotationChanged();
|
||||
|
||||
decimals: 0
|
||||
realFrom: -180
|
||||
realTo: 180
|
||||
realStepSize: 1
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 5
|
||||
|
||||
// TextStyle5
|
||||
FiraSansSemiBold {
|
||||
size: 22;
|
||||
text: "Scale"
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: scalespinner
|
||||
decimals: 2
|
||||
realStepSize: 0.1
|
||||
realFrom: 0.1
|
||||
realTo: 3.0
|
||||
realValue: 1.0
|
||||
backgroundColor: "lightgray"
|
||||
width: position.spinboxWidth
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
|
||||
property bool notify: false;
|
||||
onValueChanged: if(notify) notifyScaleChanged();
|
||||
|
||||
function set(value) {
|
||||
notify = false;
|
||||
realValue = value
|
||||
notify = true;
|
||||
}
|
||||
|
||||
function notifyScaleChanged() {
|
||||
modified = true;
|
||||
var currentWearable = getCurrentWearable();
|
||||
var naturalDimensions = currentWearable.naturalDimensions;
|
||||
|
||||
var properties = {
|
||||
dimensions: {
|
||||
'x' : realValue * naturalDimensions.x,
|
||||
'y' : realValue * naturalDimensions.y,
|
||||
'z' : realValue * naturalDimensions.z
|
||||
}
|
||||
};
|
||||
|
||||
wearableUpdated(currentWearable.id, wearablesCombobox.currentIndex, properties);
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Button {
|
||||
fontSize: 18
|
||||
height: 40
|
||||
anchors.right: parent.right
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
text: "TAKE IT OFF"
|
||||
onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id);
|
||||
enabled: wearablesCombobox.model.count !== 0
|
||||
anchors.verticalCenter: scalespinner.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
yesText: "SAVE"
|
||||
noText: "CANCEL"
|
||||
|
||||
onYesClicked: function() {
|
||||
root.close(true);
|
||||
}
|
||||
|
||||
onNoClicked: function() {
|
||||
root.close(false);
|
||||
}
|
||||
}
|
||||
}
|
58
interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml
Normal file
|
@ -0,0 +1,58 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
|
||||
ShadowRectangle {
|
||||
id: header
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 60
|
||||
|
||||
property alias pageTitle: title.text
|
||||
property alias avatarIconVisible: avatarIcon.visible
|
||||
property alias settingsButtonVisible: settingsButton.visible
|
||||
|
||||
signal settingsClicked;
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
color: style.colors.lightGrayBackground
|
||||
|
||||
HiFiGlyphs {
|
||||
id: avatarIcon
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 23
|
||||
anchors.verticalCenter: header.verticalCenter
|
||||
|
||||
size: 38
|
||||
text: "<"
|
||||
}
|
||||
|
||||
// TextStyle6
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 22;
|
||||
anchors.left: avatarIcon.visible ? avatarIcon.right : avatarIcon.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: avatarIcon.verticalCenter
|
||||
text: 'Avatar'
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: settingsButton
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
anchors.verticalCenter: avatarIcon.verticalCenter
|
||||
text: "&"
|
||||
|
||||
MouseArea {
|
||||
id: settingsMouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
settingsClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// HiFiConstants.qml
|
||||
//
|
||||
// Created by Alexander Ivash on 17 Apr 2018
|
||||
// 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
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Window 2.2
|
||||
import "../../styles-uit"
|
||||
|
||||
QtObject {
|
||||
readonly property QtObject colors: QtObject {
|
||||
readonly property color lightGrayBackground: "#f2f2f2"
|
||||
readonly property color black: "#000000"
|
||||
readonly property color white: "#ffffff"
|
||||
readonly property color blueHighlight: "#00b4ef"
|
||||
readonly property color inputFieldBackground: "#d4d4d4"
|
||||
readonly property color yellowishOrange: "#ffb017"
|
||||
readonly property color blueAccent: "#0093c5"
|
||||
readonly property color greenHighlight: "#1fc6a6"
|
||||
readonly property color lightGray: "#afafaf"
|
||||
readonly property color redHighlight: "#ea4c5f"
|
||||
readonly property color orangeAccent: "#ff6309"
|
||||
}
|
||||
}
|
73
interface/resources/qml/hifi/avatarapp/AvatarThumbnail.qml
Normal file
|
@ -0,0 +1,73 @@
|
|||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
width: 92
|
||||
height: 92
|
||||
property alias wearableIndicator: indicator
|
||||
|
||||
property int wearablesCount: 0
|
||||
onWearablesCountChanged: {
|
||||
console.debug('AvatarThumbnail: wearablesCount = ', wearablesCount)
|
||||
}
|
||||
|
||||
property alias dropShadowRadius: avatarImage.dropShadowRadius
|
||||
property alias dropShadowHorizontalOffset: avatarImage.dropShadowHorizontalOffset
|
||||
property alias dropShadowVerticalOffset: avatarImage.dropShadowVerticalOffset
|
||||
|
||||
property url externalAvatarThumbnailUrl;
|
||||
property var avatarUrl;
|
||||
property alias border: avatarImage.border
|
||||
|
||||
ShadowImage {
|
||||
id: avatarImage
|
||||
anchors.fill: parent
|
||||
radius: 5
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
Binding on source {
|
||||
when: avatarUrl !== ''
|
||||
value: avatarUrl
|
||||
}
|
||||
onSourceChanged: {
|
||||
console.debug('avatarImage: source = ', source);
|
||||
}
|
||||
|
||||
visible: avatarImage.status !== Image.Loading && avatarImage.status !== Image.Error
|
||||
}
|
||||
|
||||
ShadowImage {
|
||||
id: customAvatarImage
|
||||
anchors.fill: avatarImage;
|
||||
visible: avatarUrl === '' || avatarImage.status === Image.Error
|
||||
source: externalAvatarThumbnailUrl
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
anchors.fill: parent;
|
||||
color: 'white'
|
||||
visible: avatarImage.status === Image.Loading
|
||||
radius: avatarImage.radius
|
||||
border.width: avatarImage.border.width
|
||||
border.color: avatarImage.border.color
|
||||
|
||||
dropShadowRadius: avatarImage.dropShadowRadius;
|
||||
dropShadowHorizontalOffset: avatarImage.dropShadowHorizontalOffset
|
||||
dropShadowVerticalOffset: avatarImage.dropShadowVerticalOffset
|
||||
|
||||
Spinner {
|
||||
id: spinner
|
||||
visible: parent.visible
|
||||
anchors.fill: parent;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarWearablesIndicator {
|
||||
id: indicator
|
||||
anchors.left: avatarImage.left
|
||||
anchors.bottom: avatarImage.bottom
|
||||
anchors.leftMargin: 57
|
||||
wearablesCount: parent.wearablesCount
|
||||
visible: parent.wearablesCount !== 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import QtQuick 2.9
|
||||
import "../../controls-uit"
|
||||
import "../../styles-uit"
|
||||
|
||||
ShadowRectangle {
|
||||
property int wearablesCount: 0
|
||||
|
||||
dropShadowRadius: 4
|
||||
dropShadowHorizontalOffset: 0
|
||||
dropShadowVerticalOffset: 0
|
||||
|
||||
width: 46.5
|
||||
height: 46.5
|
||||
radius: width / 2
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
color: style.colors.greenHighlight
|
||||
|
||||
HiFiGlyphs {
|
||||
width: 26.5
|
||||
height: 13.8
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "\ue02e"
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 46.57
|
||||
height: 23
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 2.76
|
||||
|
||||
// TextStyle2
|
||||
RalewayBold {
|
||||
size: 15;
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: wearablesCount
|
||||
}
|
||||
}
|
||||
}
|
228
interface/resources/qml/hifi/avatarapp/AvatarsModel.qml
Normal file
|
@ -0,0 +1,228 @@
|
|||
import QtQuick 2.9
|
||||
|
||||
ListModel {
|
||||
id: model
|
||||
function extractMarketId(avatarUrl) {
|
||||
|
||||
var guidRegexp = '([A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12})';
|
||||
|
||||
var regexp = new RegExp(guidRegexp,["i"]);
|
||||
var match = regexp.exec(avatarUrl);
|
||||
if (match !== null) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function makeMarketItemUrl(avatarUrl) {
|
||||
var marketItemUrl = "https://highfidelity.com/marketplace/items/%marketId%"
|
||||
.split('%marketId%').join(extractMarketId(avatarUrl));
|
||||
|
||||
return marketItemUrl;
|
||||
}
|
||||
|
||||
function makeThumbnailUrl(avatarUrl) {
|
||||
var marketId = extractMarketId(avatarUrl);
|
||||
if(marketId === '')
|
||||
return '';
|
||||
|
||||
var avatarThumbnailUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/%marketId%/large/hifi-mp-%marketId%.jpg"
|
||||
.split('%marketId%').join(marketId);
|
||||
|
||||
return avatarThumbnailUrl;
|
||||
}
|
||||
|
||||
function makeAvatarObject(avatar, avatarName) {
|
||||
var avatarThumbnailUrl = makeThumbnailUrl(avatar.avatarUrl);
|
||||
|
||||
return {
|
||||
'name' : avatarName,
|
||||
'avatarScale' : avatar.avatarScale,
|
||||
'thumbnailUrl' : avatarThumbnailUrl,
|
||||
'avatarUrl' : avatar.avatarUrl,
|
||||
'wearables' : avatar.avatarEntites ? avatar.avatarEntites : [],
|
||||
'attachments' : avatar.attachments ? avatar.attachments : [],
|
||||
'entry' : avatar,
|
||||
'getMoreAvatars' : false
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function addAvatarEntry(avatar, avatarName) {
|
||||
var avatarEntry = makeAvatarObject(avatar, avatarName);
|
||||
append(avatarEntry);
|
||||
|
||||
return allAvatars.count - 1;
|
||||
}
|
||||
|
||||
function populate(bookmarks) {
|
||||
clear();
|
||||
for(var avatarName in bookmarks) {
|
||||
var avatar = bookmarks[avatarName];
|
||||
var avatarEntry = makeAvatarObject(avatar, avatarName);
|
||||
|
||||
append(avatarEntry);
|
||||
}
|
||||
}
|
||||
|
||||
function arraysAreEqual(a1, a2, comparer) {
|
||||
if(Array.isArray(a1) && Array.isArray(a2)) {
|
||||
if(a1.length !== a2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(var i = 0; i < a1.length; ++i) {
|
||||
if(!comparer(a1[i], a2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if(Array.isArray(a1)) {
|
||||
return a1.length === 0;
|
||||
} else if(Array.isArray(a2)) {
|
||||
return a2.length === 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function modelsAreEqual(m1, m2, comparer) {
|
||||
if(m1.count !== m2.count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(var i = 0; i < m1.count; ++i) {
|
||||
var e1 = m1.get(i);
|
||||
|
||||
var allDifferent = true;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function compareNumericObjects(o1, o2) {
|
||||
if(o1 === undefined && o2 !== undefined)
|
||||
return false;
|
||||
if(o1 !== undefined && o2 === undefined)
|
||||
return false;
|
||||
|
||||
for(var prop in o1) {
|
||||
if(o1.hasOwnProperty(prop) && o2.hasOwnProperty(prop)) {
|
||||
var v1 = o1[prop];
|
||||
var v2 = o2[prop];
|
||||
|
||||
|
||||
if(v1 !== v2 && Math.round(v1 * 500) != Math.round(v2 * 500)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function compareObjects(o1, o2, props, arrayProp) {
|
||||
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];
|
||||
|
||||
if(comparer) {
|
||||
if(comparer(o1Value, o2Value) === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(JSON.stringify(o1Value) !== JSON.stringify(o2Value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function compareWearables(w1, w2) {
|
||||
return compareObjects(w1, w2, [{'propertyName' : 'modelURL'},
|
||||
{'propertyName' : 'parentJointIndex'},
|
||||
{'propertyName' : 'marketplaceID'},
|
||||
{'propertyName' : 'itemName'},
|
||||
{'propertyName' : 'script'},
|
||||
{'propertyName' : 'localPosition', 'comparer' : compareNumericObjects},
|
||||
{'propertyName' : 'localRotationAngles', 'comparer' : compareNumericObjects},
|
||||
{'propertyName' : 'dimensions', 'comparer' : compareNumericObjects}], 'properties')
|
||||
}
|
||||
|
||||
function compareAttachments(a1, a2) {
|
||||
return compareObjects(a1, a2, [{'propertyName' : 'position', 'comparer' : compareNumericObjects},
|
||||
{'propertyName' : 'orientation'},
|
||||
{'propertyName' : 'parentJointIndex'},
|
||||
{'propertyName' : 'modelurl'}])
|
||||
}
|
||||
|
||||
function findAvatarIndexByValue(avatar) {
|
||||
|
||||
var index = -1;
|
||||
|
||||
// 2DO: find better way of determining selected avatar in bookmarks
|
||||
for(var i = 0; i < allAvatars.count; ++i) {
|
||||
var thesame = true;
|
||||
var bookmarkedAvatar = allAvatars.get(i);
|
||||
|
||||
if(bookmarkedAvatar.avatarUrl !== avatar.avatarUrl)
|
||||
continue;
|
||||
|
||||
if(bookmarkedAvatar.avatarScale !== avatar.avatarScale)
|
||||
continue;
|
||||
|
||||
if(!modelsAreEqual(bookmarkedAvatar.attachments, avatar.attachments, compareAttachments)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(thesame) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
function findAvatarIndex(avatarName) {
|
||||
for(var i = 0; i < count; ++i) {
|
||||
if(get(i).name === avatarName) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function findAvatar(avatarName) {
|
||||
var avatarIndex = findAvatarIndex(avatarName);
|
||||
if(avatarIndex === -1)
|
||||
return undefined;
|
||||
|
||||
return get(avatarIndex);
|
||||
}
|
||||
|
||||
}
|
15
interface/resources/qml/hifi/avatarapp/BlueButton.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
|
||||
HifiControlsUit.Button {
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
width: Math.max(hifi.dimensions.buttonWidth, implicitTextWidth + 20)
|
||||
fontSize: 18
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
height: 40
|
||||
}
|
184
interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml
Normal file
|
@ -0,0 +1,184 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
visible: false;
|
||||
anchors.fill: parent;
|
||||
color: Qt.rgba(0, 0, 0, 0.5);
|
||||
z: 999;
|
||||
|
||||
property string titleText: 'Create Favorite'
|
||||
property string favoriteNameText: favoriteName.text
|
||||
property string avatarImageUrl: null
|
||||
property int wearablesCount: 0
|
||||
|
||||
property string button1color: hifi.buttons.noneBorderlessGray;
|
||||
property string button1text: 'CANCEL'
|
||||
property string button2color: hifi.buttons.blue;
|
||||
property string button2text: 'CONFIRM'
|
||||
|
||||
property var avatars;
|
||||
property var onSaveClicked;
|
||||
property var onCancelClicked;
|
||||
|
||||
function open(wearables, thumbnail) {
|
||||
favoriteName.text = '';
|
||||
favoriteName.forceActiveFocus();
|
||||
|
||||
avatarImageUrl = thumbnail;
|
||||
wearablesCount = wearables;
|
||||
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
console.debug('closing');
|
||||
visible = false;
|
||||
}
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mainContainer;
|
||||
width: Math.max(parent.width * 0.8, 400)
|
||||
property int margin: 30;
|
||||
|
||||
height: childrenRect.height + margin * 2
|
||||
onHeightChanged: {
|
||||
console.debug('mainContainer: height = ', height)
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
color: "white"
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 24;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 30
|
||||
anchors.leftMargin: 30
|
||||
anchors.rightMargin: 30
|
||||
|
||||
text: root.titleText
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentContainer
|
||||
width: parent.width - 50
|
||||
height: childrenRect.height
|
||||
|
||||
anchors.top: title.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
|
||||
AvatarThumbnail {
|
||||
id: avatarThumbnail
|
||||
avatarUrl: avatarImageUrl
|
||||
onAvatarUrlChanged: {
|
||||
console.debug('CreateFavoritesDialog: onAvatarUrlChanged: ', avatarUrl);
|
||||
}
|
||||
|
||||
wearablesCount: avatarWearablesCount
|
||||
}
|
||||
|
||||
InputTextStyle4 {
|
||||
id: favoriteName
|
||||
anchors.right: parent.right
|
||||
height: 40
|
||||
anchors.left: avatarThumbnail.right
|
||||
anchors.leftMargin: 44
|
||||
anchors.verticalCenter: avatarThumbnail.verticalCenter
|
||||
placeholderText: "Enter Favorite Name"
|
||||
|
||||
RalewayRegular {
|
||||
id: wrongName
|
||||
anchors.top: parent.bottom;
|
||||
anchors.topMargin: 2
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: -contentContainer.anchors.rightMargin // allow text to render beyond favorite input text
|
||||
wrapMode: Text.WordWrap
|
||||
text: 'Favorite name exists. Overwrite existing favorite?'
|
||||
size: 15
|
||||
color: 'red'
|
||||
visible: {
|
||||
for(var i = 0; i < avatars.count; ++i) {
|
||||
var avatarName = avatars.get(i).name;
|
||||
if(avatarName === favoriteName.text) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
anchors.top: contentContainer.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
yesButton.enabled: favoriteNameText !== ''
|
||||
yesText: root.button2text
|
||||
noText: root.button1text
|
||||
|
||||
Binding on yesButton.text {
|
||||
when: wrongName.visible
|
||||
value: "OVERWRITE";
|
||||
}
|
||||
|
||||
Binding on yesButton.color {
|
||||
when: wrongName.visible
|
||||
value: hifi.buttons.red;
|
||||
}
|
||||
|
||||
Binding on yesButton.colorScheme {
|
||||
when: wrongName.visible
|
||||
value: hifi.colorSchemes.dark;
|
||||
}
|
||||
|
||||
onYesClicked: function() {
|
||||
if(onSaveClicked) {
|
||||
onSaveClicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
onNoClicked: function() {
|
||||
if(onCancelClicked) {
|
||||
onCancelClicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
interface/resources/qml/hifi/avatarapp/DialogButtons.qml
Normal file
|
@ -0,0 +1,41 @@
|
|||
import QtQuick 2.9
|
||||
|
||||
Row {
|
||||
id: root
|
||||
property string yesText;
|
||||
property string noText;
|
||||
property var onYesClicked;
|
||||
property var onNoClicked;
|
||||
|
||||
property alias yesButton: yesButton
|
||||
property alias noButton: noButton
|
||||
|
||||
height: childrenRect.height
|
||||
layoutDirection: Qt.RightToLeft
|
||||
|
||||
spacing: 30
|
||||
|
||||
BlueButton {
|
||||
id: yesButton;
|
||||
text: yesText;
|
||||
onClicked: {
|
||||
console.debug('bluebutton.clicked', onYesClicked);
|
||||
|
||||
if(onYesClicked) {
|
||||
onYesClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WhiteButton {
|
||||
id: noButton
|
||||
text: noText;
|
||||
onClicked: {
|
||||
console.debug('whitebutton.clicked', onNoClicked);
|
||||
|
||||
if(onNoClicked) {
|
||||
onNoClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
interface/resources/qml/hifi/avatarapp/InputField.qml
Normal file
|
@ -0,0 +1,48 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.2
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
|
||||
property bool error: false;
|
||||
text: 'ThisIsDisplayName'
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hovered"
|
||||
when: textField.hovered && !textField.focus && !textField.error;
|
||||
PropertyChanges { target: background; color: '#afafaf' }
|
||||
},
|
||||
State {
|
||||
name: "focused"
|
||||
when: textField.focus && !textField.error
|
||||
PropertyChanges { target: background; color: '#f2f2f2' }
|
||||
PropertyChanges { target: background; border.color: '#00b4ef' }
|
||||
},
|
||||
State {
|
||||
name: "error"
|
||||
when: textField.error
|
||||
PropertyChanges { target: background; color: '#f2f2f2' }
|
||||
PropertyChanges { target: background; border.color: '#e84e62' }
|
||||
}
|
||||
]
|
||||
|
||||
background: Rectangle {
|
||||
id: background
|
||||
implicitWidth: 200
|
||||
implicitHeight: 40
|
||||
color: '#d4d4d4'
|
||||
border.color: '#afafaf'
|
||||
border.width: 1
|
||||
radius: 2
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 36
|
||||
text: "\ue00d"
|
||||
}
|
||||
}
|
16
interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml
Normal file
|
@ -0,0 +1,16 @@
|
|||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../styles-uit"
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: control
|
||||
font.family: "Fira Sans"
|
||||
font.pixelSize: 15;
|
||||
implicitHeight: 40
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
}
|
184
interface/resources/qml/hifi/avatarapp/MessageBox.qml
Normal file
|
@ -0,0 +1,184 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
visible: false;
|
||||
anchors.fill: parent;
|
||||
color: Qt.rgba(0, 0, 0, 0.5);
|
||||
z: 999;
|
||||
|
||||
property string titleText: ''
|
||||
property string bodyText: ''
|
||||
property alias inputText: input;
|
||||
|
||||
property string imageSource: null
|
||||
onImageSourceChanged: {
|
||||
console.debug('imageSource = ', imageSource)
|
||||
}
|
||||
|
||||
property string button1color: hifi.buttons.noneBorderlessGray;
|
||||
property string button1text: ''
|
||||
property string button2color: hifi.buttons.blue;
|
||||
property string button2text: ''
|
||||
|
||||
property var onButton2Clicked;
|
||||
property var onButton1Clicked;
|
||||
property var onLinkClicked;
|
||||
|
||||
function open() {
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function close() {
|
||||
visible = false;
|
||||
|
||||
onButton1Clicked = null;
|
||||
onButton2Clicked = null;
|
||||
button1text = '';
|
||||
button2text = '';
|
||||
imageSource = null;
|
||||
inputText.visible = false;
|
||||
inputText.placeholderText = '';
|
||||
inputText.text = '';
|
||||
}
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mainContainer;
|
||||
width: Math.max(parent.width * 0.8, 400)
|
||||
property int margin: 30;
|
||||
|
||||
height: childrenRect.height + margin * 2
|
||||
onHeightChanged: {
|
||||
console.debug('mainContainer: height = ', height)
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
color: "white"
|
||||
|
||||
// TextStyle1
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 24;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 30
|
||||
anchors.leftMargin: 30
|
||||
anchors.rightMargin: 30
|
||||
|
||||
text: root.titleText
|
||||
elide: Qt.ElideRight
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentContainer
|
||||
spacing: 15
|
||||
|
||||
anchors.top: title.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 30;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
|
||||
InputTextStyle4 {
|
||||
id: input
|
||||
visible: false
|
||||
height: visible ? implicitHeight : 0
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
}
|
||||
|
||||
// TextStyle3
|
||||
RalewayRegular {
|
||||
id: body
|
||||
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
size: 18
|
||||
text: root.bodyText;
|
||||
linkColor: style.colors.blueHighlight
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: paintedHeight;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
wrapMode: Text.WordWrap;
|
||||
|
||||
onLinkActivated: {
|
||||
if(onLinkClicked)
|
||||
onLinkClicked(link);
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
Binding on height {
|
||||
when: imageSource === null
|
||||
value: 0
|
||||
}
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
Binding on source {
|
||||
when: imageSource !== null
|
||||
value: imageSource
|
||||
}
|
||||
|
||||
visible: imageSource !== null ? true : false
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
id: buttons
|
||||
|
||||
anchors.top: contentContainer.bottom
|
||||
anchors.topMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
yesButton.enabled: !input.visible || input.text.length !== 0
|
||||
yesText: root.button2text
|
||||
noText: root.button1text
|
||||
|
||||
onYesClicked: function() {
|
||||
if(onButton2Clicked) {
|
||||
onButton2Clicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
onNoClicked: function() {
|
||||
if(onButton1Clicked) {
|
||||
onButton1Clicked();
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
122
interface/resources/qml/hifi/avatarapp/MessageBoxes.qml
Normal file
|
@ -0,0 +1,122 @@
|
|||
import QtQuick 2.5
|
||||
|
||||
MessageBox {
|
||||
id: popup
|
||||
|
||||
function showSpecifyAvatarUrl(callback, linkCallback) {
|
||||
popup.onButton2Clicked = callback;
|
||||
popup.titleText = 'Specify Avatar URL'
|
||||
popup.bodyText = 'This will not overwrite your existing favorite if you are wearing one.<br>' +
|
||||
'<a href="https://docs.highfidelity.com/create-and-explore/avatars/create-avatars">' +
|
||||
'Learn to make a custom avatar by opening this link on your desktop.' +
|
||||
'</a>'
|
||||
popup.inputText.visible = true;
|
||||
popup.inputText.placeholderText = 'Enter Avatar Url';
|
||||
popup.button1text = 'CANCEL';
|
||||
popup.button2text = 'CONFIRM';
|
||||
|
||||
popup.onButton2Clicked = function() {
|
||||
if(callback)
|
||||
callback();
|
||||
|
||||
popup.close();
|
||||
}
|
||||
|
||||
popup.onLinkClicked = function(link) {
|
||||
if(linkCallback)
|
||||
linkCallback(link);
|
||||
}
|
||||
|
||||
popup.open();
|
||||
popup.inputText.forceActiveFocus();
|
||||
}
|
||||
|
||||
property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg'
|
||||
|
||||
function showGetWearables(callback, linkCallback) {
|
||||
popup.button2text = 'AvatarIsland'
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Get Wearables'
|
||||
popup.bodyText = 'Buy wearables from <a href="app://marketplace">Marketplace</a>' + '<br/>' +
|
||||
'Wear wearables from <a href="app://purchases">My Purchases</a>' + '<br/>' +
|
||||
'You can visit the domain “AvatarIsland” to get wearables'
|
||||
|
||||
popup.imageSource = getWearablesUrl;
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
|
||||
popup.onLinkClicked = function(link) {
|
||||
popup.close();
|
||||
|
||||
if(linkCallback)
|
||||
linkCallback(link);
|
||||
}
|
||||
|
||||
popup.open();
|
||||
}
|
||||
|
||||
function showDeleteFavorite(favoriteName, callback) {
|
||||
popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName)
|
||||
popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.'
|
||||
popup.imageSource = null;
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.button2text = 'DELETE'
|
||||
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
popup.open();
|
||||
}
|
||||
|
||||
function showLoadFavorite(favoriteName, callback) {
|
||||
popup.button2text = 'CONFIRM'
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Load Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName)
|
||||
popup.bodyText = 'This will switch your current avatar and wearables that you are wearing with a new avatar and wearables.'
|
||||
popup.imageSource = null;
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
popup.open();
|
||||
}
|
||||
|
||||
property url getAvatarsUrl: '../../../images/avatarapp/BodyMart.PNG'
|
||||
|
||||
function showBuyAvatars(callback, linkCallback) {
|
||||
popup.button2text = 'BodyMart'
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Get Avatars'
|
||||
|
||||
popup.bodyText = 'Buy avatars from <a href="app://marketplace">Marketplace</a>' + '<br/>' +
|
||||
'Wear avatars from <a href="app://purchases">My Purchases</a>' + '<br/>' +
|
||||
'You can visit the domain “BodyMart” to get avatars'
|
||||
|
||||
popup.imageSource = getAvatarsUrl;
|
||||
popup.onButton2Clicked = function() {
|
||||
popup.close();
|
||||
|
||||
if(callback)
|
||||
callback();
|
||||
}
|
||||
|
||||
popup.onLinkClicked = function(link) {
|
||||
popup.close();
|
||||
|
||||
if(linkCallback)
|
||||
linkCallback(link);
|
||||
}
|
||||
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
|
44
interface/resources/qml/hifi/avatarapp/PageIndicator.qml
Normal file
|
@ -0,0 +1,44 @@
|
|||
import QtQuick 2.9
|
||||
|
||||
ShadowGlyph {
|
||||
id: indicator
|
||||
property bool isPrevious: true;
|
||||
property bool hasNext: false
|
||||
property bool hasPrev: false
|
||||
property bool isEnabled: isPrevious ? hasPrev : hasNext
|
||||
signal clicked;
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hovered"
|
||||
when: pageIndicatorMouseArea.containsMouse;
|
||||
PropertyChanges { target: pageIndicatorMouseArea; anchors.bottomMargin: -5 }
|
||||
PropertyChanges { target: indicator; y: -5 }
|
||||
PropertyChanges { target: indicator; dropShadowVerticalOffset: 9 }
|
||||
}
|
||||
]
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
text: isPrevious ? "E" : "D";
|
||||
width: 40
|
||||
height: 40
|
||||
font.pixelSize: 100
|
||||
color: isEnabled ? 'black' : 'gray'
|
||||
visible: hasNext || hasPrev
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
MouseArea {
|
||||
id: pageIndicatorMouseArea
|
||||
anchors.fill: parent
|
||||
enabled: isEnabled
|
||||
hoverEnabled: enabled
|
||||
onClicked: {
|
||||
parent.clicked();
|
||||
}
|
||||
}
|
||||
}
|
36
interface/resources/qml/hifi/avatarapp/RoundImage.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
|
||||
property alias border: borderRectangle.border
|
||||
property alias source: image.source
|
||||
property alias fillMode: image.fillMode
|
||||
property alias radius: mask.radius
|
||||
property alias status: image.status
|
||||
property alias progress: image.progress
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
anchors.margins: borderRectangle.border.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mask
|
||||
anchors.fill: image
|
||||
}
|
||||
|
||||
TransparencyMask {
|
||||
anchors.fill: image
|
||||
source: image
|
||||
maskSource: mask
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: borderRectangle
|
||||
anchors.fill: parent
|
||||
|
||||
radius: mask.radius
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
353
interface/resources/qml/hifi/avatarapp/Settings.qml
Normal file
|
@ -0,0 +1,353 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: 'white'
|
||||
visible: false;
|
||||
|
||||
signal scaleChanged(real scale);
|
||||
|
||||
property alias onSaveClicked: dialogButtons.onYesClicked
|
||||
property alias onCancelClicked: dialogButtons.onNoClicked
|
||||
|
||||
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
|
||||
|
||||
property real avatarScaleBackup;
|
||||
function open(settings, avatarScale) {
|
||||
console.debug('Settings.qml: open: ', JSON.stringify(settings, 0, 4));
|
||||
avatarScaleBackup = avatarScale;
|
||||
|
||||
scaleSlider.notify = false;
|
||||
scaleSlider.value = Math.round(avatarScale * 10);
|
||||
scaleSlider.notify = true;;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function close() {
|
||||
visible = false
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 27
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 25
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 32
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 57
|
||||
|
||||
RowLayout {
|
||||
id: avatarScaleRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 17
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
text: "Avatar Scale"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
HiFiGlyphs {
|
||||
size: 30
|
||||
text: 'T'
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
HifiControlsUit.Slider {
|
||||
id: scaleSlider
|
||||
property bool notify: false;
|
||||
|
||||
from: 1
|
||||
to: 40
|
||||
|
||||
onValueChanged: {
|
||||
console.debug('value changed: ', value);
|
||||
if(notify) {
|
||||
console.debug('notifying.. ');
|
||||
root.scaleChanged(value / 10);
|
||||
}
|
||||
}
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
anchors.left: scaleSlider.left
|
||||
anchors.leftMargin: 5
|
||||
anchors.top: scaleSlider.bottom
|
||||
anchors.topMargin: 2
|
||||
text: String(scaleSlider.from / 10) + 'x'
|
||||
}
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
anchors.right: scaleSlider.right
|
||||
anchors.rightMargin: 5
|
||||
anchors.top: scaleSlider.bottom
|
||||
anchors.topMargin: 2
|
||||
text: String(scaleSlider.to / 10) + 'x'
|
||||
}
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
size: 40
|
||||
text: 'T'
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
ShadowRectangle {
|
||||
width: 37
|
||||
height: 28
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: style.colors.blueHighlight }
|
||||
GradientStop { position: 1.0; color: style.colors.blueAccent }
|
||||
}
|
||||
|
||||
radius: 3
|
||||
|
||||
RalewaySemiBold {
|
||||
color: 'white'
|
||||
anchors.centerIn: parent
|
||||
text: "1x"
|
||||
size: 18
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
scaleSlider.value = 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: handAndCollisions
|
||||
anchors.top: avatarScaleRow.bottom
|
||||
anchors.topMargin: 39
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
rows: 2
|
||||
rowSpacing: 25
|
||||
|
||||
columns: 3
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
Layout.row: 0
|
||||
Layout.column: 0
|
||||
|
||||
text: "Dominant Hand"
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: leftRight
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: leftHandRadioButton
|
||||
|
||||
Layout.row: 0
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -40
|
||||
|
||||
ButtonGroup.group: leftRight
|
||||
checked: true
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Left"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: rightHandRadioButton
|
||||
|
||||
Layout.row: 0
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: 20
|
||||
|
||||
ButtonGroup.group: leftRight
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Right"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
Layout.row: 1
|
||||
Layout.column: 0
|
||||
|
||||
text: "Avatar Collisions"
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: onOff
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: collisionsEnabledRadiobutton
|
||||
|
||||
Layout.row: 1
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -40
|
||||
ButtonGroup.group: onOff
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
checked: true
|
||||
|
||||
text: "ON"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: collisionsDisabledRadioButton
|
||||
|
||||
Layout.row: 1
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: 20
|
||||
|
||||
ButtonGroup.group: onOff
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
|
||||
text: "OFF"
|
||||
boxSize: 20
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: avatarAnimationLayout
|
||||
anchors.top: handAndCollisions.bottom
|
||||
anchors.topMargin: 25
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 4
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
text: "Avatar Animation JSON"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputTextStyle4 {
|
||||
id: avatarAnimationUrlInputText
|
||||
font.pixelSize: 17
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
placeholderText: 'user\\file\\dir'
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: avatarCollisionLayout
|
||||
anchors.top: avatarAnimationLayout.bottom
|
||||
anchors.topMargin: 25
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 4
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
text: "Avatar collision sound URL (optional)"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputTextStyle4 {
|
||||
id: avatarCollisionSoundUrlInputText
|
||||
font.pixelSize: 17
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
placeholderText: 'https://hifi-public.s3.amazonaws.com/sounds/Collisions-'
|
||||
}
|
||||
}
|
||||
|
||||
DialogButtons {
|
||||
id: dialogButtons
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
yesText: "SAVE"
|
||||
noText: "CANCEL"
|
||||
}
|
||||
}
|
||||
}
|
29
interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias text: glyph.text
|
||||
property alias font: glyph.font
|
||||
property alias color: glyph.color
|
||||
property alias horizontalAlignment: glyph.horizontalAlignment
|
||||
property alias dropShadowRadius: shadow.radius
|
||||
property alias dropShadowHorizontalOffset: shadow.horizontalOffset
|
||||
property alias dropShadowVerticalOffset: shadow.verticalOffset
|
||||
|
||||
HiFiGlyphs {
|
||||
id: glyph
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: glyph
|
||||
radius: 4
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 4
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: glyph
|
||||
}
|
||||
}
|
32
interface/resources/qml/hifi/avatarapp/ShadowImage.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias source: image.source
|
||||
property alias dropShadowRadius: shadow.radius
|
||||
property alias dropShadowHorizontalOffset: shadow.horizontalOffset
|
||||
property alias dropShadowVerticalOffset: shadow.verticalOffset
|
||||
property alias radius: image.radius
|
||||
property alias border: image.border
|
||||
property alias status: image.status
|
||||
property alias progress: image.progress
|
||||
property alias fillMode: image.fillMode
|
||||
|
||||
RoundImage {
|
||||
id: image
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
radius: 6
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: image
|
||||
radius: 6
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 3
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: image
|
||||
}
|
||||
}
|
30
interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
property alias color: rectangle.color
|
||||
property alias gradient: rectangle.gradient
|
||||
property alias border: rectangle.border
|
||||
property alias radius: rectangle.radius
|
||||
property alias dropShadowRadius: shadow.radius
|
||||
property alias dropShadowHorizontalOffset: shadow.horizontalOffset
|
||||
property alias dropShadowVerticalOffset: shadow.verticalOffset
|
||||
property alias dropShadowOpacity: shadow.opacity
|
||||
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: rectangle
|
||||
radius: 6
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 3
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: rectangle
|
||||
}
|
||||
}
|
7
interface/resources/qml/hifi/avatarapp/Spinner.qml
Normal file
|
@ -0,0 +1,7 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebEngine 1.5
|
||||
|
||||
AnimatedImage {
|
||||
source: "../../../icons/loader-snake-64-w.gif"
|
||||
playing: visible
|
||||
}
|
29
interface/resources/qml/hifi/avatarapp/SquareLabel.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
import "../../styles-uit"
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
ShadowRectangle {
|
||||
width: 44
|
||||
height: 28
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: style.colors.blueHighlight }
|
||||
GradientStop { position: 1.0; color: style.colors.blueAccent }
|
||||
}
|
||||
|
||||
property alias glyphText: glyph.text
|
||||
property alias glyphRotation: glyph.rotation
|
||||
property alias glyphSize: glyph.size
|
||||
|
||||
radius: 3
|
||||
|
||||
HiFiGlyphs {
|
||||
id: glyph
|
||||
color: 'white'
|
||||
anchors.centerIn: parent
|
||||
size: 30
|
||||
}
|
||||
}
|
43
interface/resources/qml/hifi/avatarapp/TransparencyMask.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
property alias source: sourceImage.sourceItem
|
||||
property alias maskSource: sourceMask.sourceItem
|
||||
|
||||
anchors.fill: parent
|
||||
ShaderEffectSource {
|
||||
id: sourceMask
|
||||
smooth: true
|
||||
hideSource: true
|
||||
}
|
||||
ShaderEffectSource {
|
||||
id: sourceImage
|
||||
hideSource: true
|
||||
}
|
||||
|
||||
ShaderEffect {
|
||||
id: maskEffect
|
||||
anchors.fill: parent
|
||||
|
||||
property variant source: sourceImage
|
||||
property variant mask: sourceMask
|
||||
|
||||
fragmentShader: {
|
||||
"
|
||||
varying highp vec2 qt_TexCoord0;
|
||||
uniform lowp sampler2D source;
|
||||
uniform lowp sampler2D mask;
|
||||
void main() {
|
||||
|
||||
highp vec4 maskColor = texture2D(mask, vec2(qt_TexCoord0.x, qt_TexCoord0.y));
|
||||
highp vec4 sourceColor = texture2D(source, vec2(qt_TexCoord0.x, qt_TexCoord0.y));
|
||||
|
||||
if(maskColor.a > 0.0)
|
||||
gl_FragColor = sourceColor;
|
||||
else
|
||||
gl_FragColor = maskColor;
|
||||
}
|
||||
"
|
||||
}
|
||||
}
|
||||
}
|
65
interface/resources/qml/hifi/avatarapp/Vector3.qml
Normal file
|
@ -0,0 +1,65 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
Row {
|
||||
id: root
|
||||
width: parent.width
|
||||
height: xspinner.controlHeight
|
||||
|
||||
property int spinboxSpace: 10
|
||||
property int spinboxWidth: (parent.width - 2 * spinboxSpace) / 3
|
||||
property color backgroundColor: "darkgray"
|
||||
|
||||
property int decimals: 4
|
||||
property real realFrom: 0
|
||||
property real realTo: 100
|
||||
property real realStepSize: 0.0001
|
||||
|
||||
spacing: spinboxSpace
|
||||
|
||||
property alias xvalue: xspinner.realValue
|
||||
property alias yvalue: yspinner.realValue
|
||||
property alias zvalue: zspinner.realValue
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: xspinner
|
||||
width: parent.spinboxWidth
|
||||
labelInside: "X:"
|
||||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.redHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: yspinner
|
||||
width: parent.spinboxWidth
|
||||
labelInside: "Y:"
|
||||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.greenHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
|
||||
HifiControlsUit.SpinBox {
|
||||
id: zspinner
|
||||
width: parent.spinboxWidth
|
||||
labelInside: "Z:"
|
||||
backgroundColor: parent.backgroundColor
|
||||
colorLabelInside: hifi.colors.primaryHighlight
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
decimals: root.decimals;
|
||||
realFrom: root.realFrom
|
||||
realTo: root.realTo
|
||||
realStepSize: root.realStepSize
|
||||
}
|
||||
}
|
15
interface/resources/qml/hifi/avatarapp/WhiteButton.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.5
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
|
||||
HifiControlsUit.Button {
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
width: Math.max(hifi.dimensions.buttonWidth, implicitTextWidth + 20)
|
||||
fontSize: 18
|
||||
color: hifi.buttons.noneBorderlessGray;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
height: 40
|
||||
}
|
|
@ -300,6 +300,12 @@ Item {
|
|||
id: controllerPrefereneces
|
||||
objectName: "TabletControllerPreferences"
|
||||
showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
|
||||
categoryProperties: {
|
||||
"VR Movement" : {
|
||||
"User real-world height (meters)" : { "anchors.right" : "undefined" },
|
||||
"RESET SENSORS" : { "width" : "180", "anchors.left" : "undefined" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ Item {
|
|||
HifiConstants { id: hifi }
|
||||
property var sections: []
|
||||
property var showCategories: []
|
||||
property var categoryProperties: ({})
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
|
@ -100,7 +101,8 @@ Item {
|
|||
// NOTE: the sort order of items in the showCategories array is the same order in the dialog.
|
||||
for (i = 0; i < showCategories.length; i++) {
|
||||
if (categoryMap[showCategories[i]]) {
|
||||
sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i]}));
|
||||
var properties = categoryProperties.hasOwnProperty(showCategories[i]) ? categoryProperties[showCategories[i]] : {};
|
||||
sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i], sectionProperties: properties}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Preference {
|
|||
property bool isLast: false
|
||||
property string name: "Header"
|
||||
property real spacing: 8
|
||||
property var sectionProperties: ({})
|
||||
default property alias preferences: contentContainer.children
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
@ -163,6 +164,28 @@ Preference {
|
|||
|
||||
if (builder) {
|
||||
preferences.push(builder.createObject(contentContainer, { preference: preference, isFirstCheckBox: (checkBoxCount === 1) , z: zpos}));
|
||||
|
||||
var preferenceObject = preferences[preferences.length - 1];
|
||||
var props = sectionProperties.hasOwnProperty(preference.name) ? sectionProperties[preference.name] : {};
|
||||
|
||||
for(var prop in props) {
|
||||
var value = props[prop];
|
||||
if(value.indexOf('.') !== -1) {
|
||||
var splittedValues = value.split('.');
|
||||
if(splittedValues[0] === 'parent') {
|
||||
value = preferenceObject.parent[splittedValues[1]];
|
||||
}
|
||||
} else if(value === 'undefined') {
|
||||
value = undefined;
|
||||
}
|
||||
|
||||
if(prop.indexOf('.') !== -1) {
|
||||
var splittedProps = prop.split('.');
|
||||
preferenceObject[splittedProps[0]][splittedProps[1]] = value;
|
||||
} else {
|
||||
preferenceObject[prop] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7640,7 +7640,6 @@ void Application::toggleEntityScriptServerLogDialog() {
|
|||
|
||||
void Application::loadAddAvatarBookmarkDialog() const {
|
||||
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
|
||||
avatarBookmarks->addBookmark();
|
||||
}
|
||||
|
||||
void Application::loadAvatarBrowser() const {
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
#include <QStandardPaths>
|
||||
#include <QQmlContext>
|
||||
#include <QList>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <Application.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
|
@ -28,7 +30,6 @@
|
|||
#include <VariantMapToScriptValue.h>
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
|
@ -92,10 +93,96 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
|
|||
}
|
||||
|
||||
AvatarBookmarks::AvatarBookmarks() {
|
||||
QDir directory(PathUtils::getAppDataPath());
|
||||
if (!directory.exists()) {
|
||||
directory.mkpath(".");
|
||||
}
|
||||
|
||||
_bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
if(!QFile::exists(_bookmarksFilename)) {
|
||||
auto defaultBookmarksFilename = PathUtils::resourcesPath() + QString("avatar/bookmarks") + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
if (!QFile::exists(defaultBookmarksFilename)) {
|
||||
qDebug() << defaultBookmarksFilename << "doesn't exist!";
|
||||
}
|
||||
|
||||
if (!QFile::copy(defaultBookmarksFilename, _bookmarksFilename)) {
|
||||
qDebug() << "failed to copy" << defaultBookmarksFilename << "to" << _bookmarksFilename;
|
||||
}
|
||||
}
|
||||
readFromFile();
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "addBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
QVariantMap bookmark = getAvatarDataToBookmark();
|
||||
insert(bookmarkName, bookmark);
|
||||
|
||||
emit bookmarkAdded(bookmarkName);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::saveBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "saveBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
if (contains(bookmarkName)) {
|
||||
QVariantMap bookmark = getAvatarDataToBookmark();
|
||||
insert(bookmarkName, bookmark);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarBookmarks::removeBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "removeBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
|
||||
remove(bookmarkName);
|
||||
emit bookmarkDeleted(bookmarkName);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
myAvatar->removeAvatarEntities();
|
||||
|
||||
addAvatarEntities(avatarEntities);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::loadBookmark(const QString& bookmarkName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "loadBookmark", Q_ARG(QString, bookmarkName));
|
||||
return;
|
||||
}
|
||||
|
||||
auto bookmarkEntry = _bookmarks.find(bookmarkName);
|
||||
|
||||
if (bookmarkEntry != _bookmarks.end()) {
|
||||
QVariantMap bookmark = bookmarkEntry.value().toMap();
|
||||
if (!bookmark.empty()) {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
myAvatar->removeAvatarEntities();
|
||||
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
|
||||
myAvatar->useFullAvatarURL(avatarUrl);
|
||||
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
|
||||
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
|
||||
|
||||
qCDebug(interfaceapp) << "Attach " << attachments;
|
||||
myAvatar->setAttachmentsVariant(attachments);
|
||||
|
||||
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
|
||||
myAvatar->setAvatarScale(qScale);
|
||||
|
||||
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
|
||||
addAvatarEntities(avatarEntities);
|
||||
|
||||
emit bookmarkLoaded(bookmarkName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarBookmarks::readFromFile() {
|
||||
// migrate old avatarbookmarks.json, used to be in 'local' folder on windows
|
||||
QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;
|
||||
|
@ -115,99 +202,37 @@ void AvatarBookmarks::readFromFile() {
|
|||
Bookmarks::readFromFile();
|
||||
}
|
||||
|
||||
void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
||||
// Add menus/actions
|
||||
auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar);
|
||||
QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection);
|
||||
_bookmarksMenu = menu->addMenu(MenuOption::AvatarBookmarks);
|
||||
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark);
|
||||
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
|
||||
|
||||
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
|
||||
addBookmarkToMenu(menubar, it.key(), it.value());
|
||||
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;
|
||||
}
|
||||
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
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());
|
||||
QVariantMap AvatarBookmarks::getAvatarDataToBookmark() {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
|
||||
if (action->data().type() == QVariant::String) {
|
||||
// TODO: Phase this out eventually.
|
||||
// Legacy avatar bookmark.
|
||||
|
||||
myAvatar->useFullAvatarURL(action->data().toString());
|
||||
qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark ";
|
||||
} else {
|
||||
|
||||
const QMap<QString, QVariant> bookmark = action->data().toMap();
|
||||
// Not magic value. This is the current made version, and if it changes this interpreter should be updated to
|
||||
// handle the new one separately.
|
||||
// This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous
|
||||
if (bookmark.value(ENTRY_VERSION) == 3) {
|
||||
myAvatar->removeAvatarEntities();
|
||||
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
|
||||
myAvatar->useFullAvatarURL(avatarUrl);
|
||||
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
|
||||
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
|
||||
|
||||
qCDebug(interfaceapp) << "Attach " << attachments;
|
||||
myAvatar->setAttachmentsVariant(attachments);
|
||||
|
||||
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
|
||||
myAvatar->setAvatarScale(qScale);
|
||||
|
||||
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
|
||||
addAvatarEntities(avatarEntities);
|
||||
|
||||
} else {
|
||||
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmark() {
|
||||
ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString());
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
auto bookmarkName = response.toString();
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap bookmark;
|
||||
bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
|
||||
QAction* changeAction = _bookmarksMenu->newAction();
|
||||
changeAction->setData(bookmark);
|
||||
connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar()));
|
||||
if (!_isMenuSorted) {
|
||||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
|
||||
} else {
|
||||
// TODO: this is aggressive but other alternatives have proved less fruitful so far.
|
||||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
}
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap bookmark;
|
||||
bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
|
||||
return bookmark;
|
||||
}
|
||||
|
|
|
@ -30,19 +30,50 @@ class AvatarBookmarks: public Bookmarks, public Dependency {
|
|||
|
||||
public:
|
||||
AvatarBookmarks();
|
||||
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
|
||||
|
||||
void setupMenus(Menu* menubar, MenuWrapper* menu) override {};
|
||||
Q_INVOKABLE QVariantMap getBookmark(const QString& bookmarkName);
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Add the current Avatar to your avatar bookmarks.
|
||||
* @function AvatarBookmarks.addBookMark
|
||||
*/
|
||||
void addBookmark();
|
||||
void addBookmark(const QString& bookmarkName);
|
||||
void saveBookmark(const QString& bookmarkName);
|
||||
void loadBookmark(const QString& bookmarkName);
|
||||
void removeBookmark(const QString& bookmarkName);
|
||||
void updateAvatarEntities(const QVariantList& avatarEntities);
|
||||
QVariantMap getBookmarks() { return _bookmarks; }
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* This function gets triggered after avatar loaded from bookmark
|
||||
* @function AvatarBookmarks.bookmarkLoaded
|
||||
* @param {string} bookmarkName
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void bookmarkLoaded(const QString& bookmarkName);
|
||||
|
||||
/**jsdoc
|
||||
* This function gets triggered after avatar bookmark deleted
|
||||
* @function AvatarBookmarks.bookmarkDeleted
|
||||
* @param {string} bookmarkName
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void bookmarkDeleted(const QString& bookmarkName);
|
||||
|
||||
/**jsdoc
|
||||
* This function gets triggered after avatar bookmark added
|
||||
* @function AvatarBookmarks.bookmarkAdded
|
||||
* @param {string} bookmarkName
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void bookmarkAdded(const QString& bookmarkName);
|
||||
|
||||
protected:
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override {};
|
||||
void readFromFile() override;
|
||||
QVariantMap getAvatarDataToBookmark();
|
||||
|
||||
private:
|
||||
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
|
||||
|
@ -53,9 +84,6 @@ private:
|
|||
const QString ENTRY_VERSION = "version";
|
||||
|
||||
const int AVATAR_BOOKMARK_VERSION = 3;
|
||||
|
||||
private slots:
|
||||
void changeToBookmarkedAvatar();
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarBookmarks_h
|
||||
|
|
|
@ -46,6 +46,10 @@ void Bookmarks::deleteBookmark() {
|
|||
return;
|
||||
}
|
||||
|
||||
deleteBookmark(bookmarkName);
|
||||
}
|
||||
|
||||
void Bookmarks::deleteBookmark(const QString& bookmarkName) {
|
||||
removeBookmarkFromMenu(Menu::getInstance(), bookmarkName);
|
||||
remove(bookmarkName);
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
QString addressForBookmark(const QString& name) const;
|
||||
|
||||
protected:
|
||||
void deleteBookmark(const QString& bookmarkName);
|
||||
|
||||
void addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark);
|
||||
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) = 0;
|
||||
void enableMenuItems(bool enabled);
|
||||
|
@ -38,9 +40,10 @@ protected:
|
|||
void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name.
|
||||
void sortActions(Menu* menubar, MenuWrapper* menu);
|
||||
int getMenuItemLocation(QList<QAction*> actions, const QString& name) const;
|
||||
|
||||
void removeBookmarkFromMenu(Menu* menubar, const QString& name);
|
||||
bool contains(const QString& name) const;
|
||||
|
||||
void remove(const QString& name);
|
||||
|
||||
QVariantMap _bookmarks; // { name: url, ... }
|
||||
QPointer<MenuWrapper> _bookmarksMenu;
|
||||
QPointer<QAction> _deleteBookmarksAction;
|
||||
|
@ -57,12 +60,9 @@ protected slots:
|
|||
void deleteBookmark();
|
||||
|
||||
private:
|
||||
void remove(const QString& name);
|
||||
static bool sortOrder(QAction* a, QAction* b);
|
||||
|
||||
void persistToFile();
|
||||
|
||||
void removeBookmarkFromMenu(Menu* menubar, const QString& name);
|
||||
};
|
||||
|
||||
#endif // hifi_Bookmarks_h
|
||||
|
|
|
@ -158,46 +158,6 @@ Menu::Menu() {
|
|||
// Edit > Reload All Content
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
|
||||
|
||||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto avatar = avatarManager->getMyAvatar();
|
||||
|
||||
// Avatar > Size
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
// Avatar > Size > Increase
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
0, // QML Qt::Key_Plus,
|
||||
avatar.get(), SLOT(increaseSize()));
|
||||
|
||||
// Avatar > Size > Decrease
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
0, // QML Qt::Key_Minus,
|
||||
avatar.get(), SLOT(decreaseSize()));
|
||||
|
||||
// Avatar > Size > Reset
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0, // QML Qt::Key_Equal,
|
||||
avatar.get(), SLOT(resetSize()));
|
||||
|
||||
// Avatar > Reset Sensors
|
||||
addActionToQMenuAndActionHash(avatarMenu,
|
||||
MenuOption::ResetSensors,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp, SLOT(resetSensors()));
|
||||
|
||||
// Avatar > Attachments...
|
||||
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"),
|
||||
QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
|
||||
});
|
||||
|
||||
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
|
||||
avatarBookmarks->setupMenus(this, avatarMenu);
|
||||
// Display menu ----------------------------------
|
||||
// FIXME - this is not yet matching Alan's spec because it doesn't have
|
||||
// menus for "2D"/"3D" - we need to add support for detecting the appropriate
|
||||
|
@ -315,13 +275,6 @@ Menu::Menu() {
|
|||
QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
|
||||
});
|
||||
|
||||
// Settings > Avatar...
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
QString("hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
});
|
||||
|
||||
// Settings > Developer Menu
|
||||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
|
@ -580,6 +533,9 @@ Menu::Menu() {
|
|||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false);
|
||||
connect(action, &QAction::triggered, [this]{ Avatar::setShowOtherLookAtVectors(isOptionChecked(MenuOption::ShowOtherLookAtVectors)); });
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
auto avatar = avatarManager->getMyAvatar();
|
||||
|
||||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableLookAtSnapping, 0, true);
|
||||
connect(action, &QAction::triggered, [this, avatar]{
|
||||
avatar->setProperty("lookAtSnappingEnabled", isOptionChecked(MenuOption::EnableLookAtSnapping));
|
||||
|
|
|
@ -1591,18 +1591,26 @@ void MyAvatar::removeAvatarEntities() {
|
|||
QVariantList MyAvatar::getAvatarEntitiesVariant() {
|
||||
QVariantList avatarEntitiesData;
|
||||
QScriptEngine scriptEngine;
|
||||
forEachChild([&](SpatiallyNestablePointer child) {
|
||||
if (child->getNestableType() == NestableType::Entity) {
|
||||
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(child);
|
||||
if (modelEntity) {
|
||||
QVariantMap avatarEntityData;
|
||||
EntityItemProperties entityProperties = modelEntity->getProperties();
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
avatarEntitiesData.append(QVariant(avatarEntityData));
|
||||
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
if (entityTree) {
|
||||
AvatarEntityMap avatarEntities = getAvatarEntityData();
|
||||
for (auto entityID : avatarEntities.keys()) {
|
||||
auto entity = entityTree->findEntityByID(entityID);
|
||||
if (!entity) {
|
||||
continue;
|
||||
}
|
||||
QVariantMap avatarEntityData;
|
||||
EncodeBitstreamParams params;
|
||||
auto desiredProperties = entity->getEntityProperties(params);
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_LOCAL_ROTATION;
|
||||
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
avatarEntitiesData.append(QVariant(avatarEntityData));
|
||||
}
|
||||
});
|
||||
}
|
||||
return avatarEntitiesData;
|
||||
}
|
||||
|
||||
|
@ -1979,7 +1987,6 @@ QUrl MyAvatar::getAnimGraphUrl() const {
|
|||
}
|
||||
|
||||
void MyAvatar::setAnimGraphUrl(const QUrl& url) {
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setAnimGraphUrl", Q_ARG(QUrl, url));
|
||||
return;
|
||||
|
@ -1988,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.
|
||||
|
||||
|
@ -2812,6 +2822,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) {
|
|||
}
|
||||
|
||||
_characterController.setCollisionless(!enabled);
|
||||
emit collisionsEnabledChanged(enabled);
|
||||
}
|
||||
|
||||
bool MyAvatar::getCollisionsEnabled() {
|
||||
|
|
|
@ -888,7 +888,12 @@ public:
|
|||
|
||||
bool hasDriveInput() const;
|
||||
|
||||
QVariantList getAvatarEntitiesVariant();
|
||||
/**jsdoc
|
||||
* Function returns list of avatar entities
|
||||
* @function MyAvatar.getAvatarEntitiesVariant()
|
||||
* @returns {object[]}
|
||||
*/
|
||||
Q_INVOKABLE QVariantList getAvatarEntitiesVariant();
|
||||
void removeAvatarEntities();
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1294,6 +1299,22 @@ signals:
|
|||
*/
|
||||
void collisionWithEntity(const Collision& collision);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when collisions with avatar enabled or disabled
|
||||
* @function MyAvatar.collisionsEnabledChanged
|
||||
* @param {boolean} enabled
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void collisionsEnabledChanged(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when avatar's animation url changes
|
||||
* @function MyAvatar.animGraphUrlChanged
|
||||
* @param {url} url
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void animGraphUrlChanged(const QUrl& url);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.energyChanged
|
||||
* @param {number} energy
|
||||
|
|
|
@ -300,6 +300,12 @@ void setupPreferences() {
|
|||
preference->setStep(0.001f);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto preference = new ButtonPreference(MOVEMENT, "RESET SENSORS", [] {
|
||||
qApp->resetSensors();
|
||||
});
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
static const QString AVATAR_CAMERA{ "Mouse Sensitivity" };
|
||||
{
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "scripting/MenuScriptingInterface.h"
|
||||
#include "scripting/SettingsScriptingInterface.h"
|
||||
#include <Preferences.h>
|
||||
#include <AvatarBookmarks.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include "FileDialogHelper.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
@ -253,6 +254,7 @@ void Web3DOverlay::setupQmlSurface() {
|
|||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
|
|
|
@ -212,6 +212,8 @@ void Avatar::setTargetScale(float targetScale) {
|
|||
_targetScale = newValue;
|
||||
_scaleChanged = usecTimestampNow();
|
||||
_isAnimatingScale = true;
|
||||
|
||||
emit targetScaleChanged(targetScale);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariantMap>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
|
@ -80,7 +81,6 @@ public:
|
|||
}
|
||||
|
||||
void setEnabler(BoolPreference* enabler, bool inverse = false);
|
||||
|
||||
virtual Type getType() { return Invalid; };
|
||||
|
||||
Q_INVOKABLE virtual void load() {};
|
||||
|
|
|
@ -48,7 +48,6 @@ const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND;
|
|||
static const char* MENU_PARENT = "Avatar";
|
||||
static const char* MENU_NAME = "Vive Controllers";
|
||||
static const char* MENU_PATH = "Avatar" ">" "Vive Controllers";
|
||||
static const char* RENDER_CONTROLLERS = "Render Hand Controllers";
|
||||
|
||||
static const int MIN_HEAD = 1;
|
||||
static const int MIN_PUCK_COUNT = 2;
|
||||
|
@ -205,11 +204,6 @@ bool ViveControllerManager::activate() {
|
|||
return false;
|
||||
}
|
||||
|
||||
_container->addMenu(MENU_PATH);
|
||||
_container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
|
||||
[this](bool clicked) { this->setRenderControllers(clicked); },
|
||||
true, true);
|
||||
|
||||
enableOpenVrKeyboard(_container);
|
||||
|
||||
// register with UserInputMapper
|
||||
|
@ -224,9 +218,6 @@ void ViveControllerManager::deactivate() {
|
|||
|
||||
disableOpenVrKeyboard();
|
||||
|
||||
_container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS);
|
||||
_container->removeMenu(MENU_PATH);
|
||||
|
||||
if (_system) {
|
||||
_container->makeRenderingContextCurrent();
|
||||
releaseOpenVrSystem();
|
||||
|
|
|
@ -57,8 +57,6 @@ public:
|
|||
void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
||||
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||
|
||||
void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; }
|
||||
|
||||
virtual void saveSettings() const override;
|
||||
virtual void loadSettings() override;
|
||||
|
||||
|
@ -219,7 +217,6 @@ private:
|
|||
int _leftHandRenderID { 0 };
|
||||
int _rightHandRenderID { 0 };
|
||||
|
||||
bool _renderControllers { false };
|
||||
vr::IVRSystem* _system { nullptr };
|
||||
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>(_system) };
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/bubble.js",
|
||||
"system/snapshot.js",
|
||||
"system/pal.js", // "system/mod.js", // older UX, if you prefer
|
||||
"system/avatarapp.js",
|
||||
"system/makeUserConnection.js",
|
||||
"system/tablet-goto.js",
|
||||
"system/marketplaces/marketplaces.js",
|
||||
|
|
550
scripts/system/avatarapp.js
Normal file
|
@ -0,0 +1,550 @@
|
|||
"use strict";
|
||||
/*jslint vars:true, plusplus:true, forin:true*/
|
||||
/*global Tablet, Settings, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, HMD, Controller, Account, UserActivityLogger, Messages, Window, XMLHttpRequest, print, location, getControllerWorldLocation*/
|
||||
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||
//
|
||||
// avatarapp.js
|
||||
//
|
||||
// Created by Alexander Ivash on April 30, 2018
|
||||
// Copyright 2016 High Fidelity, Inc
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var request = Script.require('request').request;
|
||||
var AVATARAPP_QML_SOURCE = "hifi/AvatarApp.qml";
|
||||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
// constants from AvatarBookmarks.h
|
||||
var ENTRY_AVATAR_URL = "avatarUrl";
|
||||
var ENTRY_AVATAR_ATTACHMENTS = "attachments";
|
||||
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();
|
||||
|
||||
for(var i = 0; i < wearablesArray.length; ++i) {
|
||||
var wearable = wearablesArray[i];
|
||||
var localRotation = wearable.properties.localRotation;
|
||||
wearable.properties.localRotationAngles = Quat.safeEulerAngles(localRotation)
|
||||
}
|
||||
|
||||
return wearablesArray;
|
||||
}
|
||||
|
||||
function getMyAvatar() {
|
||||
var avatar = {}
|
||||
avatar[ENTRY_AVATAR_URL] = MyAvatar.skeletonModelURL;
|
||||
avatar[ENTRY_AVATAR_SCALE] = MyAvatar.getAvatarScale();
|
||||
avatar[ENTRY_AVATAR_ATTACHMENTS] = MyAvatar.getAttachmentsVariant();
|
||||
avatar[ENTRY_AVATAR_ENTITIES] = getMyAvatarWearables();
|
||||
return avatar;
|
||||
}
|
||||
|
||||
function getMyAvatarSettings() {
|
||||
return {
|
||||
dominantHand: MyAvatar.getDominantHand(),
|
||||
collisionsEnabled : MyAvatar.getCollisionsEnabled(),
|
||||
collisionSoundUrl : MyAvatar.collisionSoundURL,
|
||||
animGraphUrl : MyAvatar.getAnimGraphUrl(),
|
||||
}
|
||||
}
|
||||
|
||||
function updateAvatarWearables(avatar, bookmarkAvatarName) {
|
||||
executeLater(function() {
|
||||
var wearables = getMyAvatarWearables();
|
||||
avatar[ENTRY_AVATAR_ENTITIES] = wearables;
|
||||
|
||||
sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables, 'avatarName' : bookmarkAvatarName})
|
||||
});
|
||||
}
|
||||
|
||||
var adjustWearables = {
|
||||
opened : false,
|
||||
cameraMode : '',
|
||||
setOpened : function(value) {
|
||||
if(this.opened !== value) {
|
||||
if(value) {
|
||||
this.cameraMode = Camera.mode;
|
||||
|
||||
if(!HMD.active) {
|
||||
Camera.mode = 'mirror';
|
||||
}
|
||||
} else {
|
||||
Camera.mode = this.cameraMode;
|
||||
}
|
||||
|
||||
this.opened = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentAvatarWearablesBackup = null;
|
||||
var currentAvatar = null;
|
||||
var currentAvatarSettings = getMyAvatarSettings();
|
||||
|
||||
var notifyScaleChanged = true;
|
||||
function onTargetScaleChanged() {
|
||||
if(currentAvatar.scale !== MyAvatar.getAvatarScale()) {
|
||||
currentAvatar.scale = MyAvatar.getAvatarScale();
|
||||
if(notifyScaleChanged) {
|
||||
sendToQml({'method' : 'scaleChanged', 'value' : currentAvatar.scale})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onSkeletonModelURLChanged() {
|
||||
if(currentAvatar || (currentAvatar.skeletonModelURL !== MyAvatar.skeletonModelURL)) {
|
||||
fromQml({'method' : 'getAvatars'});
|
||||
}
|
||||
}
|
||||
|
||||
function onDominantHandChanged(dominantHand) {
|
||||
if(currentAvatarSettings.dominantHand !== dominantHand) {
|
||||
currentAvatarSettings.dominantHand = dominantHand;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'dominantHand', 'value' : dominantHand})
|
||||
}
|
||||
}
|
||||
|
||||
function onCollisionsEnabledChanged(enabled) {
|
||||
if(currentAvatarSettings.collisionsEnabled !== enabled) {
|
||||
currentAvatarSettings.collisionsEnabled = enabled;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionsEnabled', 'value' : enabled})
|
||||
}
|
||||
}
|
||||
|
||||
function onNewCollisionSoundUrl(url) {
|
||||
if(currentAvatarSettings.collisionSoundUrl !== url) {
|
||||
currentAvatarSettings.collisionSoundUrl = url;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'collisionSoundUrl', 'value' : url})
|
||||
}
|
||||
}
|
||||
|
||||
function onAnimGraphUrlChanged(url) {
|
||||
if(currentAvatarSettings.animGraphUrl !== url) {
|
||||
currentAvatarSettings.animGraphUrl = url;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'animGraphUrl', 'value' : url})
|
||||
}
|
||||
}
|
||||
|
||||
var selectedAvatarEntityGrabbable = false;
|
||||
var selectedAvatarEntityID = null;
|
||||
var grabbedAvatarEntityChangeNotifier = null;
|
||||
|
||||
var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml";
|
||||
var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";
|
||||
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js");
|
||||
|
||||
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||
switch (message.method) {
|
||||
case 'getAvatars':
|
||||
currentAvatar = getMyAvatar();
|
||||
currentAvatarSettings = getMyAvatarSettings();
|
||||
|
||||
message.data = {
|
||||
'bookmarks' : AvatarBookmarks.getBookmarks(),
|
||||
'displayName' : MyAvatar.displayName,
|
||||
'currentAvatar' : currentAvatar,
|
||||
'currentAvatarSettings' : currentAvatarSettings
|
||||
};
|
||||
|
||||
for(var bookmarkName in message.data.bookmarks) {
|
||||
var bookmark = message.data.bookmarks[bookmarkName];
|
||||
|
||||
bookmark.avatarEntites.forEach(function(avatarEntity) {
|
||||
avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation)
|
||||
})
|
||||
}
|
||||
|
||||
sendToQml(message)
|
||||
break;
|
||||
case 'selectAvatar':
|
||||
AvatarBookmarks.loadBookmark(message.name);
|
||||
break;
|
||||
case 'deleteAvatar':
|
||||
AvatarBookmarks.removeBookmark(message.name);
|
||||
break;
|
||||
case 'addAvatar':
|
||||
AvatarBookmarks.addBookmark(message.name);
|
||||
break;
|
||||
case 'adjustWearable':
|
||||
if(message.properties.localRotationAngles) {
|
||||
message.properties.localRotation = Quat.fromVec3Degrees(message.properties.localRotationAngles)
|
||||
}
|
||||
|
||||
Entities.editEntity(message.entityID, message.properties);
|
||||
message.properties = Entities.getEntityProperties(message.entityID, Object.keys(message.properties));
|
||||
|
||||
if(message.properties.localRotation) {
|
||||
message.properties.localRotationAngles = Quat.safeEulerAngles(message.properties.localRotation);
|
||||
}
|
||||
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : message.entityID, wearableIndex : message.wearableIndex, properties : message.properties, updateUI : false})
|
||||
break;
|
||||
case 'adjustWearablesOpened':
|
||||
currentAvatarWearablesBackup = getMyAvatarWearables();
|
||||
adjustWearables.setOpened(true);
|
||||
|
||||
Entities.mousePressOnEntity.connect(onSelectedEntity);
|
||||
Messages.subscribe('Hifi-Object-Manipulation');
|
||||
Messages.messageReceived.connect(handleWearableMessages);
|
||||
break;
|
||||
case 'adjustWearablesClosed':
|
||||
if(!message.save) {
|
||||
// revert changes using snapshot of wearables
|
||||
if(currentAvatarWearablesBackup !== null) {
|
||||
AvatarBookmarks.updateAvatarEntities(currentAvatarWearablesBackup);
|
||||
updateAvatarWearables(currentAvatar, message.avatarName);
|
||||
}
|
||||
} else {
|
||||
sendToQml({'method' : 'updateAvatarInBookmarks'});
|
||||
}
|
||||
|
||||
adjustWearables.setOpened(false);
|
||||
ensureWearableSelected(null);
|
||||
Entities.mousePressOnEntity.disconnect(onSelectedEntity);
|
||||
Messages.messageReceived.disconnect(handleWearableMessages);
|
||||
Messages.unsubscribe('Hifi-Object-Manipulation');
|
||||
break;
|
||||
case 'selectWearable':
|
||||
ensureWearableSelected(message.entityID);
|
||||
break;
|
||||
case 'deleteWearable':
|
||||
Entities.deleteEntity(message.entityID);
|
||||
updateAvatarWearables(currentAvatar, message.avatarName);
|
||||
break;
|
||||
case 'changeDisplayName':
|
||||
if (MyAvatar.displayName !== message.displayName) {
|
||||
MyAvatar.displayName = message.displayName;
|
||||
UserActivityLogger.palAction("display_name_change", message.displayName);
|
||||
}
|
||||
break;
|
||||
case 'applyExternalAvatar':
|
||||
var currentAvatarURL = MyAvatar.getFullAvatarURLFromPreferences();
|
||||
if(currentAvatarURL !== message.avatarURL) {
|
||||
MyAvatar.useFullAvatarURL(message.avatarURL);
|
||||
sendToQml({'method' : 'externalAvatarApplied', 'avatarURL' : message.avatarURL})
|
||||
}
|
||||
break;
|
||||
case 'navigate':
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system")
|
||||
if(message.url.indexOf('app://') === 0) {
|
||||
if(message.url === 'app://marketplace') {
|
||||
tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
} else if(message.url === 'app://purchases') {
|
||||
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
|
||||
}
|
||||
|
||||
} else if(message.url.indexOf('hifi://') === 0) {
|
||||
AddressManager.handleLookupString(message.url, false);
|
||||
} else if(message.url.indexOf('https://') === 0 || message.url.indexOf('http://') === 0) {
|
||||
tablet.gotoWebScreen(message.url, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'setScale':
|
||||
notifyScaleChanged = false;
|
||||
MyAvatar.setAvatarScale(message.avatarScale);
|
||||
currentAvatar.avatarScale = message.avatarScale;
|
||||
notifyScaleChanged = true;
|
||||
break;
|
||||
case 'revertScale':
|
||||
MyAvatar.setAvatarScale(message.avatarScale);
|
||||
currentAvatar.avatarScale = message.avatarScale;
|
||||
break;
|
||||
case 'saveSettings':
|
||||
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();
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized message from AvatarApp.qml:', JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
function isGrabbable(entityID) {
|
||||
if(entityID === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']);
|
||||
if (properties.clientOnly) {
|
||||
var userData;
|
||||
try {
|
||||
userData = JSON.parse(properties.userData);
|
||||
} catch (e) {
|
||||
userData = {};
|
||||
}
|
||||
|
||||
return userData.grabbableKey && userData.grabbableKey.grabbable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function setGrabbable(entityID, grabbable) {
|
||||
var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']);
|
||||
if (properties.clientOnly) {
|
||||
var userData;
|
||||
try {
|
||||
userData = JSON.parse(properties.userData);
|
||||
} catch (e) {
|
||||
userData = {};
|
||||
}
|
||||
|
||||
if (userData.grabbableKey === undefined) {
|
||||
userData.grabbableKey = {};
|
||||
}
|
||||
userData.grabbableKey.grabbable = grabbable;
|
||||
Entities.editEntity(entityID, {userData: JSON.stringify(userData)});
|
||||
}
|
||||
}
|
||||
|
||||
function ensureWearableSelected(entityID) {
|
||||
if(selectedAvatarEntityID !== entityID) {
|
||||
if(grabbedAvatarEntityChangeNotifier !== null) {
|
||||
Script.clearInterval(grabbedAvatarEntityChangeNotifier);
|
||||
grabbedAvatarEntityChangeNotifier = null;
|
||||
}
|
||||
|
||||
if(selectedAvatarEntityID !== null) {
|
||||
setGrabbable(selectedAvatarEntityID, selectedAvatarEntityGrabbable);
|
||||
}
|
||||
|
||||
selectedAvatarEntityID = entityID;
|
||||
selectedAvatarEntityGrabbable = isGrabbable(entityID);
|
||||
|
||||
if(selectedAvatarEntityID !== null) {
|
||||
setGrabbable(selectedAvatarEntityID, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isEntityBeingWorn(entityID) {
|
||||
return Entities.getEntityProperties(entityID, 'parentID').parentID === MyAvatar.sessionUUID;
|
||||
};
|
||||
|
||||
function onSelectedEntity(entityID, pointerEvent) {
|
||||
if(selectedAvatarEntityID !== entityID && isEntityBeingWorn(entityID))
|
||||
{
|
||||
if(ensureWearableSelected(entityID)) {
|
||||
sendToQml({'method' : 'selectAvatarEntity', 'entityID' : selectedAvatarEntityID});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleWearableMessages(channel, message, sender) {
|
||||
if (channel !== 'Hifi-Object-Manipulation') {
|
||||
return;
|
||||
}
|
||||
|
||||
var parsedMessage = null;
|
||||
|
||||
try {
|
||||
parsedMessage = JSON.parse(message);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
var entityID = parsedMessage.grabbedEntity;
|
||||
if(parsedMessage.action === 'grab') {
|
||||
if(selectedAvatarEntityID !== entityID) {
|
||||
ensureWearableSelected(entityID);
|
||||
sendToQml({'method' : 'selectAvatarEntity', 'entityID' : selectedAvatarEntityID});
|
||||
}
|
||||
|
||||
grabbedAvatarEntityChangeNotifier = Script.setInterval(function() {
|
||||
// for some reasons Entities.getEntityProperties returns more than was asked..
|
||||
var propertyNames = ['localPosition', 'localRotation', 'dimensions', 'naturalDimensions'];
|
||||
var entityProperties = Entities.getEntityProperties(selectedAvatarEntityID, propertyNames);
|
||||
var properties = {}
|
||||
|
||||
propertyNames.forEach(function(propertyName) {
|
||||
properties[propertyName] = entityProperties[propertyName];
|
||||
})
|
||||
|
||||
properties.localRotationAngles = Quat.safeEulerAngles(properties.localRotation);
|
||||
sendToQml({'method' : 'wearableUpdated', 'entityID' : selectedAvatarEntityID, 'wearableIndex' : -1, 'properties' : properties, updateUI : true})
|
||||
|
||||
}, 1000);
|
||||
} else if(parsedMessage.action === 'release') {
|
||||
if(grabbedAvatarEntityChangeNotifier !== null) {
|
||||
Script.clearInterval(grabbedAvatarEntityChangeNotifier);
|
||||
grabbedAvatarEntityChangeNotifier = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sendToQml(message) {
|
||||
tablet.sendToQml(message);
|
||||
}
|
||||
|
||||
function onBookmarkLoaded(bookmarkName) {
|
||||
executeLater(function() {
|
||||
currentAvatar = getMyAvatar();
|
||||
sendToQml({'method' : 'bookmarkLoaded', 'data' : {'name' : bookmarkName, 'currentAvatar' : currentAvatar} });
|
||||
});
|
||||
}
|
||||
|
||||
function onBookmarkDeleted(bookmarkName) {
|
||||
sendToQml({'method' : 'bookmarkDeleted', 'name' : bookmarkName});
|
||||
}
|
||||
|
||||
function onBookmarkAdded(bookmarkName) {
|
||||
var bookmark = AvatarBookmarks.getBookmark(bookmarkName);
|
||||
bookmark.avatarEntites.forEach(function(avatarEntity) {
|
||||
avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation)
|
||||
})
|
||||
|
||||
sendToQml({ 'method': 'bookmarkAdded', 'bookmarkName': bookmarkName, 'bookmark': bookmark });
|
||||
}
|
||||
|
||||
//
|
||||
// Manage the connection between the button and the window.
|
||||
//
|
||||
var button;
|
||||
var buttonName = "AVATAR";
|
||||
var tablet = null;
|
||||
|
||||
function startup() {
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
button = tablet.addButton({
|
||||
text: buttonName,
|
||||
icon: "icons/tablet-icons/avatar-i.svg",
|
||||
activeIcon: "icons/tablet-icons/avatar-a.svg",
|
||||
sortOrder: 7
|
||||
});
|
||||
button.clicked.connect(onTabletButtonClicked);
|
||||
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||
}
|
||||
|
||||
startup();
|
||||
|
||||
var isWired = false;
|
||||
function off() {
|
||||
if (isWired) { // It is not ok to disconnect these twice, hence guard.
|
||||
isWired = false;
|
||||
}
|
||||
|
||||
if(adjustWearables.opened) {
|
||||
adjustWearables.setOpened(false);
|
||||
ensureWearableSelected(null);
|
||||
Entities.mousePressOnEntity.disconnect(onSelectedEntity);
|
||||
|
||||
Messages.messageReceived.disconnect(handleWearableMessages);
|
||||
Messages.unsubscribe('Hifi-Object-Manipulation');
|
||||
}
|
||||
|
||||
AvatarBookmarks.bookmarkLoaded.disconnect(onBookmarkLoaded);
|
||||
AvatarBookmarks.bookmarkDeleted.disconnect(onBookmarkDeleted);
|
||||
AvatarBookmarks.bookmarkAdded.disconnect(onBookmarkAdded);
|
||||
|
||||
MyAvatar.skeletonModelURLChanged.disconnect(onSkeletonModelURLChanged);
|
||||
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.skeletonModelURLChanged.connect(onSkeletonModelURLChanged);
|
||||
MyAvatar.dominantHandChanged.connect(onDominantHandChanged);
|
||||
MyAvatar.collisionsEnabledChanged.connect(onCollisionsEnabledChanged);
|
||||
MyAvatar.newCollisionSoundURL.connect(onNewCollisionSoundUrl);
|
||||
MyAvatar.animGraphUrlChanged.connect(onAnimGraphUrlChanged);
|
||||
MyAvatar.targetScaleChanged.connect(onTargetScaleChanged);
|
||||
}
|
||||
|
||||
function onTabletButtonClicked() {
|
||||
if (onAvatarAppScreen) {
|
||||
// for toolbar-mode: go back to home screen, this will close the window.
|
||||
tablet.gotoHomeScreen();
|
||||
} else {
|
||||
ContextOverlay.enabled = false;
|
||||
tablet.loadQMLSource(AVATARAPP_QML_SOURCE);
|
||||
isWired = true;
|
||||
}
|
||||
}
|
||||
var hasEventBridge = false;
|
||||
function wireEventBridge(on) {
|
||||
if (on) {
|
||||
if (!hasEventBridge) {
|
||||
tablet.fromQml.connect(fromQml);
|
||||
hasEventBridge = true;
|
||||
}
|
||||
} else {
|
||||
if (hasEventBridge) {
|
||||
tablet.fromQml.disconnect(fromQml);
|
||||
hasEventBridge = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var onAvatarAppScreen = false;
|
||||
function onTabletScreenChanged(type, url) {
|
||||
var onAvatarAppScreenNow = (type === "QML" && url === AVATARAPP_QML_SOURCE);
|
||||
wireEventBridge(onAvatarAppScreenNow);
|
||||
// for toolbar mode: change button to active when window is first openend, false otherwise.
|
||||
button.editProperties({isActive: onAvatarAppScreenNow});
|
||||
|
||||
if (!onAvatarAppScreen && onAvatarAppScreenNow) {
|
||||
on();
|
||||
} else if(onAvatarAppScreen && !onAvatarAppScreenNow) {
|
||||
off();
|
||||
}
|
||||
|
||||
onAvatarAppScreen = onAvatarAppScreenNow;
|
||||
|
||||
if(onAvatarAppScreenNow) {
|
||||
var message = {
|
||||
'method' : 'initialize',
|
||||
'data' : {
|
||||
'jointNames' : MyAvatar.getJointNames()
|
||||
}
|
||||
};
|
||||
|
||||
sendToQml(message)
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
if (onAvatarAppScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onTabletButtonClicked);
|
||||
tablet.removeButton(button);
|
||||
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||
|
||||
off();
|
||||
}
|
||||
|
||||
//
|
||||
// Cleanup.
|
||||
//
|
||||
Script.scriptEnding.connect(shutdown);
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
|
@ -29,7 +29,6 @@ var CONTOLLER_SCRIPTS = [
|
|||
"controllerModules/disableOtherModule.js",
|
||||
"controllerModules/farTrigger.js",
|
||||
"controllerModules/teleport.js",
|
||||
"controllerModules/scaleAvatar.js",
|
||||
"controllerModules/hudOverlayPointer.js",
|
||||
"controllerModules/mouseHMD.js",
|
||||
"controllerModules/scaleEntity.js",
|
||||
|
|