Unified · Split
| @@ -1,276 +0,0 @@ | |||
| 1 | import QtQuick 2.6 | ||
| 2 | import QtQuick.Controls 2.2 | ||
| 3 | import QtQuick.Layouts 1.3 | ||
| 4 | import QtQml.Models 2.1 | ||
| 5 | import QtGraphicalEffects 1.0 | ||
| 6 | import "../controlsUit" 1.0 as HifiControls | ||
| 7 | import "../stylesUit" 1.0 | ||
| 8 | import "../windows" as Windows | ||
| 9 | import "../dialogs" | ||
| 10 | import "./avatarPackager" 1.0 | ||
| 11 | import "avatarapp" 1.0 as AvatarApp | ||
| 12 | |||
| 13 | Windows.ScrollingWindow { | ||
| 14 | id: root | ||
| 15 | objectName: "AvatarPackager" | ||
| 16 | width: 480 | ||
| 17 | height: 706 | ||
| 18 | title: "Avatar Packager" | ||
| 19 | resizable: false | ||
| 20 | opacity: parent.opacity | ||
| 21 | destroyOnHidden: true | ||
| 22 | implicitWidth: 384; implicitHeight: 640 | ||
| 23 | minSize: Qt.vector2d(480, 706) | ||
| 24 | |||
| 25 | HifiConstants { id: hifi } | ||
| 26 | |||
| 27 | Item { | ||
| 28 | id: windowContent | ||
| 29 | height: pane.height | ||
| 30 | width: pane.width | ||
| 31 | |||
| 32 | InfoBox { | ||
| 33 | id: fileListPopup | ||
| 34 | |||
| 35 | title: "List of Files" | ||
| 36 | |||
| 37 | content: Rectangle { | ||
| 38 | id: fileList | ||
| 39 | |||
| 40 | color: "#404040" | ||
| 41 | |||
| 42 | anchors.fill: parent | ||
| 43 | anchors.topMargin: 10 | ||
| 44 | anchors.bottomMargin: 10 | ||
| 45 | anchors.leftMargin: 29 | ||
| 46 | anchors.rightMargin: 29 | ||
| 47 | |||
| 48 | clip: true | ||
| 49 | |||
| 50 | ListView { | ||
| 51 | anchors.fill: parent | ||
| 52 | model: AvatarPackagerCore.currentAvatarProject === null ? [] : AvatarPackagerCore.currentAvatarProject.projectFiles | ||
| 53 | delegate: Rectangle { | ||
| 54 | width: parent.width | ||
| 55 | height: fileText.implicitHeight + 8 | ||
| 56 | color: "#404040" | ||
| 57 | RalewaySemiBold { | ||
| 58 | id: fileText | ||
| 59 | size: 16 | ||
| 60 | elide: Text.ElideLeft | ||
| 61 | anchors.top: parent.top | ||
| 62 | anchors.left: parent.left | ||
| 63 | anchors.right: parent.right | ||
| 64 | anchors.leftMargin: 16 | ||
| 65 | anchors.rightMargin: 16 | ||
| 66 | anchors.topMargin: 4 | ||
| 67 | width: parent.width - 10 | ||
| 68 | color: "white" | ||
| 69 | text: modelData | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | Rectangle { | ||
| 77 | id: modalOverlay | ||
| 78 | anchors.fill: parent | ||
| 79 | z: 20 | ||
| 80 | color: "#a15d5d5d" | ||
| 81 | visible: false | ||
| 82 | |||
| 83 | // This mouse area captures the cursor events while the modalOverlay is active | ||
| 84 | MouseArea { | ||
| 85 | anchors.fill: parent | ||
| 86 | propagateComposedEvents: false; | ||
| 87 | hoverEnabled: true; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | AvatarApp.MessageBox { | ||
| 92 | id: popup | ||
| 93 | anchors.fill: parent | ||
| 94 | visible: false | ||
| 95 | closeOnClickOutside: true | ||
| 96 | } | ||
| 97 | |||
| 98 | Column { | ||
| 99 | id: avatarPackager | ||
| 100 | anchors.fill: parent | ||
| 101 | state: "main" | ||
| 102 | states: [ | ||
| 103 | State { | ||
| 104 | name: AvatarPackagerState.main | ||
| 105 | PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); faqEnabled: true; backButtonVisible: false } | ||
| 106 | PropertyChanges { target: avatarPackagerMain; visible: true } | ||
| 107 | PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer } | ||
| 108 | }, | ||
| 109 | State { | ||
| 110 | name: AvatarPackagerState.createProject | ||
| 111 | PropertyChanges { target: avatarPackagerHeader; title: qsTr("Create Project") } | ||
| 112 | PropertyChanges { target: createAvatarProject; visible: true } | ||
| 113 | PropertyChanges { target: avatarPackagerFooter; content: createAvatarProject.footer } | ||
| 114 | }, | ||
| 115 | State { | ||
| 116 | name: AvatarPackagerState.project | ||
| 117 | PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; canRename: true } | ||
| 118 | PropertyChanges { target: avatarProject; visible: true } | ||
| 119 | PropertyChanges { target: avatarPackagerFooter; content: avatarProject.footer } | ||
| 120 | }, | ||
| 121 | State { | ||
| 122 | name: "project-upload" | ||
| 123 | PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; backButtonEnabled: false } | ||
| 124 | PropertyChanges { target: avatarUploader; visible: true } | ||
| 125 | PropertyChanges { target: avatarPackagerFooter; visible: false } | ||
| 126 | } | ||
| 127 | ] | ||
| 128 | |||
| 129 | property alias showModalOverlay: modalOverlay.visible | ||
| 130 | |||
| 131 | function openProject(path) { | ||
| 132 | let project = AvatarPackagerCore.openAvatarProject(path); | ||
| 133 | if (project) { | ||
| 134 | avatarProject.reset(); | ||
| 135 | avatarPackager.state = "project"; | ||
| 136 | } | ||
| 137 | return project; | ||
| 138 | } | ||
| 139 | |||
| 140 | AvatarPackagerHeader { | ||
| 141 | id: avatarPackagerHeader | ||
| 142 | onBackButtonClicked: { | ||
| 143 | avatarPackager.state = "main" | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | Item { | ||
| 148 | height: pane.height - avatarPackagerHeader.height - avatarPackagerFooter.height | ||
| 149 | width: pane.width | ||
| 150 | |||
| 151 | Rectangle { | ||
| 152 | anchors.fill: parent | ||
| 153 | color: "#404040" | ||
| 154 | } | ||
| 155 | |||
| 156 | AvatarProject { | ||
| 157 | id: avatarProject | ||
| 158 | colorScheme: root.colorScheme | ||
| 159 | anchors.fill: parent | ||
| 160 | } | ||
| 161 | |||
| 162 | AvatarProjectUpload { | ||
| 163 | id: avatarUploader | ||
| 164 | anchors.fill: parent | ||
| 165 | root: avatarProject | ||
| 166 | } | ||
| 167 | |||
| 168 | CreateAvatarProject { | ||
| 169 | id: createAvatarProject | ||
| 170 | colorScheme: root.colorScheme | ||
| 171 | anchors.fill: parent | ||
| 172 | } | ||
| 173 | |||
| 174 | Item { | ||
| 175 | id: avatarPackagerMain | ||
| 176 | visible: false | ||
| 177 | anchors.fill: parent | ||
| 178 | |||
| 179 | property var footer: Item { | ||
| 180 | anchors.fill: parent | ||
| 181 | anchors.rightMargin: 17 | ||
| 182 | HifiControls.Button { | ||
| 183 | id: createProjectButton | ||
| 184 | anchors.verticalCenter: parent.verticalCenter | ||
| 185 | anchors.right: openProjectButton.left | ||
| 186 | anchors.rightMargin: 22 | ||
| 187 | height: 40 | ||
| 188 | width: 134 | ||
| 189 | text: qsTr("New Project") | ||
| 190 | colorScheme: root.colorScheme | ||
| 191 | onClicked: { | ||
| 192 | createAvatarProject.clearInputs(); | ||
| 193 | avatarPackager.state = AvatarPackagerState.createProject; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | HifiControls.Button { | ||
| 198 | id: openProjectButton | ||
| 199 | anchors.verticalCenter: parent.verticalCenter | ||
| 200 | anchors.right: parent.right | ||
| 201 | height: 40 | ||
| 202 | width: 133 | ||
| 203 | text: qsTr("Open Project") | ||
| 204 | color: hifi.buttons.blue | ||
| 205 | colorScheme: root.colorScheme | ||
| 206 | onClicked: { | ||
| 207 | avatarPackager.showModalOverlay = true; | ||
| 208 | let browser = desktop.fileDialog({ | ||
| 209 | selectDirectory: false, | ||
| 210 | dir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH), | ||
| 211 | filter: "Avatar Project FST Files (*.fst)", | ||
| 212 | title: "Open Project (.fst)", | ||
| 213 | }); | ||
| 214 | |||
| 215 | browser.canceled.connect(function() { | ||
| 216 | avatarPackager.showModalOverlay = false; | ||
| 217 | }); | ||
| 218 | |||
| 219 | browser.selectedFile.connect(function(fileUrl) { | ||
| 220 | let fstFilePath = fileDialogHelper.urlToPath(fileUrl); | ||
| 221 | let currentAvatarProject = avatarPackager.openProject(fstFilePath); | ||
| 222 | if (currentAvatarProject) { | ||
| 223 | avatarPackager.showModalOverlay = false; | ||
| 224 | } | ||
| 225 | }); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | Flow { | ||
| 231 | visible: AvatarPackagerCore.recentProjects.length === 0 | ||
| 232 | anchors { | ||
| 233 | fill: parent | ||
| 234 | topMargin: 18 | ||
| 235 | leftMargin: 16 | ||
| 236 | rightMargin: 16 | ||
| 237 | } | ||
| 238 | RalewayRegular { | ||
| 239 | size: 20 | ||
| 240 | color: "white" | ||
| 241 | text: qsTr("Use a custom avatar to express your identity") | ||
| 242 | } | ||
| 243 | RalewayRegular { | ||
| 244 | size: 20 | ||
| 245 | color: "white" | ||
| 246 | text: qsTr("To learn more about using this tool, visit our docs") | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | Column { | ||
| 251 | visible: AvatarPackagerCore.recentProjects.length > 0 | ||
| 252 | anchors { | ||
| 253 | fill: parent | ||
| 254 | topMargin: 18 | ||
| 255 | leftMargin: 16 | ||
| 256 | rightMargin: 16 | ||
| 257 | } | ||
| 258 | spacing: 10 | ||
| 259 | |||
| 260 | Repeater { | ||
| 261 | model: AvatarPackagerCore.recentProjects | ||
| 262 | AvatarProjectCard { | ||
| 263 | title: modelData.name | ||
| 264 | path: modelData.path | ||
| 265 | onOpen: avatarPackager.openProject(modelData.path) | ||
| 266 | } | ||
| 267 | } | ||
| 268 | } | ||
| 269 | } | ||
| 270 | } | ||
| 271 | AvatarPackagerFooter { | ||
| 272 | id: avatarPackagerFooter | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } | ||
| 276 | } | ||
| @@ -0,0 +1,32 @@ | |||
| 1 | import QtQuick 2.6 | ||
| 2 | import QtQuick.Controls 2.2 | ||
| 3 | import QtQuick.Layouts 1.3 | ||
| 4 | import QtQml.Models 2.1 | ||
| 5 | import QtGraphicalEffects 1.0 | ||
| 6 | import "../controlsUit" 1.0 as HifiControls | ||
| 7 | import "../stylesUit" 1.0 | ||
| 8 | import "../windows" as Windows | ||
| 9 | import "../controls" 1.0 | ||
| 10 | import "../dialogs" | ||
| 11 | import "avatarPackager" 1.0 | ||
| 12 | import "avatarapp" 1.0 as AvatarApp | ||
| 13 | |||
| 14 | Windows.ScrollingWindow { | ||
| 15 | id: root | ||
| 16 | objectName: "AvatarPackager" | ||
| 17 | width: 480 | ||
| 18 | height: 706 | ||
| 19 | title: "Avatar Packager" | ||
| 20 | resizable: false | ||
| 21 | opacity: parent.opacity | ||
| 22 | destroyOnHidden: true | ||
| 23 | implicitWidth: 384; implicitHeight: 640 | ||
| 24 | minSize: Qt.vector2d(480, 706) | ||
| 25 | |||
| 26 | HifiConstants { id: hifi } | ||
| 27 | |||
| 28 | AvatarPackagerApp { | ||
| 29 | height: pane.height | ||
| 30 | width: pane.width | ||
| 31 | } | ||
| 32 | } | ||
| @@ -0,0 +1,288 @@ | |||
| 1 | import QtQuick 2.6 | ||
| 2 | import QtQuick.Controls 2.2 | ||
| 3 | import QtQuick.Layouts 1.3 | ||
| 4 | import QtQml.Models 2.1 | ||
| 5 | import QtGraphicalEffects 1.0 | ||
| 6 | import "../../controlsUit" 1.0 as HifiControls | ||
| 7 | import "../../stylesUit" 1.0 | ||
| 8 | import "../../windows" as Windows | ||
| 9 | import "../../controls" 1.0 | ||
| 10 | import "../../dialogs" | ||
| 11 | import "../avatarapp" 1.0 as AvatarApp | ||
| 12 | |||
| 13 | Item { | ||
| 14 | HifiConstants { id: hifi } | ||
| 15 | |||
| 16 | property alias desktopObject: avatarPackager.desktopObject | ||
| 17 | |||
| 18 | id: windowContent | ||
| 19 | // height: pane ? pane.height : parent.width | ||
| 20 | // width: pane ? pane.width : parent.width | ||
| 21 | |||
| 22 | |||
| 23 | MouseArea { | ||
| 24 | anchors.fill: parent | ||
| 25 | onClicked: { | ||
| 26 | unfocusser.forceActiveFocus(); | ||
| 27 | } | ||
| 28 | Item { | ||
| 29 | id: unfocusser | ||
| 30 | visible: false | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | InfoBox { | ||
| 35 | id: fileListPopup | ||
| 36 | |||
| 37 | title: "List of Files" | ||
| 38 | |||
| 39 | content: Rectangle { | ||
| 40 | id: fileList | ||
| 41 | |||
| 42 | color: "#404040" | ||
| 43 | |||
| 44 | anchors.fill: parent | ||
| 45 | anchors.topMargin: 10 | ||
| 46 | anchors.bottomMargin: 10 | ||
| 47 | anchors.leftMargin: 29 | ||
| 48 | anchors.rightMargin: 29 | ||
| 49 | |||
| 50 | clip: true | ||
| 51 | |||
| 52 | ListView { | ||
| 53 | anchors.fill: parent | ||
| 54 | model: AvatarPackagerCore.currentAvatarProject === null ? [] : AvatarPackagerCore.currentAvatarProject.projectFiles | ||
| 55 | delegate: Rectangle { | ||
| 56 | width: parent.width | ||
| 57 | height: fileText.implicitHeight + 8 | ||
| 58 | color: "#404040" | ||
| 59 | RalewaySemiBold { | ||
| 60 | id: fileText | ||
| 61 | size: 16 | ||
| 62 | elide: Text.ElideLeft | ||
| 63 | anchors.top: parent.top | ||
| 64 | anchors.left: parent.left | ||
| 65 | anchors.right: parent.right | ||
| 66 | anchors.leftMargin: 16 | ||
| 67 | anchors.rightMargin: 16 | ||
| 68 | anchors.topMargin: 4 | ||
| 69 | width: parent.width - 10 | ||
| 70 | color: "white" | ||
| 71 | text: modelData | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | Rectangle { | ||
| 79 | id: modalOverlay | ||
| 80 | anchors.fill: parent | ||
| 81 | z: 20 | ||
| 82 | color: "#a15d5d5d" | ||
| 83 | visible: false | ||
| 84 | |||
| 85 | // This mouse area captures the cursor events while the modalOverlay is active | ||
| 86 | MouseArea { | ||
| 87 | anchors.fill: parent | ||
| 88 | propagateComposedEvents: false; | ||
| 89 | hoverEnabled: true; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | AvatarApp.MessageBox { | ||
| 94 | id: popup | ||
| 95 | anchors.fill: parent | ||
| 96 | visible: false | ||
| 97 | closeOnClickOutside: true | ||
| 98 | } | ||
| 99 | |||
| 100 | Column { | ||
| 101 | id: avatarPackager | ||
| 102 | anchors.fill: parent | ||
| 103 | state: "main" | ||
| 104 | states: [ | ||
| 105 | State { | ||
| 106 | name: AvatarPackagerState.main | ||
| 107 | PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; backButtonVisible: false } | ||
| 108 | PropertyChanges { target: avatarPackagerMain; visible: true } | ||
| 109 | PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer } | ||
| 110 | }, | ||
| 111 | State { | ||
| 112 | name: AvatarPackagerState.createProject | ||
| 113 | PropertyChanges { target: avatarPackagerHeader; title: qsTr("Create Project") } | ||
| 114 | PropertyChanges { target: createAvatarProject; visible: true } | ||
| 115 | PropertyChanges { target: avatarPackagerFooter; content: createAvatarProject.footer } | ||
| 116 | }, | ||
| 117 | State { | ||
| 118 | name: AvatarPackagerState.project | ||
| 119 | PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; canRename: true } | ||
| 120 | PropertyChanges { target: avatarProject; visible: true } | ||
| 121 | PropertyChanges { target: avatarPackagerFooter; content: avatarProject.footer } | ||
| 122 | }, | ||
| 123 | State { | ||
| 124 | name: AvatarPackagerState.projectUpload | ||
| 125 | PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; backButtonEnabled: false } | ||
| 126 | PropertyChanges { target: avatarUploader; visible: true } | ||
| 127 | PropertyChanges { target: avatarPackagerFooter; visible: false } | ||
| 128 | } | ||
| 129 | ] | ||
| 130 | |||
| 131 | property alias showModalOverlay: modalOverlay.visible | ||
| 132 | |||
| 133 | property var desktopObject: desktop | ||
| 134 | |||
| 135 | function openProject(path) { | ||
| 136 | let project = AvatarPackagerCore.openAvatarProject(path); | ||
| 137 | if (project) { | ||
| 138 | avatarProject.reset(); | ||
| 139 | avatarPackager.state = AvatarPackagerState.project; | ||
| 140 | } | ||
| 141 | return project; | ||
| 142 | } | ||
| 143 | |||
| 144 | function openDocs() { | ||
| 145 | Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/create-avatars#how-to-package-your-avatar"); | ||
| 146 | } | ||
| 147 | |||
| 148 | AvatarPackagerHeader { | ||
| 149 | id: avatarPackagerHeader | ||
| 150 | colorScheme: root.colorScheme | ||
| 151 | onBackButtonClicked: { | ||
| 152 | avatarPackager.state = AvatarPackagerState.main; | ||
| 153 | } | ||
| 154 | onDocsButtonClicked: { | ||
| 155 | avatarPackager.openDocs(); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | Item { | ||
| 160 | height: windowContent.height - avatarPackagerHeader.height - avatarPackagerFooter.height | ||
| 161 | width: windowContent.width | ||
| 162 | |||
| 163 | Rectangle { | ||
| 164 | anchors.fill: parent | ||
| 165 | color: "#404040" | ||
| 166 | } | ||
| 167 | |||
| 168 | AvatarProject { | ||
| 169 | id: avatarProject | ||
| 170 | colorScheme: root.colorScheme | ||
| 171 | anchors.fill: parent | ||
| 172 | } | ||
| 173 | |||
| 174 | AvatarProjectUpload { | ||
| 175 | id: avatarUploader | ||
| 176 | anchors.fill: parent | ||
| 177 | root: avatarProject | ||
| 178 | } | ||
| 179 | |||
| 180 | CreateAvatarProject { | ||
| 181 | id: createAvatarProject | ||
| 182 | colorScheme: root.colorScheme | ||
| 183 | anchors.fill: parent | ||
| 184 | } | ||
| 185 | |||
| 186 | Item { | ||
| 187 | id: avatarPackagerMain | ||
| 188 | visible: false | ||
| 189 | anchors.fill: parent | ||
| 190 | |||
| 191 | property var footer: Item { | ||
| 192 | anchors.fill: parent | ||
| 193 | anchors.rightMargin: 17 | ||
| 194 | HifiControls.Button { | ||
| 195 | id: createProjectButton | ||
| 196 | anchors.verticalCenter: parent.verticalCenter | ||
| 197 | anchors.right: openProjectButton.left | ||
| 198 | anchors.rightMargin: 22 | ||
| 199 | height: 40 | ||
| 200 | width: 134 | ||
| 201 | text: qsTr("New Project") | ||
| 202 | colorScheme: root.colorScheme | ||
| 203 | onClicked: { | ||
| 204 | createAvatarProject.clearInputs(); | ||
| 205 | avatarPackager.state = AvatarPackagerState.createProject; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | HifiControls.Button { | ||
| 210 | id: openProjectButton | ||
| 211 | anchors.verticalCenter: parent.verticalCenter | ||
| 212 | anchors.right: parent.right | ||
| 213 | height: 40 | ||
| 214 | width: 133 | ||
| 215 | text: qsTr("Open Project") | ||
| 216 | color: hifi.buttons.blue | ||
| 217 | colorScheme: root.colorScheme | ||
| 218 | onClicked: { | ||
| 219 | avatarPackager.showModalOverlay = true; | ||
| 220 | |||
| 221 | let browser = avatarPackager.desktopObject.fileDialog({ | ||
| 222 | selectDirectory: false, | ||
| 223 | dir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH), | ||
| 224 | filter: "Avatar Project FST Files (*.fst)", | ||
| 225 | title: "Open Project (.fst)", | ||
| 226 | }); | ||
| 227 | |||
| 228 | browser.canceled.connect(function() { | ||
| 229 | avatarPackager.showModalOverlay = false; | ||
| 230 | }); | ||
| 231 | |||
| 232 | browser.selectedFile.connect(function(fileUrl) { | ||
| 233 | let fstFilePath = fileDialogHelper.urlToPath(fileUrl); | ||
| 234 | let currentAvatarProject = avatarPackager.openProject(fstFilePath); | ||
| 235 | if (currentAvatarProject) { | ||
| 236 | avatarPackager.showModalOverlay = false; | ||
| 237 | } | ||
| 238 | }); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | Flow { | ||
| 244 | visible: AvatarPackagerCore.recentProjects.length === 0 | ||
| 245 | anchors { | ||
| 246 | fill: parent | ||
| 247 | topMargin: 18 | ||
| 248 | leftMargin: 16 | ||
| 249 | rightMargin: 16 | ||
| 250 | } | ||
| 251 | RalewayRegular { | ||
| 252 | size: 20 | ||
| 253 | color: "white" | ||
| 254 | text: qsTr("Use a custom avatar to express your identity") | ||
| 255 | } | ||
| 256 | RalewayRegular { | ||
| 257 | size: 20 | ||
| 258 | color: "white" | ||
| 259 | text: qsTr("To learn more about using this tool, visit our docs") | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | Column { | ||
| 264 | visible: AvatarPackagerCore.recentProjects.length > 0 | ||
| 265 | anchors { | ||
| 266 | fill: parent | ||
| 267 | topMargin: 18 | ||
| 268 | leftMargin: 16 | ||
| 269 | rightMargin: 16 | ||
| 270 | } | ||
| 271 | spacing: 10 | ||
| 272 | |||
| 273 | Repeater { | ||
| 274 | model: AvatarPackagerCore.recentProjects | ||
| 275 | AvatarProjectCard { | ||
| 276 | title: modelData.name | ||
| 277 | path: modelData.projectPath | ||
| 278 | onOpen: avatarPackager.openProject(modelData.path) | ||
| 279 | } | ||
| 280 | } | ||
| 281 | } | ||
| 282 | } | ||
| 283 | } | ||
| 284 | AvatarPackagerFooter { | ||
| 285 | id: avatarPackagerFooter | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| @@ -11,11 +11,18 @@ Rectangle { · 10 unmodified lines | |||
| 11 | color: "#252525" | 11 | color: "#252525" |
| 12 | 12 | ||
| 13 | property alias title: title.text | 13 | property alias title: title.text |
| 14 | property alias faqEnabled: faq.visible | 14 | property alias docsEnabled: docs.visible |
| 15 | property bool backButtonVisible: true // If false, is not visible and does not take up space | 15 | property bool backButtonVisible: true // If false, is not visible and does not take up space |
| 16 | property bool backButtonEnabled: true // If false, is not visible but does not affect space | 16 | property bool backButtonEnabled: true // If false, is not visible but does not affect space |
| 17 | property bool canRename: false; | 17 | property bool canRename: false |
| 18 | property int colorScheme | ||
| 19 | |||
| 20 | property color textColor: "white" | ||
| 21 | property color hoverTextColor: "gray" | ||
| 22 | property color pressedTextColor: "#6A6A6A" | ||
| 23 | |||
| 18 | signal backButtonClicked | 24 | signal backButtonClicked |
| 25 | signal docsButtonClicked | ||
| 19 | 26 | ||
| 20 | RalewaySemiBold { | 27 | RalewaySemiBold { |
| 21 | id: back | 28 | id: back |
| @@ -27,7 +34,7 @@ Rectangle { · 5 unmodified lines | |||
| 27 | anchors.leftMargin: 16 | 34 | anchors.leftMargin: 16 |
| 28 | anchors.verticalCenter: back.verticalCenter | 35 | anchors.verticalCenter: back.verticalCenter |
| 29 | text: "◀" | 36 | text: "◀" |
| 30 | color: "white" | 37 | color: textColor |
| 31 | MouseArea { | 38 | MouseArea { |
| 32 | anchors.fill: parent | 39 | anchors.fill: parent |
| 33 | onClicked: root.backButtonClicked() | 40 | onClicked: root.backButtonClicked() |
| @@ -37,37 +44,102 @@ Rectangle { · 3 unmodified lines | |||
| 37 | states: [ | 44 | states: [ |
| 38 | State { | 45 | State { |
| 39 | name: "hovering" | 46 | name: "hovering" |
| 40 | PropertyChanges { | 47 | PropertyChanges { target: back; color: hoverTextColor } |
| 41 | target: back | ||
| 42 | color: "gray" | ||
| 43 | } | ||
| 44 | } | 48 | } |
| 45 | ] | 49 | ] |
| 46 | } | 50 | } |
| 47 | } | 51 | } |
| 48 | 52 | Item { | |
| 49 | RalewaySemiBold { | 53 | id: titleArea |
| 50 | id: title | ||
| 51 | size: 28 | ||
| 52 | anchors.top: parent.top | 54 | anchors.top: parent.top |
| 53 | anchors.bottom: parent.bottom | 55 | anchors.bottom: parent.bottom |
| 54 | anchors.left: root.backButtonVisible ? back.right : parent.left | 56 | anchors.left: root.backButtonVisible ? back.right : parent.left |
| 55 | anchors.leftMargin: root.backButtonVisible ? 11 : 21 | 57 | anchors.leftMargin: root.backButtonVisible ? 11 : 21 |
| 56 | anchors.verticalCenter: title.verticalCenter | 58 | anchors.verticalCenter: title.verticalCenter |
| 57 | text: qsTr("Avatar Packager") | 59 | anchors.right: docs.left |
| 58 | color: "white" | 60 | states: [ |
| 61 | State { | ||
| 62 | name: "renaming" | ||
| 63 | PropertyChanges { target: title; visible: false } | ||
| 64 | PropertyChanges { target: titleInputArea; visible: true } | ||
| 65 | } | ||
| 66 | ] | ||
| 67 | |||
| 68 | RalewaySemiBold { | ||
| 69 | id: title | ||
| 70 | size: 28 | ||
| 71 | anchors.fill: parent | ||
| 72 | text: qsTr("Avatar Packager") | ||
| 73 | color: "white" | ||
| 74 | |||
| 75 | MouseArea { | ||
| 76 | anchors.fill: parent | ||
| 77 | onClicked: { | ||
| 78 | if (!root.canRename || AvatarPackagerCore.currentAvatarProject === null) { | ||
| 79 | return; | ||
| 80 | } | ||
| 81 | |||
| 82 | titleArea.state = "renaming"; | ||
| 83 | titleInput.text = AvatarPackagerCore.currentAvatarProject.name; | ||
| 84 | titleInput.selectAll(); | ||
| 85 | titleInput.forceActiveFocus(Qt.MouseFocusReason); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | Item { | ||
| 90 | id: titleInputArea | ||
| 91 | visible: false | ||
| 92 | anchors.fill: parent | ||
| 93 | |||
| 94 | HifiControls.TextField { | ||
| 95 | id: titleInput | ||
| 96 | anchors.fill: parent | ||
| 97 | text: "" | ||
| 98 | colorScheme: root.colorScheme | ||
| 99 | font.family: "Fira Sans" | ||
| 100 | font.pixelSize: 28 | ||
| 101 | z: 200 | ||
| 102 | onFocusChanged: { | ||
| 103 | if (titleArea.state === "renaming" && !titleArea.focus) { | ||
| 104 | //titleArea.state = ""; | ||
| 105 | accepted(); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | Keys.onPressed: { | ||
| 109 | if (event.key === Qt.Key_Escape) { | ||
| 110 | titleArea.state = ""; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | onAccepted: { | ||
| 114 | if (acceptableInput) { | ||
| 115 | //AvatarPackagerCore.renameProject(text); | ||
| 116 | console.warn(text); | ||
| 117 | AvatarPackagerCore.currentAvatarProject.name = text; | ||
| 118 | console.warn(AvatarPackagerCore.currentAvatarProject.name); | ||
| 119 | |||
| 120 | } | ||
| 121 | titleArea.state = ""; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 59 | } | 125 | } |
| 60 | 126 | ||
| 61 | RalewaySemiBold { | 127 | RalewaySemiBold { |
| 62 | id: faq | 128 | id: docs |
| 63 | visible: false | 129 | visible: false |
| 64 | size: 28 | 130 | size: 28 |
| 65 | anchors.top: parent.top | 131 | anchors.top: parent.top |
| 66 | anchors.bottom: parent.bottom | 132 | anchors.bottom: parent.bottom |
| 67 | anchors.right: parent.right | 133 | anchors.right: parent.right |
| 68 | anchors.rightMargin: 16 | 134 | anchors.rightMargin: 16 |
| 69 | anchors.verticalCenter: faq.verticalCenter | 135 | anchors.verticalCenter: docs.verticalCenter |
| 70 | text: qsTr("Docs") | 136 | text: qsTr("Docs") |
| 71 | color: "white" | 137 | color: "white" |
| 138 | MouseArea { | ||
| 139 | anchors.fill: parent | ||
| 140 | onClicked: { | ||
| 141 | docsButtonClicked(); | ||
| 142 | } | ||
| 143 | } | ||
| 72 | } | 144 | } |
| 73 | } | 145 | } |
| @@ -6,4 +6,5 @@ Item { · 5 unmodified lines | |||
| 6 | readonly property string main: "main" | 6 | readonly property string main: "main" |
| 7 | readonly property string project: "project" | 7 | readonly property string project: "project" |
| 8 | readonly property string createProject: "createProject" | 8 | readonly property string createProject: "createProject" |
| 9 | readonly property string projectUpload: "projectUpload" | ||
| 9 | } | 10 | } |
| @@ -178,7 +178,7 @@ Item { · 177 unmodified lines | |||
| 178 | } | 178 | } |
| 179 | }); | 179 | }); |
| 180 | root.uploader.send(); | 180 | root.uploader.send(); |
| 181 | avatarPackager.state = "project-upload"; | 181 | avatarPackager.state = AvatarPackagerState.projectUpload; |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | function showConfirmUploadPopup() { | 184 | function showConfirmUploadPopup() { |
| @@ -53,11 +53,14 @@ Item { · 52 unmodified lines | |||
| 53 | 53 | ||
| 54 | RalewayBold { | 54 | RalewayBold { |
| 55 | id: title | 55 | id: title |
| 56 | elide: "ElideRight" | ||
| 56 | anchors { | 57 | anchors { |
| 57 | top: parent.top | 58 | top: parent.top |
| 58 | topMargin: 13 | 59 | topMargin: 13 |
| 59 | left: parent.left | 60 | left: parent.left |
| 60 | leftMargin: 16 | 61 | leftMargin: 16 |
| 62 | right: parent.right | ||
| 63 | rightMargin: 16 | ||
| 61 | } | 64 | } |
| 62 | text: "<title missing>" | 65 | text: "<title missing>" |
| 63 | size: 16 | 66 | size: 16 |
| @@ -69,7 +72,11 @@ Item { · 5 unmodified lines | |||
| 69 | top: title.bottom | 72 | top: title.bottom |
| 70 | left: parent.left | 73 | left: parent.left |
| 71 | leftMargin: 32 | 74 | leftMargin: 32 |
| 75 | right: background.right | ||
| 76 | rightMargin: 16 | ||
| 72 | } | 77 | } |
| 78 | elide: "ElideLeft" | ||
| 79 | horizontalAlignment: Text.AlignRight | ||
| 73 | text: "<path missing>" | 80 | text: "<path missing>" |
| 74 | size: 20 | 81 | size: 20 |
| 75 | } | 82 | } |
| @@ -21,8 +21,8 @@ Item { · 20 unmodified lines | |||
| 21 | running: false | 21 | running: false |
| 22 | repeat: false | 22 | repeat: false |
| 23 | onTriggered: { | 23 | onTriggered: { |
| 24 | if (avatarPackager.state =="project-upload") { | 24 | if (avatarPackager.state === AvatarPackagerState.projectUpload) { |
| 25 | avatarPackager.state = "project" | 25 | avatarPackager.state = AvatarPackagerState.project; |
| 26 | } | 26 | } |
| 27 | } | 27 | } |
| 28 | } | 28 | } |
| @@ -130,7 +130,7 @@ Item { · 101 unmodified lines | |||
| 130 | AvatarUploadStatusItem { | 130 | AvatarUploadStatusItem { |
| 131 | id: statusCategories | 131 | id: statusCategories |
| 132 | uploader: root.uploader | 132 | uploader: root.uploader |
| 133 | text: "Retreiving categories" | 133 | text: "Retrieving categories" |
| 134 | 134 | ||
| 135 | uploaderState: 1 | 135 | uploaderState: 1 |
| 136 | } | 136 | } |
| @@ -189,8 +189,8 @@ Item { · 52 unmodified lines | |||
| 189 | colorScheme: root.colorScheme | 189 | colorScheme: root.colorScheme |
| 190 | width: 133 | 190 | width: 133 |
| 191 | height: 40 | 191 | height: 40 |
| 192 | onClicked: function() { | 192 | onClicked: { |
| 193 | avatarPackager.state = "project" | 193 | avatarPackager.state = AvatarPackagerState.project; |
| 194 | } | 194 | } |
| 195 | } | 195 | } |
| 196 | } | 196 | } |
| @@ -14,9 +14,9 @@ Item { · 13 unmodified lines | |||
| 14 | property int uploaderState; | 14 | property int uploaderState; |
| 15 | property var uploader; | 15 | property var uploader; |
| 16 | 16 | ||
| 17 | state: root.uploader.state > uploaderState | 17 | state: root.uploader === undefined ? "" : |
| 18 | ? "success" | 18 | (root.uploader.state > uploaderState ? "success" |
| 19 | : (root.uploader.error !== 0 ? "fail" : (root.uploader.state === uploaderState ? "running" : "")) | 19 | : (root.uploader.error !== 0 ? "fail" : (root.uploader.state === uploaderState ? "running" : ""))) |
| 20 | 20 | ||
| 21 | states: [ | 21 | states: [ |
| 22 | State { | 22 | State { |
| @@ -90,4 +90,4 @@ Item { · 67 unmodified lines | |||
| 90 | size: 28 | 90 | size: 28 |
| 91 | color: "#777777" | 91 | color: "#777777" |
| 92 | } | 92 | } |
| 93 | } | 93 | } |
| @@ -119,7 +119,7 @@ Item { · 118 unmodified lines | |||
| 119 | 119 | ||
| 120 | ProjectInputControl { | 120 | ProjectInputControl { |
| 121 | id: textureFolder | 121 | id: textureFolder |
| 122 | label: "Specify Texture Folder <i> - optional</i>" | 122 | label: "Specify Texture Folder - <i>Optional</i>" |
| 123 | colorScheme: root.colorScheme | 123 | colorScheme: root.colorScheme |
| 124 | browseEnabled: true | 124 | browseEnabled: true |
| 125 | browseFolder: true | 125 | browseFolder: true |
| @@ -128,5 +128,4 @@ Item { · 2 unmodified lines | |||
| 128 | onTextChanged: checkErrors() | 128 | onTextChanged: checkErrors() |
| 129 | } | 129 | } |
| 130 | } | 130 | } |
| 131 | |||
| 132 | } | 131 | } |
| @@ -57,7 +57,7 @@ Column { · 56 unmodified lines | |||
| 57 | colorScheme: root.colorScheme | 57 | colorScheme: root.colorScheme |
| 58 | onClicked: { | 58 | onClicked: { |
| 59 | avatarPackager.showModalOverlay = true; | 59 | avatarPackager.showModalOverlay = true; |
| 60 | let browser = desktop.fileDialog({ | 60 | let browser = avatarPackager.desktopObject.fileDialog({ |
| 61 | selectDirectory: browseFolder, | 61 | selectDirectory: browseFolder, |
| 62 | dir: browseDir, | 62 | dir: browseDir, |
| 63 | filter: browseFilter, | 63 | filter: browseFilter, |
| @@ -0,0 +1,15 @@ | |||
| 1 | import QtQuick 2.0 | ||
| 2 | import "../avatarPackager" 1.0 | ||
| 3 | |||
| 4 | Item { | ||
| 5 | id: root | ||
| 6 | width: 480 | ||
| 7 | height: 706 | ||
| 8 | |||
| 9 | AvatarPackagerApp { | ||
| 10 | width: parent.width | ||
| 11 | height: parent.height | ||
| 12 | |||
| 13 | desktopObject: tabletRoot | ||
| 14 | } | ||
| 15 | } | ||
| @@ -11,6 +11,8 @@ · 10 unmodified lines | |||
| 11 | 11 | ||
| 12 | #include "AvatarPackager.h" | 12 | #include "AvatarPackager.h" |
| 13 | 13 | ||
| 14 | #include "Application.h" | ||
| 15 | |||
| 14 | #include <QQmlContext> | 16 | #include <QQmlContext> |
| 15 | #include <QQmlEngine> | 17 | #include <QQmlEngine> |
| 16 | #include <QUrl> | 18 | #include <QUrl> |
| @@ -19,8 +21,9 @@ · 2 unmodified lines | |||
| 19 | #include "ModelSelector.h" | 21 | #include "ModelSelector.h" |
| 20 | #include <avatar/MarketplaceItemUploader.h> | 22 | #include <avatar/MarketplaceItemUploader.h> |
| 21 | 23 | ||
| 22 | #include <thread> | ||
| 23 | #include <mutex> | 24 | #include <mutex> |
| 25 | #include "scripting/HMDScriptingInterface.h" | ||
| 26 | #include "ui/TabletScriptingInterface.h" | ||
| 24 | 27 | ||
| 25 | std::once_flag setupQMLTypesFlag; | 28 | std::once_flag setupQMLTypesFlag; |
| 26 | AvatarPackager::AvatarPackager() { | 29 | AvatarPackager::AvatarPackager() { |
| @@ -38,31 +41,32 @@ AvatarPackager::AvatarPackager() { · 11 unmodified lines | |||
| 38 | } | 41 | } |
| 39 | 42 | ||
| 40 | bool AvatarPackager::open() { | 43 | bool AvatarPackager::open() { |
| 41 | static const QUrl url{ "hifi/AvatarPackager.qml" }; | 44 | static const QUrl url{ "hifi/AvatarPackagerWindow.qml" }; |
| 42 | 45 | ||
| 43 | const auto packageModelDialogCreated = [=](QQmlContext* context, QObject* newObject) { | 46 | const auto packageModelDialogCreated = [=](QQmlContext* context, QObject* newObject) { |
| 44 | context->setContextProperty("AvatarPackagerCore", this); | 47 | context->setContextProperty("AvatarPackagerCore", this); |
| 45 | }; | 48 | }; |
| 46 | DependencyManager::get<OffscreenUi>()->show(url, "AvatarPackager", packageModelDialogCreated); | ||
| 47 | return true; | ||
| 48 | } | ||
| 49 | 49 | ||
| 50 | AvatarProject* AvatarPackager::openAvatarProject(const QString& avatarProjectFSTPath) { | 50 | static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; |
| 51 | if (_currentAvatarProject) { | 51 | auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); |
| 52 | _currentAvatarProject->deleteLater(); | 52 | auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); |
| 53 | } | 53 | auto hmd = DependencyManager::get<HMDScriptingInterface>(); |
| 54 | _currentAvatarProject = AvatarProject::openAvatarProject(avatarProjectFSTPath); | 54 | |
| 55 | if (_currentAvatarProject) { | 55 | if (tablet->getToolbarMode()) { |
| 56 | addRecentProject(avatarProjectFSTPath, _currentAvatarProject->getProjectName()); | 56 | DependencyManager::get<OffscreenUi>()->show(url, "AvatarPackager", packageModelDialogCreated); |
| 57 | } else { | ||
| 58 | static const QUrl url("hifi/tablet/AvatarPackager.qml"); | ||
| 59 | if (!tablet->isPathLoaded(url)) { | ||
| 60 | tablet->getTabletSurface()->getSurfaceContext()->setContextProperty("AvatarPackagerCore", this); | ||
| 61 | tablet->pushOntoStack(url); | ||
| 62 | } | ||
| 57 | } | 63 | } |
| 58 | qDebug() << "_currentAvatarProject has" << (QQmlEngine::objectOwnership(_currentAvatarProject) == QQmlEngine::CppOwnership ? "CPP" : "JS") << "OWNERSHIP"; | 64 | return true; |
| 59 | QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership); | ||
| 60 | emit avatarProjectChanged(); | ||
| 61 | return _currentAvatarProject; | ||
| 62 | } | 65 | } |
| 63 | 66 | ||
| 64 | void AvatarPackager::addRecentProject(QString fstPath, QString projectName) { | 67 | void AvatarPackager::addCurrentProjectToRecentProjects() { |
| 65 | const int MAX_RECENT_PROJECTS = 5; | 68 | const int MAX_RECENT_PROJECTS = 5; |
| 69 | const QString& fstPath = _currentAvatarProject->getFSTPath(); | ||
| 66 | auto removeProjects = QVector<RecentAvatarProject>(); | 70 | auto removeProjects = QVector<RecentAvatarProject>(); |
| 67 | for (auto project : _recentProjects) { | 71 | for (auto project : _recentProjects) { |
| 68 | if (project.getProjectFSTPath() == fstPath) { | 72 | if (project.getProjectFSTPath() == fstPath) { |
| @@ -73,27 +77,61 @@ void AvatarPackager::addRecentProject(QString fstPath, QString projectName) { · 4 unmodified lines | |||
| 73 | _recentProjects.removeOne(removeProject); | 77 | _recentProjects.removeOne(removeProject); |
| 74 | } | 78 | } |
| 75 | 79 | ||
| 76 | RecentAvatarProject newRecentProject = RecentAvatarProject(projectName, fstPath); | 80 | const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath); |
| 77 | _recentProjects.prepend(newRecentProject); | 81 | _recentProjects.prepend(newRecentProject); |
| 78 | 82 | ||
| 79 | while (_recentProjects.size() > MAX_RECENT_PROJECTS) { | 83 | while (_recentProjects.size() > MAX_RECENT_PROJECTS) { |
| 80 | _recentProjects.pop_back(); | 84 | _recentProjects.pop_back(); |
| 81 | } | 85 | } |
| 82 | 86 | ||
| 83 | _recentProjectsSetting.set(recentProjectsToVariantList()); | 87 | _recentProjectsSetting.set(recentProjectsToVariantList(false)); |
| 84 | emit recentProjectsChanged(); | 88 | emit recentProjectsChanged(); |
| 85 | } | 89 | } |
| 86 | 90 | ||
| 91 | QVariantList AvatarPackager::recentProjectsToVariantList(bool includeProjectPaths) { | ||
| 92 | QVariantList result; | ||
| 93 | for (const auto& project : _recentProjects) { | ||
| 94 | QVariantMap projectVariant; | ||
| 95 | projectVariant.insert("name", project.getProjectName()); | ||
| 96 | projectVariant.insert("path", project.getProjectFSTPath()); | ||
| 97 | if (includeProjectPaths) { | ||
| 98 | projectVariant.insert("projectPath", project.getProjectPath()); | ||
| 99 | } | ||
| 100 | result.append(projectVariant); | ||
| 101 | } | ||
| 102 | |||
| 103 | return result; | ||
| 104 | } | ||
| 105 | void AvatarPackager::recentProjectsFromVariantList(QVariantList projectsVariant) { | ||
| 106 | _recentProjects.clear(); | ||
| 107 | for (const auto& projectVariant : projectsVariant) { | ||
| 108 | auto map = projectVariant.toMap(); | ||
| 109 | _recentProjects.append(RecentAvatarProject(map.value("name").toString(), map.value("path").toString())); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | AvatarProject* AvatarPackager::openAvatarProject(const QString& avatarProjectFSTPath) { | ||
| 114 | setAvatarProject(AvatarProject::openAvatarProject(avatarProjectFSTPath)); | ||
| 115 | return _currentAvatarProject; | ||
| 116 | } | ||
| 117 | |||
| 87 | AvatarProject* AvatarPackager::createAvatarProject(const QString& projectsFolder, const QString& avatarProjectName, const QString& avatarModelPath, const QString& textureFolder) { | 118 | AvatarProject* AvatarPackager::createAvatarProject(const QString& projectsFolder, const QString& avatarProjectName, const QString& avatarModelPath, const QString& textureFolder) { |
| 119 | setAvatarProject(AvatarProject::createAvatarProject(projectsFolder, avatarProjectName, avatarModelPath, textureFolder)); | ||
| 120 | return _currentAvatarProject; | ||
| 121 | } | ||
| 122 | |||
| 123 | void AvatarPackager::setAvatarProject(AvatarProject* avatarProject) { | ||
| 124 | if (avatarProject == _currentAvatarProject) { | ||
| 125 | return; | ||
| 126 | } | ||
| 88 | if (_currentAvatarProject) { | 127 | if (_currentAvatarProject) { |
| 89 | _currentAvatarProject->deleteLater(); | 128 | _currentAvatarProject->deleteLater(); |
| 90 | } | 129 | } |
| 91 | _currentAvatarProject = AvatarProject::createAvatarProject(projectsFolder, avatarProjectName, avatarModelPath, textureFolder); | 130 | _currentAvatarProject = avatarProject; |
| 92 | if (_currentAvatarProject) { | 131 | if (_currentAvatarProject) { |
| 93 | addRecentProject(_currentAvatarProject->getFSTPath(), _currentAvatarProject->getProjectName()); | 132 | addCurrentProjectToRecentProjects(); |
| 133 | connect(_currentAvatarProject, &AvatarProject::nameChanged, this, &AvatarPackager::addCurrentProjectToRecentProjects); | ||
| 134 | QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership); | ||
| 94 | } | 135 | } |
| 95 | qDebug() << "_currentAvatarProject has" << (QQmlEngine::objectOwnership(_currentAvatarProject) == QQmlEngine::CppOwnership ? "CPP" : "JS") << "OWNERSHIP"; | ||
| 96 | QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership); | ||
| 97 | emit avatarProjectChanged(); | 136 | emit avatarProjectChanged(); |
| 98 | return _currentAvatarProject; | ||
| 99 | } | 137 | } |
| @@ -35,12 +35,14 @@ public: · 34 unmodified lines | |||
| 35 | _projectFSTPath = other._projectFSTPath; | 35 | _projectFSTPath = other._projectFSTPath; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | ~RecentAvatarProject() = default; | ||
| 39 | |||
| 40 | QString getProjectName() const { return _projectName; } | 38 | QString getProjectName() const { return _projectName; } |
| 41 | 39 | ||
| 42 | QString getProjectFSTPath() const { return _projectFSTPath; } | 40 | QString getProjectFSTPath() const { return _projectFSTPath; } |
| 43 | 41 | ||
| 42 | QString getProjectPath() const { | ||
| 43 | return QFileInfo(_projectFSTPath).absoluteDir().absolutePath(); | ||
| 44 | } | ||
| 45 | |||
| 44 | bool operator==(const RecentAvatarProject& other) const { | 46 | bool operator==(const RecentAvatarProject& other) const { |
| 45 | return _projectName == other._projectName && _projectFSTPath == other._projectFSTPath; | 47 | return _projectName == other._projectName && _projectFSTPath == other._projectFSTPath; |
| 46 | } | 48 | } |
| @@ -72,31 +74,18 @@ signals: · 25 unmodified lines | |||
| 72 | private: | 74 | private: |
| 73 | Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; }; | 75 | Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; }; |
| 74 | Q_INVOKABLE QString getAvatarProjectsPath() const { return AvatarProject::getDefaultProjectsPath(); } | 76 | Q_INVOKABLE QString getAvatarProjectsPath() const { return AvatarProject::getDefaultProjectsPath(); } |
| 75 | Q_INVOKABLE QVariantList getRecentProjects() { return recentProjectsToVariantList(); } | 77 | Q_INVOKABLE QVariantList getRecentProjects() { return recentProjectsToVariantList(true); } |
| 78 | |||
| 79 | void setAvatarProject(AvatarProject* avatarProject); | ||
| 76 | 80 | ||
| 77 | void addRecentProject(QString fstPath, QString projectName); | 81 | void addCurrentProjectToRecentProjects(); |
| 78 | 82 | ||
| 79 | AvatarProject* _currentAvatarProject{ nullptr }; | 83 | AvatarProject* _currentAvatarProject{ nullptr }; |
| 80 | QVector<RecentAvatarProject> _recentProjects; | 84 | QVector<RecentAvatarProject> _recentProjects; |
| 81 | QVariantList recentProjectsToVariantList() { | ||
| 82 | QVariantList result; | ||
| 83 | for (const auto& project : _recentProjects) { | ||
| 84 | QVariantMap projectVariant; | ||
| 85 | projectVariant.insert("name", project.getProjectName()); | ||
| 86 | projectVariant.insert("path", project.getProjectFSTPath()); | ||
| 87 | result.append(projectVariant); | ||
| 88 | } | ||
| 89 | |||
| 90 | return result; | ||
| 91 | } | ||
| 92 | 85 | ||
| 93 | void recentProjectsFromVariantList(QVariantList projectsVariant) { | 86 | QVariantList recentProjectsToVariantList(bool includeProjectPaths); |
| 94 | _recentProjects.clear(); | 87 | |
| 95 | for (const auto& projectVariant : projectsVariant) { | 88 | void recentProjectsFromVariantList(QVariantList projectsVariant); |
| 96 | auto map = projectVariant.toMap(); | ||
| 97 | _recentProjects.append(RecentAvatarProject(map.value("name").toString(), map.value("path").toString())); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | 89 | ||
| 101 | 90 | ||
| 102 | Setting::Handle<QVariantList> _recentProjectsSetting{ "io.highfidelity.avatarPackager.recentProjects", QVariantList() }; | 91 | Setting::Handle<QVariantList> _recentProjectsSetting{ "io.highfidelity.avatarPackager.recentProjects", QVariantList() }; |
| @@ -81,18 +81,18 @@ AvatarProject* AvatarProject::createAvatarProject(const QString& projectsFolder, · 80 unmodified lines | |||
| 81 | } | 81 | } |
| 82 | }; | 82 | }; |
| 83 | 83 | ||
| 84 | foreach(const HFMMaterial mat, hfmModel->materials) { | 84 | foreach(const HFMMaterial material, hfmModel->materials) { |
| 85 | addTextureToList(mat.normalTexture); | 85 | addTextureToList(material.normalTexture); |
| 86 | addTextureToList(mat.albedoTexture); | 86 | addTextureToList(material.albedoTexture); |
| 87 | addTextureToList(mat.opacityTexture); | 87 | addTextureToList(material.opacityTexture); |
| 88 | addTextureToList(mat.glossTexture); | 88 | addTextureToList(material.glossTexture); |
| 89 | addTextureToList(mat.roughnessTexture); | 89 | addTextureToList(material.roughnessTexture); |
| 90 | addTextureToList(mat.specularTexture); | 90 | addTextureToList(material.specularTexture); |
| 91 | addTextureToList(mat.metallicTexture); | 91 | addTextureToList(material.metallicTexture); |
| 92 | addTextureToList(mat.emissiveTexture); | 92 | addTextureToList(material.emissiveTexture); |
| 93 | addTextureToList(mat.occlusionTexture); | 93 | addTextureToList(material.occlusionTexture); |
| 94 | addTextureToList(mat.scatteringTexture); | 94 | addTextureToList(material.scatteringTexture); |
| 95 | addTextureToList(mat.lightmapTexture); | 95 | addTextureToList(material.lightmapTexture); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | QDir textureDir(textureFolder.isEmpty() ? fbxInfo.absoluteDir() : textureFolder); | 98 | QDir textureDir(textureFolder.isEmpty() ? fbxInfo.absoluteDir() : textureFolder); |
| @@ -152,7 +152,6 @@ AvatarProject::AvatarProject(FST* fst) { · 53 unmodified lines | |||
| 152 | _fst->setScriptPaths(getScriptPaths(QDir(_directory.path() + "/scripts"))); | 152 | _fst->setScriptPaths(getScriptPaths(QDir(_directory.path() + "/scripts"))); |
| 153 | _fst->write(); | 153 | _fst->write(); |
| 154 | 154 | ||
| 155 | //_projectFiles = _directory.entryList(); | ||
| 156 | refreshProjectFiles(); | 155 | refreshProjectFiles(); |
| 157 | 156 | ||
| 158 | _projectPath = fileInfo.absoluteDir().absolutePath(); | 157 | _projectPath = fileInfo.absoluteDir().absolutePath(); |
| @@ -28,10 +28,10 @@ class AvatarProject : public QObject { · 27 unmodified lines | |||
| 28 | 28 | ||
| 29 | Q_PROPERTY(QStringList projectFiles READ getProjectFiles NOTIFY projectFilesChanged) | 29 | Q_PROPERTY(QStringList projectFiles READ getProjectFiles NOTIFY projectFilesChanged) |
| 30 | 30 | ||
| 31 | Q_PROPERTY(QString projectFolderPath READ getProjectPath) | 31 | Q_PROPERTY(QString projectFolderPath READ getProjectPath CONSTANT) |
| 32 | Q_PROPERTY(QString projectFSTPath READ getFSTPath) | 32 | Q_PROPERTY(QString projectFSTPath READ getFSTPath CONSTANT) |
| 33 | Q_PROPERTY(QString projectFBXPath READ getFBXPath) | 33 | Q_PROPERTY(QString projectFBXPath READ getFBXPath CONSTANT) |
| 34 | Q_PROPERTY(QString name READ getProjectName NOTIFY nameChanged) | 34 | Q_PROPERTY(QString name READ getProjectName WRITE setProjectName NOTIFY nameChanged) |
| 35 | 35 | ||
| 36 | public: | 36 | public: |
| 37 | Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting); | 37 | Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting); |
| @@ -39,6 +39,13 @@ public: · 1 unmodified line | |||
| 39 | Q_INVOKABLE QStringList getProjectFiles() const; | 39 | Q_INVOKABLE QStringList getProjectFiles() const; |
| 40 | 40 | ||
| 41 | Q_INVOKABLE QString getProjectName() const { return _fst->getName(); } | 41 | Q_INVOKABLE QString getProjectName() const { return _fst->getName(); } |
| 42 | Q_INVOKABLE void setProjectName(const QString& newProjectName) { | ||
| 43 | if (newProjectName.trimmed().length() > 0) { | ||
| 44 | _fst->setName(newProjectName); | ||
| 45 | _fst->write(); | ||
| 46 | emit nameChanged(); | ||
| 47 | } | ||
| 48 | } | ||
| 42 | Q_INVOKABLE QString getProjectPath() const { return _projectPath; } | 49 | Q_INVOKABLE QString getProjectPath() const { return _projectPath; } |
| 43 | Q_INVOKABLE QString getFSTPath() const { return _fst->getPath(); } | 50 | Q_INVOKABLE QString getFSTPath() const { return _fst->getPath(); } |
| 44 | Q_INVOKABLE QString getFBXPath() const { return _fst->getModelPath(); } | 51 | Q_INVOKABLE QString getFBXPath() const { return _fst->getModelPath(); } |
| @@ -15,7 +15,7 @@ · 14 unmodified lines | |||
| 15 | #include <QFileInfo> | 15 | #include <QFileInfo> |
| 16 | #include <hfm/HFM.h> | 16 | #include <hfm/HFM.h> |
| 17 | 17 | ||
| 18 | FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) { | 18 | FST::FST(QString fstPath, QVariantHash data) : _fstPath(std::move(fstPath)) { |
| 19 | 19 | ||
| 20 | auto setValueFromFSTData = [&data] (const QString& propertyID, auto &targetProperty) mutable { | 20 | auto setValueFromFSTData = [&data] (const QString& propertyID, auto &targetProperty) mutable { |
| 21 | if (data.contains(propertyID)) { | 21 | if (data.contains(propertyID)) { |
| @@ -38,7 +38,7 @@ FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) { · 16 unmodified lines | |||
| 38 | _other = data; | 38 | _other = data; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | FST* FST::createFSTFromModel(QString fstPath, QString modelFilePath, const hfm::Model& hfmModel) { | 41 | FST* FST::createFSTFromModel(const QString& fstPath, const QString& modelFilePath, const hfm::Model& hfmModel) { |
| 42 | QVariantHash mapping; | 42 | QVariantHash mapping; |
| 43 | 43 | ||
| 44 | // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will | 44 | // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will |
| @@ -26,9 +26,9 @@ class FST : public QObject { · 25 unmodified lines | |||
| 26 | Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID) | 26 | Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID) |
| 27 | Q_PROPERTY(bool hasMarketplaceID READ getHasMarketplaceID NOTIFY marketplaceIDChanged) | 27 | Q_PROPERTY(bool hasMarketplaceID READ getHasMarketplaceID NOTIFY marketplaceIDChanged) |
| 28 | public: | 28 | public: |
| 29 | FST(const QString& fstPath, QVariantHash data); | 29 | FST(QString fstPath, QVariantHash data); |
| 30 | 30 | ||
| 31 | static FST* createFSTFromModel(QString fstPath, QString modelFilePath, const hfm::Model& hfmModel); | 31 | static FST* createFSTFromModel(const QString& fstPath, const QString& modelFilePath, const hfm::Model& hfmModel); |
| 32 | 32 | ||
| 33 | QString absoluteModelPath() const; | 33 | QString absoluteModelPath() const; |
| 34 | 34 | ||