History

- rename functionality - avatar packager works in tablet now

Thijs Wenker committed 7 years ago

Commit cb33a911 parent 1ec8aa818 files · +542 −371Browse files

Unified · Split

18 files
  1. interface/resources/qml/hifi/AvatarPackager.qml +0 −276
  2. interface/resources/qml/hifi/AvatarPackagerWindow.qml +32 −0
  3. interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml +288 −0
  4. interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml +87 −15
  5. interface/resources/qml/hifi/avatarPackager/AvatarPackagerState.qml +1 −0
  6. interface/resources/qml/hifi/avatarPackager/AvatarProject.qml +1 −1
  7. interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml +7 −0
  8. interface/resources/qml/hifi/avatarPackager/AvatarProjectUpload.qml +5 −5
  9. interface/resources/qml/hifi/avatarPackager/AvatarUploadStatusItem.qml +4 −4
  10. interface/resources/qml/hifi/avatarPackager/CreateAvatarProject.qml +1 −2
  11. interface/resources/qml/hifi/avatarPackager/ProjectInputControl.qml +1 −1
  12. interface/resources/qml/hifi/tablet/AvatarPackager.qml +15 −0
  13. interface/src/avatar/AvatarPackager.cpp +62 −24
  14. interface/src/avatar/AvatarPackager.h +11 −22
  15. interface/src/avatar/AvatarProject.cpp +12 −13
  16. interface/src/avatar/AvatarProject.h +11 −4
  17. libraries/fbx/src/FST.cpp +2 −2
  18. libraries/fbx/src/FST.h +2 −2
interface/resources/qml/hifi/AvatarPackager.qml+0−276
@@ -1,276 +0,0 @@
1import QtQuick 2.6
2import QtQuick.Controls 2.2
3import QtQuick.Layouts 1.3
4import QtQml.Models 2.1
5import QtGraphicalEffects 1.0
6import "../controlsUit" 1.0 as HifiControls
7import "../stylesUit" 1.0
8import "../windows" as Windows
9import "../dialogs"
10import "./avatarPackager" 1.0
11import "avatarapp" 1.0 as AvatarApp
12
13Windows.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}
interface/resources/qml/hifi/AvatarPackagerWindow.qml+32−0
@@ -0,0 +1,32 @@
1import QtQuick 2.6
2import QtQuick.Controls 2.2
3import QtQuick.Layouts 1.3
4import QtQml.Models 2.1
5import QtGraphicalEffects 1.0
6import "../controlsUit" 1.0 as HifiControls
7import "../stylesUit" 1.0
8import "../windows" as Windows
9import "../controls" 1.0
10import "../dialogs"
11import "avatarPackager" 1.0
12import "avatarapp" 1.0 as AvatarApp
13
14Windows.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}
interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml+288−0
@@ -0,0 +1,288 @@
1import QtQuick 2.6
2import QtQuick.Controls 2.2
3import QtQuick.Layouts 1.3
4import QtQml.Models 2.1
5import QtGraphicalEffects 1.0
6import "../../controlsUit" 1.0 as HifiControls
7import "../../stylesUit" 1.0
8import "../../windows" as Windows
9import "../../controls" 1.0
10import "../../dialogs"
11import "../avatarapp" 1.0 as AvatarApp
12
13Item {
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}
interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml+87−15
@@ -11,11 +11,18 @@ Rectangle { · 10 unmodified lines
11 color: "#252525"11 color: "#252525"
12 12
13 property alias title: title.text13 property alias title: title.text
14 property alias faqEnabled: faq.visible14 property alias docsEnabled: docs.visible
15 property bool backButtonVisible: true // If false, is not visible and does not take up space15 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 space16 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 backButtonClicked24 signal backButtonClicked
25 signal docsButtonClicked
19 26
20 RalewaySemiBold {27 RalewaySemiBold {
21 id: back28 id: back
@@ -27,7 +34,7 @@ Rectangle { · 5 unmodified lines
27 anchors.leftMargin: 1634 anchors.leftMargin: 16
28 anchors.verticalCenter: back.verticalCenter35 anchors.verticalCenter: back.verticalCenter
29 text: "◀"36 text: "◀"
30 color: "white"37 color: textColor
31 MouseArea {38 MouseArea {
32 anchors.fill: parent39 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.top54 anchors.top: parent.top
53 anchors.bottom: parent.bottom55 anchors.bottom: parent.bottom
54 anchors.left: root.backButtonVisible ? back.right : parent.left56 anchors.left: root.backButtonVisible ? back.right : parent.left
55 anchors.leftMargin: root.backButtonVisible ? 11 : 2157 anchors.leftMargin: root.backButtonVisible ? 11 : 21
56 anchors.verticalCenter: title.verticalCenter58 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: faq128 id: docs
63 visible: false129 visible: false
64 size: 28130 size: 28
65 anchors.top: parent.top131 anchors.top: parent.top
66 anchors.bottom: parent.bottom132 anchors.bottom: parent.bottom
67 anchors.right: parent.right133 anchors.right: parent.right
68 anchors.rightMargin: 16134 anchors.rightMargin: 16
69 anchors.verticalCenter: faq.verticalCenter135 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}
interface/resources/qml/hifi/avatarPackager/AvatarPackagerState.qml+1−0
@@ -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}
interface/resources/qml/hifi/avatarPackager/AvatarProject.qml+1−1
@@ -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() {
interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml+7−0
@@ -53,11 +53,14 @@ Item { · 52 unmodified lines
53 53
54 RalewayBold {54 RalewayBold {
55 id: title55 id: title
56 elide: "ElideRight"
56 anchors {57 anchors {
57 top: parent.top58 top: parent.top
58 topMargin: 1359 topMargin: 13
59 left: parent.left60 left: parent.left
60 leftMargin: 1661 leftMargin: 16
62 right: parent.right
63 rightMargin: 16
61 }64 }
62 text: "<title missing>"65 text: "<title missing>"
63 size: 1666 size: 16
@@ -69,7 +72,11 @@ Item { · 5 unmodified lines
69 top: title.bottom72 top: title.bottom
70 left: parent.left73 left: parent.left
71 leftMargin: 3274 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: 2081 size: 20
75 }82 }
interface/resources/qml/hifi/avatarPackager/AvatarProjectUpload.qml+5−5
@@ -21,8 +21,8 @@ Item { · 20 unmodified lines
21 running: false21 running: false
22 repeat: false22 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: statusCategories131 id: statusCategories
132 uploader: root.uploader132 uploader: root.uploader
133 text: "Retreiving categories"133 text: "Retrieving categories"
134 134
135 uploaderState: 1135 uploaderState: 1
136 }136 }
@@ -189,8 +189,8 @@ Item { · 52 unmodified lines
189 colorScheme: root.colorScheme189 colorScheme: root.colorScheme
190 width: 133190 width: 133
191 height: 40191 height: 40
192 onClicked: function() {192 onClicked: {
193 avatarPackager.state = "project"193 avatarPackager.state = AvatarPackagerState.project;
194 }194 }
195 }195 }
196 }196 }
interface/resources/qml/hifi/avatarPackager/AvatarUploadStatusItem.qml+4−4
@@ -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 > uploaderState17 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: 2890 size: 28
91 color: "#777777"91 color: "#777777"
92 }92 }
93}93}
interface/resources/qml/hifi/avatarPackager/CreateAvatarProject.qml+1−2
@@ -119,7 +119,7 @@ Item { · 118 unmodified lines
119 119
120 ProjectInputControl {120 ProjectInputControl {
121 id: textureFolder121 id: textureFolder
122 label: "Specify Texture Folder <i> - optional</i>"122 label: "Specify Texture Folder - <i>Optional</i>"
123 colorScheme: root.colorScheme123 colorScheme: root.colorScheme
124 browseEnabled: true124 browseEnabled: true
125 browseFolder: true125 browseFolder: true
@@ -128,5 +128,4 @@ Item { · 2 unmodified lines
128 onTextChanged: checkErrors()128 onTextChanged: checkErrors()
129 }129 }
130 }130 }
131
132}131}
interface/resources/qml/hifi/avatarPackager/ProjectInputControl.qml+1−1
@@ -57,7 +57,7 @@ Column { · 56 unmodified lines
57 colorScheme: root.colorScheme57 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,
interface/resources/qml/hifi/tablet/AvatarPackager.qml+15−0
@@ -0,0 +1,15 @@
1import QtQuick 2.0
2import "../avatarPackager" 1.0
3
4Item {
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}
interface/src/avatar/AvatarPackager.cpp+62−24
@@ -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
25std::once_flag setupQMLTypesFlag;28std::once_flag setupQMLTypesFlag;
26AvatarPackager::AvatarPackager() {29AvatarPackager::AvatarPackager() {
@@ -38,31 +41,32 @@ AvatarPackager::AvatarPackager() { · 11 unmodified lines
38}41}
39 42
40bool AvatarPackager::open() {43bool 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
50AvatarProject* 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
64void AvatarPackager::addRecentProject(QString fstPath, QString projectName) {67void 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
91QVariantList 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}
105void 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
113AvatarProject* AvatarPackager::openAvatarProject(const QString& avatarProjectFSTPath) {
114 setAvatarProject(AvatarProject::openAvatarProject(avatarProjectFSTPath));
115 return _currentAvatarProject;
116}
117
87AvatarProject* AvatarPackager::createAvatarProject(const QString& projectsFolder, const QString& avatarProjectName, const QString& avatarModelPath, const QString& textureFolder) {118AvatarProject* 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
123void 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}
interface/src/avatar/AvatarPackager.h+11−22
@@ -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
72private:74private:
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() };
interface/src/avatar/AvatarProject.cpp+12−13
@@ -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();
interface/src/avatar/AvatarProject.h+11−4
@@ -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
36public:36public:
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(); }
libraries/fbx/src/FST.cpp+2−2
@@ -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
18FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) {18FST::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
41FST* FST::createFSTFromModel(QString fstPath, QString modelFilePath, const hfm::Model& hfmModel) {41FST* 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 will44 // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will
libraries/fbx/src/FST.h+2−2
@@ -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)
28public:28public:
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

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t