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 @@
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−}
interface/resources/qml/hifi/AvatarPackagerWindow.qml+32−0
@@ -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+}
interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml+288−0
@@ -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+}
interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml+87−15
@@ -11,11 +11,18 @@ Rectangle { · 10 unmodified lines
1111 color: "#252525"
1212
1313 property alias title: title.text
14− property alias faqEnabled: faq.visible
14+ property alias docsEnabled: docs.visible
1515 property bool backButtonVisible: true // If false, is not visible and does not take up space
1616 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+
1824 signal backButtonClicked
25+ signal docsButtonClicked
1926
2027 RalewaySemiBold {
2128 id: back
@@ -27,7 +34,7 @@ Rectangle { · 5 unmodified lines
2734 anchors.leftMargin: 16
2835 anchors.verticalCenter: back.verticalCenter
2936 text: "◀"
30− color: "white"
37+ color: textColor
3138 MouseArea {
3239 anchors.fill: parent
3340 onClicked: root.backButtonClicked()
@@ -37,37 +44,102 @@ Rectangle { · 3 unmodified lines
3744 states: [
3845 State {
3946 name: "hovering"
40− PropertyChanges {
41− target: back
42− color: "gray"
43− }
47+ PropertyChanges { target: back; color: hoverTextColor }
4448 }
4549 ]
4650 }
4751 }
48−
49− RalewaySemiBold {
50− id: title
51− size: 28
52+ Item {
53+ id: titleArea
5254 anchors.top: parent.top
5355 anchors.bottom: parent.bottom
5456 anchors.left: root.backButtonVisible ? back.right : parent.left
5557 anchors.leftMargin: root.backButtonVisible ? 11 : 21
5658 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+ }
59125 }
60126
61127 RalewaySemiBold {
62− id: faq
128+ id: docs
63129 visible: false
64130 size: 28
65131 anchors.top: parent.top
66132 anchors.bottom: parent.bottom
67133 anchors.right: parent.right
68134 anchors.rightMargin: 16
69− anchors.verticalCenter: faq.verticalCenter
135+ anchors.verticalCenter: docs.verticalCenter
70136 text: qsTr("Docs")
71137 color: "white"
138+ MouseArea {
139+ anchors.fill: parent
140+ onClicked: {
141+ docsButtonClicked();
142+ }
143+ }
72144 }
73145 }
interface/resources/qml/hifi/avatarPackager/AvatarPackagerState.qml+1−0
@@ -6,4 +6,5 @@ Item { · 5 unmodified lines
66 readonly property string main: "main"
77 readonly property string project: "project"
88 readonly property string createProject: "createProject"
9+ readonly property string projectUpload: "projectUpload"
910 }
interface/resources/qml/hifi/avatarPackager/AvatarProject.qml+1−1
@@ -178,7 +178,7 @@ Item { · 177 unmodified lines
178178 }
179179 });
180180 root.uploader.send();
181− avatarPackager.state = "project-upload";
181+ avatarPackager.state = AvatarPackagerState.projectUpload;
182182 }
183183
184184 function showConfirmUploadPopup() {
interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml+7−0
@@ -53,11 +53,14 @@ Item { · 52 unmodified lines
5353
5454 RalewayBold {
5555 id: title
56+ elide: "ElideRight"
5657 anchors {
5758 top: parent.top
5859 topMargin: 13
5960 left: parent.left
6061 leftMargin: 16
62+ right: parent.right
63+ rightMargin: 16
6164 }
6265 text: "<title missing>"
6366 size: 16
@@ -69,7 +72,11 @@ Item { · 5 unmodified lines
6972 top: title.bottom
7073 left: parent.left
7174 leftMargin: 32
75+ right: background.right
76+ rightMargin: 16
7277 }
78+ elide: "ElideLeft"
79+ horizontalAlignment: Text.AlignRight
7380 text: "<path missing>"
7481 size: 20
7582 }
interface/resources/qml/hifi/avatarPackager/AvatarProjectUpload.qml+5−5
@@ -21,8 +21,8 @@ Item { · 20 unmodified lines
2121 running: false
2222 repeat: false
2323 onTriggered: {
24− if (avatarPackager.state =="project-upload") {
25− avatarPackager.state = "project"
24+ if (avatarPackager.state === AvatarPackagerState.projectUpload) {
25+ avatarPackager.state = AvatarPackagerState.project;
2626 }
2727 }
2828 }
@@ -130,7 +130,7 @@ Item { · 101 unmodified lines
130130 AvatarUploadStatusItem {
131131 id: statusCategories
132132 uploader: root.uploader
133− text: "Retreiving categories"
133+ text: "Retrieving categories"
134134
135135 uploaderState: 1
136136 }
@@ -189,8 +189,8 @@ Item { · 52 unmodified lines
189189 colorScheme: root.colorScheme
190190 width: 133
191191 height: 40
192− onClicked: function() {
193− avatarPackager.state = "project"
192+ onClicked: {
193+ avatarPackager.state = AvatarPackagerState.project;
194194 }
195195 }
196196 }
interface/resources/qml/hifi/avatarPackager/AvatarUploadStatusItem.qml+4−4
@@ -14,9 +14,9 @@ Item { · 13 unmodified lines
1414 property int uploaderState;
1515 property var uploader;
1616
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" : "")))
2020
2121 states: [
2222 State {
@@ -90,4 +90,4 @@ Item { · 67 unmodified lines
9090 size: 28
9191 color: "#777777"
9292 }
93−}
93+}
interface/resources/qml/hifi/avatarPackager/CreateAvatarProject.qml+1−2
@@ -119,7 +119,7 @@ Item { · 118 unmodified lines
119119
120120 ProjectInputControl {
121121 id: textureFolder
122− label: "Specify Texture Folder <i> - optional</i>"
122+ label: "Specify Texture Folder - <i>Optional</i>"
123123 colorScheme: root.colorScheme
124124 browseEnabled: true
125125 browseFolder: true
@@ -128,5 +128,4 @@ Item { · 2 unmodified lines
128128 onTextChanged: checkErrors()
129129 }
130130 }
131−
132131 }
interface/resources/qml/hifi/avatarPackager/ProjectInputControl.qml+1−1
@@ -57,7 +57,7 @@ Column { · 56 unmodified lines
5757 colorScheme: root.colorScheme
5858 onClicked: {
5959 avatarPackager.showModalOverlay = true;
60− let browser = desktop.fileDialog({
60+ let browser = avatarPackager.desktopObject.fileDialog({
6161 selectDirectory: browseFolder,
6262 dir: browseDir,
6363 filter: browseFilter,
interface/resources/qml/hifi/tablet/AvatarPackager.qml+15−0
@@ -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+}
interface/src/avatar/AvatarPackager.cpp+62−24
@@ -11,6 +11,8 @@ · 10 unmodified lines
1111
1212 #include "AvatarPackager.h"
1313
14+#include "Application.h"
15+
1416 #include <QQmlContext>
1517 #include <QQmlEngine>
1618 #include <QUrl>
@@ -19,8 +21,9 @@ · 2 unmodified lines
1921 #include "ModelSelector.h"
2022 #include <avatar/MarketplaceItemUploader.h>
2123
22−#include <thread>
2324 #include <mutex>
25+#include "scripting/HMDScriptingInterface.h"
26+#include "ui/TabletScriptingInterface.h"
2427
2528 std::once_flag setupQMLTypesFlag;
2629 AvatarPackager::AvatarPackager() {
@@ -38,31 +41,32 @@ AvatarPackager::AvatarPackager() { · 11 unmodified lines
3841 }
3942
4043 bool AvatarPackager::open() {
41− static const QUrl url{ "hifi/AvatarPackager.qml" };
44+ static const QUrl url{ "hifi/AvatarPackagerWindow.qml" };
4245
4346 const auto packageModelDialogCreated = [=](QQmlContext* context, QObject* newObject) {
4447 context->setContextProperty("AvatarPackagerCore", this);
4548 };
46− DependencyManager::get<OffscreenUi>()->show(url, "AvatarPackager", packageModelDialogCreated);
47− return true;
48−}
4949
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+ }
5763 }
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;
6265 }
6366
64−void AvatarPackager::addRecentProject(QString fstPath, QString projectName) {
67+void AvatarPackager::addCurrentProjectToRecentProjects() {
6568 const int MAX_RECENT_PROJECTS = 5;
69+ const QString& fstPath = _currentAvatarProject->getFSTPath();
6670 auto removeProjects = QVector<RecentAvatarProject>();
6771 for (auto project : _recentProjects) {
6872 if (project.getProjectFSTPath() == fstPath) {
@@ -73,27 +77,61 @@ void AvatarPackager::addRecentProject(QString fstPath, QString projectName) { · 4 unmodified lines
7377 _recentProjects.removeOne(removeProject);
7478 }
7579
76− RecentAvatarProject newRecentProject = RecentAvatarProject(projectName, fstPath);
80+ const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath);
7781 _recentProjects.prepend(newRecentProject);
7882
7983 while (_recentProjects.size() > MAX_RECENT_PROJECTS) {
8084 _recentProjects.pop_back();
8185 }
8286
83− _recentProjectsSetting.set(recentProjectsToVariantList());
87+ _recentProjectsSetting.set(recentProjectsToVariantList(false));
8488 emit recentProjectsChanged();
8589 }
8690
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+
87118 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+ }
88127 if (_currentAvatarProject) {
89128 _currentAvatarProject->deleteLater();
90129 }
91− _currentAvatarProject = AvatarProject::createAvatarProject(projectsFolder, avatarProjectName, avatarModelPath, textureFolder);
130+ _currentAvatarProject = avatarProject;
92131 if (_currentAvatarProject) {
93− addRecentProject(_currentAvatarProject->getFSTPath(), _currentAvatarProject->getProjectName());
132+ addCurrentProjectToRecentProjects();
133+ connect(_currentAvatarProject, &AvatarProject::nameChanged, this, &AvatarPackager::addCurrentProjectToRecentProjects);
134+ QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership);
94135 }
95− qDebug() << "_currentAvatarProject has" << (QQmlEngine::objectOwnership(_currentAvatarProject) == QQmlEngine::CppOwnership ? "CPP" : "JS") << "OWNERSHIP";
96− QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership);
97136 emit avatarProjectChanged();
98− return _currentAvatarProject;
99137 }
interface/src/avatar/AvatarPackager.h+11−22
@@ -35,12 +35,14 @@ public: · 34 unmodified lines
3535 _projectFSTPath = other._projectFSTPath;
3636 }
3737
38− ~RecentAvatarProject() = default;
39−
4038 QString getProjectName() const { return _projectName; }
4139
4240 QString getProjectFSTPath() const { return _projectFSTPath; }
4341
42+ QString getProjectPath() const {
43+ return QFileInfo(_projectFSTPath).absoluteDir().absolutePath();
44+ }
45+
4446 bool operator==(const RecentAvatarProject& other) const {
4547 return _projectName == other._projectName && _projectFSTPath == other._projectFSTPath;
4648 }
@@ -72,31 +74,18 @@ signals: · 25 unmodified lines
7274 private:
7375 Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; };
7476 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);
7680
77− void addRecentProject(QString fstPath, QString projectName);
81+ void addCurrentProjectToRecentProjects();
7882
7983 AvatarProject* _currentAvatarProject{ nullptr };
8084 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− }
9285
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);
10089
10190
10291 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
8181 }
8282 };
8383
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);
9696 }
9797
9898 QDir textureDir(textureFolder.isEmpty() ? fbxInfo.absoluteDir() : textureFolder);
@@ -152,7 +152,6 @@ AvatarProject::AvatarProject(FST* fst) { · 53 unmodified lines
152152 _fst->setScriptPaths(getScriptPaths(QDir(_directory.path() + "/scripts")));
153153 _fst->write();
154154
155− //_projectFiles = _directory.entryList();
156155 refreshProjectFiles();
157156
158157 _projectPath = fileInfo.absoluteDir().absolutePath();
interface/src/avatar/AvatarProject.h+11−4
@@ -28,10 +28,10 @@ class AvatarProject : public QObject { · 27 unmodified lines
2828
2929 Q_PROPERTY(QStringList projectFiles READ getProjectFiles NOTIFY projectFilesChanged)
3030
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)
3535
3636 public:
3737 Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting);
@@ -39,6 +39,13 @@ public: · 1 unmodified line
3939 Q_INVOKABLE QStringList getProjectFiles() const;
4040
4141 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+ }
4249 Q_INVOKABLE QString getProjectPath() const { return _projectPath; }
4350 Q_INVOKABLE QString getFSTPath() const { return _fst->getPath(); }
4451 Q_INVOKABLE QString getFBXPath() const { return _fst->getModelPath(); }
libraries/fbx/src/FST.cpp+2−2
@@ -15,7 +15,7 @@ · 14 unmodified lines
1515 #include <QFileInfo>
1616 #include <hfm/HFM.h>
1717
18−FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) {
18+FST::FST(QString fstPath, QVariantHash data) : _fstPath(std::move(fstPath)) {
1919
2020 auto setValueFromFSTData = [&data] (const QString& propertyID, auto &targetProperty) mutable {
2121 if (data.contains(propertyID)) {
@@ -38,7 +38,7 @@ FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) { · 16 unmodified lines
3838 _other = data;
3939 }
4040
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) {
4242 QVariantHash mapping;
4343
4444 // 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
2626 Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID)
2727 Q_PROPERTY(bool hasMarketplaceID READ getHasMarketplaceID NOTIFY marketplaceIDChanged)
2828 public:
29− FST(const QString& fstPath, QVariantHash data);
29+ FST(QString fstPath, QVariantHash data);
3030
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);
3232
3333 QString absoluteModelPath() const;
3434

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