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 | 11 | color: "#252525" |
| 12 | 12 | |
| 13 | 13 | property alias title: title.text |
| 14 | − property alias faqEnabled: faq.visible | |
| 14 | + property alias docsEnabled: docs.visible | |
| 15 | 15 | property bool backButtonVisible: true // If false, is not visible and does not take up space |
| 16 | 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 | 24 | signal backButtonClicked |
| 25 | + signal docsButtonClicked | |
| 19 | 26 | |
| 20 | 27 | RalewaySemiBold { |
| 21 | 28 | id: back |
| @@ -27,7 +34,7 @@ Rectangle { · 5 unmodified lines | ||
| 27 | 34 | anchors.leftMargin: 16 |
| 28 | 35 | anchors.verticalCenter: back.verticalCenter |
| 29 | 36 | text: "◀" |
| 30 | − color: "white" | |
| 37 | + color: textColor | |
| 31 | 38 | MouseArea { |
| 32 | 39 | anchors.fill: parent |
| 33 | 40 | onClicked: root.backButtonClicked() |
| @@ -37,37 +44,102 @@ Rectangle { · 3 unmodified lines | ||
| 37 | 44 | states: [ |
| 38 | 45 | State { |
| 39 | 46 | name: "hovering" |
| 40 | − PropertyChanges { | |
| 41 | − target: back | |
| 42 | − color: "gray" | |
| 43 | − } | |
| 47 | + PropertyChanges { target: back; color: hoverTextColor } | |
| 44 | 48 | } |
| 45 | 49 | ] |
| 46 | 50 | } |
| 47 | 51 | } |
| 48 | − | |
| 49 | − RalewaySemiBold { | |
| 50 | − id: title | |
| 51 | − size: 28 | |
| 52 | + Item { | |
| 53 | + id: titleArea | |
| 52 | 54 | anchors.top: parent.top |
| 53 | 55 | anchors.bottom: parent.bottom |
| 54 | 56 | anchors.left: root.backButtonVisible ? back.right : parent.left |
| 55 | 57 | anchors.leftMargin: root.backButtonVisible ? 11 : 21 |
| 56 | 58 | anchors.verticalCenter: title.verticalCenter |
| 57 | − text: qsTr("Avatar Packager") | |
| 58 | − color: "white" | |
| 59 | + anchors.right: docs.left | |
| 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 | 127 | RalewaySemiBold { |
| 62 | − id: faq | |
| 128 | + id: docs | |
| 63 | 129 | visible: false |
| 64 | 130 | size: 28 |
| 65 | 131 | anchors.top: parent.top |
| 66 | 132 | anchors.bottom: parent.bottom |
| 67 | 133 | anchors.right: parent.right |
| 68 | 134 | anchors.rightMargin: 16 |
| 69 | − anchors.verticalCenter: faq.verticalCenter | |
| 135 | + anchors.verticalCenter: docs.verticalCenter | |
| 70 | 136 | text: qsTr("Docs") |
| 71 | 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 | 6 | readonly property string main: "main" |
| 7 | 7 | readonly property string project: "project" |
| 8 | 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 | 180 | root.uploader.send(); |
| 181 | − avatarPackager.state = "project-upload"; | |
| 181 | + avatarPackager.state = AvatarPackagerState.projectUpload; | |
| 182 | 182 | } |
| 183 | 183 | |
| 184 | 184 | function showConfirmUploadPopup() { |
| @@ -53,11 +53,14 @@ Item { · 52 unmodified lines | ||
| 53 | 53 | |
| 54 | 54 | RalewayBold { |
| 55 | 55 | id: title |
| 56 | + elide: "ElideRight" | |
| 56 | 57 | anchors { |
| 57 | 58 | top: parent.top |
| 58 | 59 | topMargin: 13 |
| 59 | 60 | left: parent.left |
| 60 | 61 | leftMargin: 16 |
| 62 | + right: parent.right | |
| 63 | + rightMargin: 16 | |
| 61 | 64 | } |
| 62 | 65 | text: "<title missing>" |
| 63 | 66 | size: 16 |
| @@ -69,7 +72,11 @@ Item { · 5 unmodified lines | ||
| 69 | 72 | top: title.bottom |
| 70 | 73 | left: parent.left |
| 71 | 74 | leftMargin: 32 |
| 75 | + right: background.right | |
| 76 | + rightMargin: 16 | |
| 72 | 77 | } |
| 78 | + elide: "ElideLeft" | |
| 79 | + horizontalAlignment: Text.AlignRight | |
| 73 | 80 | text: "<path missing>" |
| 74 | 81 | size: 20 |
| 75 | 82 | } |
| @@ -21,8 +21,8 @@ Item { · 20 unmodified lines | ||
| 21 | 21 | running: false |
| 22 | 22 | repeat: false |
| 23 | 23 | onTriggered: { |
| 24 | − if (avatarPackager.state =="project-upload") { | |
| 25 | − avatarPackager.state = "project" | |
| 24 | + if (avatarPackager.state === AvatarPackagerState.projectUpload) { | |
| 25 | + avatarPackager.state = AvatarPackagerState.project; | |
| 26 | 26 | } |
| 27 | 27 | } |
| 28 | 28 | } |
| @@ -130,7 +130,7 @@ Item { · 101 unmodified lines | ||
| 130 | 130 | AvatarUploadStatusItem { |
| 131 | 131 | id: statusCategories |
| 132 | 132 | uploader: root.uploader |
| 133 | − text: "Retreiving categories" | |
| 133 | + text: "Retrieving categories" | |
| 134 | 134 | |
| 135 | 135 | uploaderState: 1 |
| 136 | 136 | } |
| @@ -189,8 +189,8 @@ Item { · 52 unmodified lines | ||
| 189 | 189 | colorScheme: root.colorScheme |
| 190 | 190 | width: 133 |
| 191 | 191 | height: 40 |
| 192 | − onClicked: function() { | |
| 193 | − avatarPackager.state = "project" | |
| 192 | + onClicked: { | |
| 193 | + avatarPackager.state = AvatarPackagerState.project; | |
| 194 | 194 | } |
| 195 | 195 | } |
| 196 | 196 | } |
| @@ -14,9 +14,9 @@ Item { · 13 unmodified lines | ||
| 14 | 14 | property int uploaderState; |
| 15 | 15 | property var uploader; |
| 16 | 16 | |
| 17 | − state: root.uploader.state > uploaderState | |
| 18 | − ? "success" | |
| 19 | − : (root.uploader.error !== 0 ? "fail" : (root.uploader.state === uploaderState ? "running" : "")) | |
| 17 | + state: root.uploader === undefined ? "" : | |
| 18 | + (root.uploader.state > uploaderState ? "success" | |
| 19 | + : (root.uploader.error !== 0 ? "fail" : (root.uploader.state === uploaderState ? "running" : ""))) | |
| 20 | 20 | |
| 21 | 21 | states: [ |
| 22 | 22 | State { |
| @@ -90,4 +90,4 @@ Item { · 67 unmodified lines | ||
| 90 | 90 | size: 28 |
| 91 | 91 | color: "#777777" |
| 92 | 92 | } |
| 93 | −} | |
| 93 | +} | |
| @@ -119,7 +119,7 @@ Item { · 118 unmodified lines | ||
| 119 | 119 | |
| 120 | 120 | ProjectInputControl { |
| 121 | 121 | id: textureFolder |
| 122 | − label: "Specify Texture Folder <i> - optional</i>" | |
| 122 | + label: "Specify Texture Folder - <i>Optional</i>" | |
| 123 | 123 | colorScheme: root.colorScheme |
| 124 | 124 | browseEnabled: true |
| 125 | 125 | browseFolder: true |
| @@ -128,5 +128,4 @@ Item { · 2 unmodified lines | ||
| 128 | 128 | onTextChanged: checkErrors() |
| 129 | 129 | } |
| 130 | 130 | } |
| 131 | − | |
| 132 | 131 | } |
| @@ -57,7 +57,7 @@ Column { · 56 unmodified lines | ||
| 57 | 57 | colorScheme: root.colorScheme |
| 58 | 58 | onClicked: { |
| 59 | 59 | avatarPackager.showModalOverlay = true; |
| 60 | − let browser = desktop.fileDialog({ | |
| 60 | + let browser = avatarPackager.desktopObject.fileDialog({ | |
| 61 | 61 | selectDirectory: browseFolder, |
| 62 | 62 | dir: browseDir, |
| 63 | 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 | 12 | #include "AvatarPackager.h" |
| 13 | 13 | |
| 14 | +#include "Application.h" | |
| 15 | + | |
| 14 | 16 | #include <QQmlContext> |
| 15 | 17 | #include <QQmlEngine> |
| 16 | 18 | #include <QUrl> |
| @@ -19,8 +21,9 @@ · 2 unmodified lines | ||
| 19 | 21 | #include "ModelSelector.h" |
| 20 | 22 | #include <avatar/MarketplaceItemUploader.h> |
| 21 | 23 | |
| 22 | −#include <thread> | |
| 23 | 24 | #include <mutex> |
| 25 | +#include "scripting/HMDScriptingInterface.h" | |
| 26 | +#include "ui/TabletScriptingInterface.h" | |
| 24 | 27 | |
| 25 | 28 | std::once_flag setupQMLTypesFlag; |
| 26 | 29 | AvatarPackager::AvatarPackager() { |
| @@ -38,31 +41,32 @@ AvatarPackager::AvatarPackager() { · 11 unmodified lines | ||
| 38 | 41 | } |
| 39 | 42 | |
| 40 | 43 | bool AvatarPackager::open() { |
| 41 | − static const QUrl url{ "hifi/AvatarPackager.qml" }; | |
| 44 | + static const QUrl url{ "hifi/AvatarPackagerWindow.qml" }; | |
| 42 | 45 | |
| 43 | 46 | const auto packageModelDialogCreated = [=](QQmlContext* context, QObject* newObject) { |
| 44 | 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) { | |
| 51 | − if (_currentAvatarProject) { | |
| 52 | − _currentAvatarProject->deleteLater(); | |
| 53 | − } | |
| 54 | − _currentAvatarProject = AvatarProject::openAvatarProject(avatarProjectFSTPath); | |
| 55 | − if (_currentAvatarProject) { | |
| 56 | − addRecentProject(avatarProjectFSTPath, _currentAvatarProject->getProjectName()); | |
| 50 | + static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; | |
| 51 | + auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); | |
| 52 | + auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); | |
| 53 | + auto hmd = DependencyManager::get<HMDScriptingInterface>(); | |
| 54 | + | |
| 55 | + if (tablet->getToolbarMode()) { | |
| 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"; | |
| 59 | − QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership); | |
| 60 | − emit avatarProjectChanged(); | |
| 61 | − return _currentAvatarProject; | |
| 64 | + return true; | |
| 62 | 65 | } |
| 63 | 66 | |
| 64 | −void AvatarPackager::addRecentProject(QString fstPath, QString projectName) { | |
| 67 | +void AvatarPackager::addCurrentProjectToRecentProjects() { | |
| 65 | 68 | const int MAX_RECENT_PROJECTS = 5; |
| 69 | + const QString& fstPath = _currentAvatarProject->getFSTPath(); | |
| 66 | 70 | auto removeProjects = QVector<RecentAvatarProject>(); |
| 67 | 71 | for (auto project : _recentProjects) { |
| 68 | 72 | if (project.getProjectFSTPath() == fstPath) { |
| @@ -73,27 +77,61 @@ void AvatarPackager::addRecentProject(QString fstPath, QString projectName) { · 4 unmodified lines | ||
| 73 | 77 | _recentProjects.removeOne(removeProject); |
| 74 | 78 | } |
| 75 | 79 | |
| 76 | − RecentAvatarProject newRecentProject = RecentAvatarProject(projectName, fstPath); | |
| 80 | + const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath); | |
| 77 | 81 | _recentProjects.prepend(newRecentProject); |
| 78 | 82 | |
| 79 | 83 | while (_recentProjects.size() > MAX_RECENT_PROJECTS) { |
| 80 | 84 | _recentProjects.pop_back(); |
| 81 | 85 | } |
| 82 | 86 | |
| 83 | − _recentProjectsSetting.set(recentProjectsToVariantList()); | |
| 87 | + _recentProjectsSetting.set(recentProjectsToVariantList(false)); | |
| 84 | 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 | 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 | 127 | if (_currentAvatarProject) { |
| 89 | 128 | _currentAvatarProject->deleteLater(); |
| 90 | 129 | } |
| 91 | − _currentAvatarProject = AvatarProject::createAvatarProject(projectsFolder, avatarProjectName, avatarModelPath, textureFolder); | |
| 130 | + _currentAvatarProject = avatarProject; | |
| 92 | 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 | 136 | emit avatarProjectChanged(); |
| 98 | − return _currentAvatarProject; | |
| 99 | 137 | } |
| @@ -35,12 +35,14 @@ public: · 34 unmodified lines | ||
| 35 | 35 | _projectFSTPath = other._projectFSTPath; |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | − ~RecentAvatarProject() = default; | |
| 39 | − | |
| 40 | 38 | QString getProjectName() const { return _projectName; } |
| 41 | 39 | |
| 42 | 40 | QString getProjectFSTPath() const { return _projectFSTPath; } |
| 43 | 41 | |
| 42 | + QString getProjectPath() const { | |
| 43 | + return QFileInfo(_projectFSTPath).absoluteDir().absolutePath(); | |
| 44 | + } | |
| 45 | + | |
| 44 | 46 | bool operator==(const RecentAvatarProject& other) const { |
| 45 | 47 | return _projectName == other._projectName && _projectFSTPath == other._projectFSTPath; |
| 46 | 48 | } |
| @@ -72,31 +74,18 @@ signals: · 25 unmodified lines | ||
| 72 | 74 | private: |
| 73 | 75 | Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; }; |
| 74 | 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 | 83 | AvatarProject* _currentAvatarProject{ nullptr }; |
| 80 | 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) { | |
| 94 | − _recentProjects.clear(); | |
| 95 | − for (const auto& projectVariant : projectsVariant) { | |
| 96 | − auto map = projectVariant.toMap(); | |
| 97 | − _recentProjects.append(RecentAvatarProject(map.value("name").toString(), map.value("path").toString())); | |
| 98 | − } | |
| 99 | − } | |
| 86 | + QVariantList recentProjectsToVariantList(bool includeProjectPaths); | |
| 87 | + | |
| 88 | + void recentProjectsFromVariantList(QVariantList projectsVariant); | |
| 100 | 89 | |
| 101 | 90 | |
| 102 | 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) { | |
| 85 | − addTextureToList(mat.normalTexture); | |
| 86 | − addTextureToList(mat.albedoTexture); | |
| 87 | − addTextureToList(mat.opacityTexture); | |
| 88 | − addTextureToList(mat.glossTexture); | |
| 89 | − addTextureToList(mat.roughnessTexture); | |
| 90 | − addTextureToList(mat.specularTexture); | |
| 91 | − addTextureToList(mat.metallicTexture); | |
| 92 | − addTextureToList(mat.emissiveTexture); | |
| 93 | − addTextureToList(mat.occlusionTexture); | |
| 94 | − addTextureToList(mat.scatteringTexture); | |
| 95 | − addTextureToList(mat.lightmapTexture); | |
| 84 | + foreach(const HFMMaterial material, hfmModel->materials) { | |
| 85 | + addTextureToList(material.normalTexture); | |
| 86 | + addTextureToList(material.albedoTexture); | |
| 87 | + addTextureToList(material.opacityTexture); | |
| 88 | + addTextureToList(material.glossTexture); | |
| 89 | + addTextureToList(material.roughnessTexture); | |
| 90 | + addTextureToList(material.specularTexture); | |
| 91 | + addTextureToList(material.metallicTexture); | |
| 92 | + addTextureToList(material.emissiveTexture); | |
| 93 | + addTextureToList(material.occlusionTexture); | |
| 94 | + addTextureToList(material.scatteringTexture); | |
| 95 | + addTextureToList(material.lightmapTexture); | |
| 96 | 96 | } |
| 97 | 97 | |
| 98 | 98 | QDir textureDir(textureFolder.isEmpty() ? fbxInfo.absoluteDir() : textureFolder); |
| @@ -152,7 +152,6 @@ AvatarProject::AvatarProject(FST* fst) { · 53 unmodified lines | ||
| 152 | 152 | _fst->setScriptPaths(getScriptPaths(QDir(_directory.path() + "/scripts"))); |
| 153 | 153 | _fst->write(); |
| 154 | 154 | |
| 155 | − //_projectFiles = _directory.entryList(); | |
| 156 | 155 | refreshProjectFiles(); |
| 157 | 156 | |
| 158 | 157 | _projectPath = fileInfo.absoluteDir().absolutePath(); |
| @@ -28,10 +28,10 @@ class AvatarProject : public QObject { · 27 unmodified lines | ||
| 28 | 28 | |
| 29 | 29 | Q_PROPERTY(QStringList projectFiles READ getProjectFiles NOTIFY projectFilesChanged) |
| 30 | 30 | |
| 31 | − Q_PROPERTY(QString projectFolderPath READ getProjectPath) | |
| 32 | − Q_PROPERTY(QString projectFSTPath READ getFSTPath) | |
| 33 | − Q_PROPERTY(QString projectFBXPath READ getFBXPath) | |
| 34 | − Q_PROPERTY(QString name READ getProjectName NOTIFY nameChanged) | |
| 31 | + Q_PROPERTY(QString projectFolderPath READ getProjectPath CONSTANT) | |
| 32 | + Q_PROPERTY(QString projectFSTPath READ getFSTPath CONSTANT) | |
| 33 | + Q_PROPERTY(QString projectFBXPath READ getFBXPath CONSTANT) | |
| 34 | + Q_PROPERTY(QString name READ getProjectName WRITE setProjectName NOTIFY nameChanged) | |
| 35 | 35 | |
| 36 | 36 | public: |
| 37 | 37 | Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting); |
| @@ -39,6 +39,13 @@ public: · 1 unmodified line | ||
| 39 | 39 | Q_INVOKABLE QStringList getProjectFiles() const; |
| 40 | 40 | |
| 41 | 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 | 49 | Q_INVOKABLE QString getProjectPath() const { return _projectPath; } |
| 43 | 50 | Q_INVOKABLE QString getFSTPath() const { return _fst->getPath(); } |
| 44 | 51 | Q_INVOKABLE QString getFBXPath() const { return _fst->getModelPath(); } |
| @@ -15,7 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <QFileInfo> |
| 16 | 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 | 20 | auto setValueFromFSTData = [&data] (const QString& propertyID, auto &targetProperty) mutable { |
| 21 | 21 | if (data.contains(propertyID)) { |
| @@ -38,7 +38,7 @@ FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) { · 16 unmodified lines | ||
| 38 | 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 | 42 | QVariantHash mapping; |
| 43 | 43 | |
| 44 | 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 | 26 | Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID) |
| 27 | 27 | Q_PROPERTY(bool hasMarketplaceID READ getHasMarketplaceID NOTIFY marketplaceIDChanged) |
| 28 | 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 | 33 | QString absoluteModelPath() const; |
| 34 | 34 | |