mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-14 13:36:02 +02:00
- rename functionality
- avatar packager works in tablet now
This commit is contained in:
parent
1ec8aa8fa5
commit
cb33a91a34
18 changed files with 543 additions and 372 deletions
|
@ -1,276 +0,0 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQml.Models 2.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../controlsUit" 1.0 as HifiControls
|
||||
import "../stylesUit" 1.0
|
||||
import "../windows" as Windows
|
||||
import "../dialogs"
|
||||
import "./avatarPackager" 1.0
|
||||
import "avatarapp" 1.0 as AvatarApp
|
||||
|
||||
Windows.ScrollingWindow {
|
||||
id: root
|
||||
objectName: "AvatarPackager"
|
||||
width: 480
|
||||
height: 706
|
||||
title: "Avatar Packager"
|
||||
resizable: false
|
||||
opacity: parent.opacity
|
||||
destroyOnHidden: true
|
||||
implicitWidth: 384; implicitHeight: 640
|
||||
minSize: Qt.vector2d(480, 706)
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
Item {
|
||||
id: windowContent
|
||||
height: pane.height
|
||||
width: pane.width
|
||||
|
||||
InfoBox {
|
||||
id: fileListPopup
|
||||
|
||||
title: "List of Files"
|
||||
|
||||
content: Rectangle {
|
||||
id: fileList
|
||||
|
||||
color: "#404040"
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 10
|
||||
anchors.bottomMargin: 10
|
||||
anchors.leftMargin: 29
|
||||
anchors.rightMargin: 29
|
||||
|
||||
clip: true
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
model: AvatarPackagerCore.currentAvatarProject === null ? [] : AvatarPackagerCore.currentAvatarProject.projectFiles
|
||||
delegate: Rectangle {
|
||||
width: parent.width
|
||||
height: fileText.implicitHeight + 8
|
||||
color: "#404040"
|
||||
RalewaySemiBold {
|
||||
id: fileText
|
||||
size: 16
|
||||
elide: Text.ElideLeft
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.topMargin: 4
|
||||
width: parent.width - 10
|
||||
color: "white"
|
||||
text: modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: modalOverlay
|
||||
anchors.fill: parent
|
||||
z: 20
|
||||
color: "#a15d5d5d"
|
||||
visible: false
|
||||
|
||||
// This mouse area captures the cursor events while the modalOverlay is active
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarApp.MessageBox {
|
||||
id: popup
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
closeOnClickOutside: true
|
||||
}
|
||||
|
||||
Column {
|
||||
id: avatarPackager
|
||||
anchors.fill: parent
|
||||
state: "main"
|
||||
states: [
|
||||
State {
|
||||
name: AvatarPackagerState.main
|
||||
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); faqEnabled: true; backButtonVisible: false }
|
||||
PropertyChanges { target: avatarPackagerMain; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.createProject
|
||||
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Create Project") }
|
||||
PropertyChanges { target: createAvatarProject; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: createAvatarProject.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.project
|
||||
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; canRename: true }
|
||||
PropertyChanges { target: avatarProject; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: avatarProject.footer }
|
||||
},
|
||||
State {
|
||||
name: "project-upload"
|
||||
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; backButtonEnabled: false }
|
||||
PropertyChanges { target: avatarUploader; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; visible: false }
|
||||
}
|
||||
]
|
||||
|
||||
property alias showModalOverlay: modalOverlay.visible
|
||||
|
||||
function openProject(path) {
|
||||
let project = AvatarPackagerCore.openAvatarProject(path);
|
||||
if (project) {
|
||||
avatarProject.reset();
|
||||
avatarPackager.state = "project";
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
AvatarPackagerHeader {
|
||||
id: avatarPackagerHeader
|
||||
onBackButtonClicked: {
|
||||
avatarPackager.state = "main"
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: pane.height - avatarPackagerHeader.height - avatarPackagerFooter.height
|
||||
width: pane.width
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#404040"
|
||||
}
|
||||
|
||||
AvatarProject {
|
||||
id: avatarProject
|
||||
colorScheme: root.colorScheme
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
AvatarProjectUpload {
|
||||
id: avatarUploader
|
||||
anchors.fill: parent
|
||||
root: avatarProject
|
||||
}
|
||||
|
||||
CreateAvatarProject {
|
||||
id: createAvatarProject
|
||||
colorScheme: root.colorScheme
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Item {
|
||||
id: avatarPackagerMain
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
|
||||
property var footer: Item {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 17
|
||||
HifiControls.Button {
|
||||
id: createProjectButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: openProjectButton.left
|
||||
anchors.rightMargin: 22
|
||||
height: 40
|
||||
width: 134
|
||||
text: qsTr("New Project")
|
||||
colorScheme: root.colorScheme
|
||||
onClicked: {
|
||||
createAvatarProject.clearInputs();
|
||||
avatarPackager.state = AvatarPackagerState.createProject;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: openProjectButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
height: 40
|
||||
width: 133
|
||||
text: qsTr("Open Project")
|
||||
color: hifi.buttons.blue
|
||||
colorScheme: root.colorScheme
|
||||
onClicked: {
|
||||
avatarPackager.showModalOverlay = true;
|
||||
let browser = desktop.fileDialog({
|
||||
selectDirectory: false,
|
||||
dir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH),
|
||||
filter: "Avatar Project FST Files (*.fst)",
|
||||
title: "Open Project (.fst)",
|
||||
});
|
||||
|
||||
browser.canceled.connect(function() {
|
||||
avatarPackager.showModalOverlay = false;
|
||||
});
|
||||
|
||||
browser.selectedFile.connect(function(fileUrl) {
|
||||
let fstFilePath = fileDialogHelper.urlToPath(fileUrl);
|
||||
let currentAvatarProject = avatarPackager.openProject(fstFilePath);
|
||||
if (currentAvatarProject) {
|
||||
avatarPackager.showModalOverlay = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flow {
|
||||
visible: AvatarPackagerCore.recentProjects.length === 0
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 18
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
}
|
||||
RalewayRegular {
|
||||
size: 20
|
||||
color: "white"
|
||||
text: qsTr("Use a custom avatar to express your identity")
|
||||
}
|
||||
RalewayRegular {
|
||||
size: 20
|
||||
color: "white"
|
||||
text: qsTr("To learn more about using this tool, visit our docs")
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
visible: AvatarPackagerCore.recentProjects.length > 0
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 18
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
}
|
||||
spacing: 10
|
||||
|
||||
Repeater {
|
||||
model: AvatarPackagerCore.recentProjects
|
||||
AvatarProjectCard {
|
||||
title: modelData.name
|
||||
path: modelData.path
|
||||
onOpen: avatarPackager.openProject(modelData.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AvatarPackagerFooter {
|
||||
id: avatarPackagerFooter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
interface/resources/qml/hifi/AvatarPackagerWindow.qml
Normal file
32
interface/resources/qml/hifi/AvatarPackagerWindow.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQml.Models 2.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../controlsUit" 1.0 as HifiControls
|
||||
import "../stylesUit" 1.0
|
||||
import "../windows" as Windows
|
||||
import "../controls" 1.0
|
||||
import "../dialogs"
|
||||
import "avatarPackager" 1.0
|
||||
import "avatarapp" 1.0 as AvatarApp
|
||||
|
||||
Windows.ScrollingWindow {
|
||||
id: root
|
||||
objectName: "AvatarPackager"
|
||||
width: 480
|
||||
height: 706
|
||||
title: "Avatar Packager"
|
||||
resizable: false
|
||||
opacity: parent.opacity
|
||||
destroyOnHidden: true
|
||||
implicitWidth: 384; implicitHeight: 640
|
||||
minSize: Qt.vector2d(480, 706)
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
AvatarPackagerApp {
|
||||
height: pane.height
|
||||
width: pane.width
|
||||
}
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQml.Models 2.1
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../controlsUit" 1.0 as HifiControls
|
||||
import "../../stylesUit" 1.0
|
||||
import "../../windows" as Windows
|
||||
import "../../controls" 1.0
|
||||
import "../../dialogs"
|
||||
import "../avatarapp" 1.0 as AvatarApp
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
property alias desktopObject: avatarPackager.desktopObject
|
||||
|
||||
id: windowContent
|
||||
// height: pane ? pane.height : parent.width
|
||||
// width: pane ? pane.width : parent.width
|
||||
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
unfocusser.forceActiveFocus();
|
||||
}
|
||||
Item {
|
||||
id: unfocusser
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
InfoBox {
|
||||
id: fileListPopup
|
||||
|
||||
title: "List of Files"
|
||||
|
||||
content: Rectangle {
|
||||
id: fileList
|
||||
|
||||
color: "#404040"
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 10
|
||||
anchors.bottomMargin: 10
|
||||
anchors.leftMargin: 29
|
||||
anchors.rightMargin: 29
|
||||
|
||||
clip: true
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
model: AvatarPackagerCore.currentAvatarProject === null ? [] : AvatarPackagerCore.currentAvatarProject.projectFiles
|
||||
delegate: Rectangle {
|
||||
width: parent.width
|
||||
height: fileText.implicitHeight + 8
|
||||
color: "#404040"
|
||||
RalewaySemiBold {
|
||||
id: fileText
|
||||
size: 16
|
||||
elide: Text.ElideLeft
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.topMargin: 4
|
||||
width: parent.width - 10
|
||||
color: "white"
|
||||
text: modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: modalOverlay
|
||||
anchors.fill: parent
|
||||
z: 20
|
||||
color: "#a15d5d5d"
|
||||
visible: false
|
||||
|
||||
// This mouse area captures the cursor events while the modalOverlay is active
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarApp.MessageBox {
|
||||
id: popup
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
closeOnClickOutside: true
|
||||
}
|
||||
|
||||
Column {
|
||||
id: avatarPackager
|
||||
anchors.fill: parent
|
||||
state: "main"
|
||||
states: [
|
||||
State {
|
||||
name: AvatarPackagerState.main
|
||||
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; backButtonVisible: false }
|
||||
PropertyChanges { target: avatarPackagerMain; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.createProject
|
||||
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Create Project") }
|
||||
PropertyChanges { target: createAvatarProject; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: createAvatarProject.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.project
|
||||
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; canRename: true }
|
||||
PropertyChanges { target: avatarProject; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: avatarProject.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.projectUpload
|
||||
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; backButtonEnabled: false }
|
||||
PropertyChanges { target: avatarUploader; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; visible: false }
|
||||
}
|
||||
]
|
||||
|
||||
property alias showModalOverlay: modalOverlay.visible
|
||||
|
||||
property var desktopObject: desktop
|
||||
|
||||
function openProject(path) {
|
||||
let project = AvatarPackagerCore.openAvatarProject(path);
|
||||
if (project) {
|
||||
avatarProject.reset();
|
||||
avatarPackager.state = AvatarPackagerState.project;
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
function openDocs() {
|
||||
Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/create-avatars#how-to-package-your-avatar");
|
||||
}
|
||||
|
||||
AvatarPackagerHeader {
|
||||
id: avatarPackagerHeader
|
||||
colorScheme: root.colorScheme
|
||||
onBackButtonClicked: {
|
||||
avatarPackager.state = AvatarPackagerState.main;
|
||||
}
|
||||
onDocsButtonClicked: {
|
||||
avatarPackager.openDocs();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: windowContent.height - avatarPackagerHeader.height - avatarPackagerFooter.height
|
||||
width: windowContent.width
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#404040"
|
||||
}
|
||||
|
||||
AvatarProject {
|
||||
id: avatarProject
|
||||
colorScheme: root.colorScheme
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
AvatarProjectUpload {
|
||||
id: avatarUploader
|
||||
anchors.fill: parent
|
||||
root: avatarProject
|
||||
}
|
||||
|
||||
CreateAvatarProject {
|
||||
id: createAvatarProject
|
||||
colorScheme: root.colorScheme
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Item {
|
||||
id: avatarPackagerMain
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
|
||||
property var footer: Item {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 17
|
||||
HifiControls.Button {
|
||||
id: createProjectButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: openProjectButton.left
|
||||
anchors.rightMargin: 22
|
||||
height: 40
|
||||
width: 134
|
||||
text: qsTr("New Project")
|
||||
colorScheme: root.colorScheme
|
||||
onClicked: {
|
||||
createAvatarProject.clearInputs();
|
||||
avatarPackager.state = AvatarPackagerState.createProject;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: openProjectButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
height: 40
|
||||
width: 133
|
||||
text: qsTr("Open Project")
|
||||
color: hifi.buttons.blue
|
||||
colorScheme: root.colorScheme
|
||||
onClicked: {
|
||||
avatarPackager.showModalOverlay = true;
|
||||
|
||||
let browser = avatarPackager.desktopObject.fileDialog({
|
||||
selectDirectory: false,
|
||||
dir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH),
|
||||
filter: "Avatar Project FST Files (*.fst)",
|
||||
title: "Open Project (.fst)",
|
||||
});
|
||||
|
||||
browser.canceled.connect(function() {
|
||||
avatarPackager.showModalOverlay = false;
|
||||
});
|
||||
|
||||
browser.selectedFile.connect(function(fileUrl) {
|
||||
let fstFilePath = fileDialogHelper.urlToPath(fileUrl);
|
||||
let currentAvatarProject = avatarPackager.openProject(fstFilePath);
|
||||
if (currentAvatarProject) {
|
||||
avatarPackager.showModalOverlay = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flow {
|
||||
visible: AvatarPackagerCore.recentProjects.length === 0
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 18
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
}
|
||||
RalewayRegular {
|
||||
size: 20
|
||||
color: "white"
|
||||
text: qsTr("Use a custom avatar to express your identity")
|
||||
}
|
||||
RalewayRegular {
|
||||
size: 20
|
||||
color: "white"
|
||||
text: qsTr("To learn more about using this tool, visit our docs")
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
visible: AvatarPackagerCore.recentProjects.length > 0
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 18
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
}
|
||||
spacing: 10
|
||||
|
||||
Repeater {
|
||||
model: AvatarPackagerCore.recentProjects
|
||||
AvatarProjectCard {
|
||||
title: modelData.name
|
||||
path: modelData.projectPath
|
||||
onOpen: avatarPackager.openProject(modelData.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AvatarPackagerFooter {
|
||||
id: avatarPackagerFooter
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,11 +11,18 @@ Rectangle {
|
|||
color: "#252525"
|
||||
|
||||
property alias title: title.text
|
||||
property alias faqEnabled: faq.visible
|
||||
property alias docsEnabled: docs.visible
|
||||
property bool backButtonVisible: true // If false, is not visible and does not take up space
|
||||
property bool backButtonEnabled: true // If false, is not visible but does not affect space
|
||||
property bool canRename: false;
|
||||
property bool canRename: false
|
||||
property int colorScheme
|
||||
|
||||
property color textColor: "white"
|
||||
property color hoverTextColor: "gray"
|
||||
property color pressedTextColor: "#6A6A6A"
|
||||
|
||||
signal backButtonClicked
|
||||
signal docsButtonClicked
|
||||
|
||||
RalewaySemiBold {
|
||||
id: back
|
||||
|
@ -27,7 +34,7 @@ Rectangle {
|
|||
anchors.leftMargin: 16
|
||||
anchors.verticalCenter: back.verticalCenter
|
||||
text: "◀"
|
||||
color: "white"
|
||||
color: textColor
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.backButtonClicked()
|
||||
|
@ -37,37 +44,102 @@ Rectangle {
|
|||
states: [
|
||||
State {
|
||||
name: "hovering"
|
||||
PropertyChanges {
|
||||
target: back
|
||||
color: "gray"
|
||||
}
|
||||
PropertyChanges { target: back; color: hoverTextColor }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 28
|
||||
Item {
|
||||
id: titleArea
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: root.backButtonVisible ? back.right : parent.left
|
||||
anchors.leftMargin: root.backButtonVisible ? 11 : 21
|
||||
anchors.verticalCenter: title.verticalCenter
|
||||
text: qsTr("Avatar Packager")
|
||||
color: "white"
|
||||
anchors.right: docs.left
|
||||
states: [
|
||||
State {
|
||||
name: "renaming"
|
||||
PropertyChanges { target: title; visible: false }
|
||||
PropertyChanges { target: titleInputArea; visible: true }
|
||||
}
|
||||
]
|
||||
|
||||
RalewaySemiBold {
|
||||
id: title
|
||||
size: 28
|
||||
anchors.fill: parent
|
||||
text: qsTr("Avatar Packager")
|
||||
color: "white"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (!root.canRename || AvatarPackagerCore.currentAvatarProject === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
titleArea.state = "renaming";
|
||||
titleInput.text = AvatarPackagerCore.currentAvatarProject.name;
|
||||
titleInput.selectAll();
|
||||
titleInput.forceActiveFocus(Qt.MouseFocusReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: titleInputArea
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
|
||||
HifiControls.TextField {
|
||||
id: titleInput
|
||||
anchors.fill: parent
|
||||
text: ""
|
||||
colorScheme: root.colorScheme
|
||||
font.family: "Fira Sans"
|
||||
font.pixelSize: 28
|
||||
z: 200
|
||||
onFocusChanged: {
|
||||
if (titleArea.state === "renaming" && !titleArea.focus) {
|
||||
//titleArea.state = "";
|
||||
accepted();
|
||||
}
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
titleArea.state = "";
|
||||
}
|
||||
}
|
||||
onAccepted: {
|
||||
if (acceptableInput) {
|
||||
//AvatarPackagerCore.renameProject(text);
|
||||
console.warn(text);
|
||||
AvatarPackagerCore.currentAvatarProject.name = text;
|
||||
console.warn(AvatarPackagerCore.currentAvatarProject.name);
|
||||
|
||||
}
|
||||
titleArea.state = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: faq
|
||||
id: docs
|
||||
visible: false
|
||||
size: 28
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 16
|
||||
anchors.verticalCenter: faq.verticalCenter
|
||||
anchors.verticalCenter: docs.verticalCenter
|
||||
text: qsTr("Docs")
|
||||
color: "white"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
docsButtonClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ Item {
|
|||
readonly property string main: "main"
|
||||
readonly property string project: "project"
|
||||
readonly property string createProject: "createProject"
|
||||
readonly property string projectUpload: "projectUpload"
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ Item {
|
|||
}
|
||||
});
|
||||
root.uploader.send();
|
||||
avatarPackager.state = "project-upload";
|
||||
avatarPackager.state = AvatarPackagerState.projectUpload;
|
||||
}
|
||||
|
||||
function showConfirmUploadPopup() {
|
||||
|
|
|
@ -53,11 +53,14 @@ Item {
|
|||
|
||||
RalewayBold {
|
||||
id: title
|
||||
elide: "ElideRight"
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 13
|
||||
left: parent.left
|
||||
leftMargin: 16
|
||||
right: parent.right
|
||||
rightMargin: 16
|
||||
}
|
||||
text: "<title missing>"
|
||||
size: 16
|
||||
|
@ -69,7 +72,11 @@ Item {
|
|||
top: title.bottom
|
||||
left: parent.left
|
||||
leftMargin: 32
|
||||
right: background.right
|
||||
rightMargin: 16
|
||||
}
|
||||
elide: "ElideLeft"
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: "<path missing>"
|
||||
size: 20
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ Item {
|
|||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (avatarPackager.state =="project-upload") {
|
||||
avatarPackager.state = "project"
|
||||
if (avatarPackager.state === AvatarPackagerState.projectUpload) {
|
||||
avatarPackager.state = AvatarPackagerState.project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ Item {
|
|||
AvatarUploadStatusItem {
|
||||
id: statusCategories
|
||||
uploader: root.uploader
|
||||
text: "Retreiving categories"
|
||||
text: "Retrieving categories"
|
||||
|
||||
uploaderState: 1
|
||||
}
|
||||
|
@ -189,8 +189,8 @@ Item {
|
|||
colorScheme: root.colorScheme
|
||||
width: 133
|
||||
height: 40
|
||||
onClicked: function() {
|
||||
avatarPackager.state = "project"
|
||||
onClicked: {
|
||||
avatarPackager.state = AvatarPackagerState.project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ Item {
|
|||
property int uploaderState;
|
||||
property var uploader;
|
||||
|
||||
state: root.uploader.state > uploaderState
|
||||
? "success"
|
||||
: (root.uploader.error !== 0 ? "fail" : (root.uploader.state === uploaderState ? "running" : ""))
|
||||
state: root.uploader === undefined ? "" :
|
||||
(root.uploader.state > uploaderState ? "success"
|
||||
: (root.uploader.error !== 0 ? "fail" : (root.uploader.state === uploaderState ? "running" : "")))
|
||||
|
||||
states: [
|
||||
State {
|
||||
|
@ -90,4 +90,4 @@ Item {
|
|||
size: 28
|
||||
color: "#777777"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ Item {
|
|||
|
||||
ProjectInputControl {
|
||||
id: textureFolder
|
||||
label: "Specify Texture Folder <i> - optional</i>"
|
||||
label: "Specify Texture Folder - <i>Optional</i>"
|
||||
colorScheme: root.colorScheme
|
||||
browseEnabled: true
|
||||
browseFolder: true
|
||||
|
@ -128,5 +128,4 @@ Item {
|
|||
onTextChanged: checkErrors()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ Column {
|
|||
colorScheme: root.colorScheme
|
||||
onClicked: {
|
||||
avatarPackager.showModalOverlay = true;
|
||||
let browser = desktop.fileDialog({
|
||||
let browser = avatarPackager.desktopObject.fileDialog({
|
||||
selectDirectory: browseFolder,
|
||||
dir: browseDir,
|
||||
filter: browseFilter,
|
||||
|
|
15
interface/resources/qml/hifi/tablet/AvatarPackager.qml
Normal file
15
interface/resources/qml/hifi/tablet/AvatarPackager.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.0
|
||||
import "../avatarPackager" 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 480
|
||||
height: 706
|
||||
|
||||
AvatarPackagerApp {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
desktopObject: tabletRoot
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "AvatarPackager.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QUrl>
|
||||
|
@ -19,8 +21,9 @@
|
|||
#include "ModelSelector.h"
|
||||
#include <avatar/MarketplaceItemUploader.h>
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
#include "ui/TabletScriptingInterface.h"
|
||||
|
||||
std::once_flag setupQMLTypesFlag;
|
||||
AvatarPackager::AvatarPackager() {
|
||||
|
@ -38,31 +41,32 @@ AvatarPackager::AvatarPackager() {
|
|||
}
|
||||
|
||||
bool AvatarPackager::open() {
|
||||
static const QUrl url{ "hifi/AvatarPackager.qml" };
|
||||
static const QUrl url{ "hifi/AvatarPackagerWindow.qml" };
|
||||
|
||||
const auto packageModelDialogCreated = [=](QQmlContext* context, QObject* newObject) {
|
||||
context->setContextProperty("AvatarPackagerCore", this);
|
||||
};
|
||||
DependencyManager::get<OffscreenUi>()->show(url, "AvatarPackager", packageModelDialogCreated);
|
||||
|
||||
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
|
||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||
|
||||
if (tablet->getToolbarMode()) {
|
||||
DependencyManager::get<OffscreenUi>()->show(url, "AvatarPackager", packageModelDialogCreated);
|
||||
} else {
|
||||
static const QUrl url("hifi/tablet/AvatarPackager.qml");
|
||||
if (!tablet->isPathLoaded(url)) {
|
||||
tablet->getTabletSurface()->getSurfaceContext()->setContextProperty("AvatarPackagerCore", this);
|
||||
tablet->pushOntoStack(url);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
AvatarProject* AvatarPackager::openAvatarProject(const QString& avatarProjectFSTPath) {
|
||||
if (_currentAvatarProject) {
|
||||
_currentAvatarProject->deleteLater();
|
||||
}
|
||||
_currentAvatarProject = AvatarProject::openAvatarProject(avatarProjectFSTPath);
|
||||
if (_currentAvatarProject) {
|
||||
addRecentProject(avatarProjectFSTPath, _currentAvatarProject->getProjectName());
|
||||
}
|
||||
qDebug() << "_currentAvatarProject has" << (QQmlEngine::objectOwnership(_currentAvatarProject) == QQmlEngine::CppOwnership ? "CPP" : "JS") << "OWNERSHIP";
|
||||
QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership);
|
||||
emit avatarProjectChanged();
|
||||
return _currentAvatarProject;
|
||||
}
|
||||
|
||||
void AvatarPackager::addRecentProject(QString fstPath, QString projectName) {
|
||||
void AvatarPackager::addCurrentProjectToRecentProjects() {
|
||||
const int MAX_RECENT_PROJECTS = 5;
|
||||
const QString& fstPath = _currentAvatarProject->getFSTPath();
|
||||
auto removeProjects = QVector<RecentAvatarProject>();
|
||||
for (auto project : _recentProjects) {
|
||||
if (project.getProjectFSTPath() == fstPath) {
|
||||
|
@ -73,27 +77,61 @@ void AvatarPackager::addRecentProject(QString fstPath, QString projectName) {
|
|||
_recentProjects.removeOne(removeProject);
|
||||
}
|
||||
|
||||
RecentAvatarProject newRecentProject = RecentAvatarProject(projectName, fstPath);
|
||||
const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath);
|
||||
_recentProjects.prepend(newRecentProject);
|
||||
|
||||
while (_recentProjects.size() > MAX_RECENT_PROJECTS) {
|
||||
_recentProjects.pop_back();
|
||||
}
|
||||
|
||||
_recentProjectsSetting.set(recentProjectsToVariantList());
|
||||
_recentProjectsSetting.set(recentProjectsToVariantList(false));
|
||||
emit recentProjectsChanged();
|
||||
}
|
||||
|
||||
QVariantList AvatarPackager::recentProjectsToVariantList(bool includeProjectPaths) {
|
||||
QVariantList result;
|
||||
for (const auto& project : _recentProjects) {
|
||||
QVariantMap projectVariant;
|
||||
projectVariant.insert("name", project.getProjectName());
|
||||
projectVariant.insert("path", project.getProjectFSTPath());
|
||||
if (includeProjectPaths) {
|
||||
projectVariant.insert("projectPath", project.getProjectPath());
|
||||
}
|
||||
result.append(projectVariant);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
void AvatarPackager::recentProjectsFromVariantList(QVariantList projectsVariant) {
|
||||
_recentProjects.clear();
|
||||
for (const auto& projectVariant : projectsVariant) {
|
||||
auto map = projectVariant.toMap();
|
||||
_recentProjects.append(RecentAvatarProject(map.value("name").toString(), map.value("path").toString()));
|
||||
}
|
||||
}
|
||||
|
||||
AvatarProject* AvatarPackager::openAvatarProject(const QString& avatarProjectFSTPath) {
|
||||
setAvatarProject(AvatarProject::openAvatarProject(avatarProjectFSTPath));
|
||||
return _currentAvatarProject;
|
||||
}
|
||||
|
||||
AvatarProject* AvatarPackager::createAvatarProject(const QString& projectsFolder, const QString& avatarProjectName, const QString& avatarModelPath, const QString& textureFolder) {
|
||||
setAvatarProject(AvatarProject::createAvatarProject(projectsFolder, avatarProjectName, avatarModelPath, textureFolder));
|
||||
return _currentAvatarProject;
|
||||
}
|
||||
|
||||
void AvatarPackager::setAvatarProject(AvatarProject* avatarProject) {
|
||||
if (avatarProject == _currentAvatarProject) {
|
||||
return;
|
||||
}
|
||||
if (_currentAvatarProject) {
|
||||
_currentAvatarProject->deleteLater();
|
||||
}
|
||||
_currentAvatarProject = AvatarProject::createAvatarProject(projectsFolder, avatarProjectName, avatarModelPath, textureFolder);
|
||||
_currentAvatarProject = avatarProject;
|
||||
if (_currentAvatarProject) {
|
||||
addRecentProject(_currentAvatarProject->getFSTPath(), _currentAvatarProject->getProjectName());
|
||||
addCurrentProjectToRecentProjects();
|
||||
connect(_currentAvatarProject, &AvatarProject::nameChanged, this, &AvatarPackager::addCurrentProjectToRecentProjects);
|
||||
QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership);
|
||||
}
|
||||
qDebug() << "_currentAvatarProject has" << (QQmlEngine::objectOwnership(_currentAvatarProject) == QQmlEngine::CppOwnership ? "CPP" : "JS") << "OWNERSHIP";
|
||||
QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership);
|
||||
emit avatarProjectChanged();
|
||||
return _currentAvatarProject;
|
||||
}
|
||||
|
|
|
@ -35,12 +35,14 @@ public:
|
|||
_projectFSTPath = other._projectFSTPath;
|
||||
}
|
||||
|
||||
~RecentAvatarProject() = default;
|
||||
|
||||
QString getProjectName() const { return _projectName; }
|
||||
|
||||
QString getProjectFSTPath() const { return _projectFSTPath; }
|
||||
|
||||
QString getProjectPath() const {
|
||||
return QFileInfo(_projectFSTPath).absoluteDir().absolutePath();
|
||||
}
|
||||
|
||||
bool operator==(const RecentAvatarProject& other) const {
|
||||
return _projectName == other._projectName && _projectFSTPath == other._projectFSTPath;
|
||||
}
|
||||
|
@ -72,31 +74,18 @@ signals:
|
|||
private:
|
||||
Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; };
|
||||
Q_INVOKABLE QString getAvatarProjectsPath() const { return AvatarProject::getDefaultProjectsPath(); }
|
||||
Q_INVOKABLE QVariantList getRecentProjects() { return recentProjectsToVariantList(); }
|
||||
Q_INVOKABLE QVariantList getRecentProjects() { return recentProjectsToVariantList(true); }
|
||||
|
||||
void addRecentProject(QString fstPath, QString projectName);
|
||||
void setAvatarProject(AvatarProject* avatarProject);
|
||||
|
||||
void addCurrentProjectToRecentProjects();
|
||||
|
||||
AvatarProject* _currentAvatarProject{ nullptr };
|
||||
QVector<RecentAvatarProject> _recentProjects;
|
||||
QVariantList recentProjectsToVariantList() {
|
||||
QVariantList result;
|
||||
for (const auto& project : _recentProjects) {
|
||||
QVariantMap projectVariant;
|
||||
projectVariant.insert("name", project.getProjectName());
|
||||
projectVariant.insert("path", project.getProjectFSTPath());
|
||||
result.append(projectVariant);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void recentProjectsFromVariantList(QVariantList projectsVariant) {
|
||||
_recentProjects.clear();
|
||||
for (const auto& projectVariant : projectsVariant) {
|
||||
auto map = projectVariant.toMap();
|
||||
_recentProjects.append(RecentAvatarProject(map.value("name").toString(), map.value("path").toString()));
|
||||
}
|
||||
}
|
||||
QVariantList recentProjectsToVariantList(bool includeProjectPaths);
|
||||
|
||||
void recentProjectsFromVariantList(QVariantList projectsVariant);
|
||||
|
||||
|
||||
Setting::Handle<QVariantList> _recentProjectsSetting{ "io.highfidelity.avatarPackager.recentProjects", QVariantList() };
|
||||
|
|
|
@ -81,18 +81,18 @@ AvatarProject* AvatarProject::createAvatarProject(const QString& projectsFolder,
|
|||
}
|
||||
};
|
||||
|
||||
foreach(const HFMMaterial mat, hfmModel->materials) {
|
||||
addTextureToList(mat.normalTexture);
|
||||
addTextureToList(mat.albedoTexture);
|
||||
addTextureToList(mat.opacityTexture);
|
||||
addTextureToList(mat.glossTexture);
|
||||
addTextureToList(mat.roughnessTexture);
|
||||
addTextureToList(mat.specularTexture);
|
||||
addTextureToList(mat.metallicTexture);
|
||||
addTextureToList(mat.emissiveTexture);
|
||||
addTextureToList(mat.occlusionTexture);
|
||||
addTextureToList(mat.scatteringTexture);
|
||||
addTextureToList(mat.lightmapTexture);
|
||||
foreach(const HFMMaterial material, hfmModel->materials) {
|
||||
addTextureToList(material.normalTexture);
|
||||
addTextureToList(material.albedoTexture);
|
||||
addTextureToList(material.opacityTexture);
|
||||
addTextureToList(material.glossTexture);
|
||||
addTextureToList(material.roughnessTexture);
|
||||
addTextureToList(material.specularTexture);
|
||||
addTextureToList(material.metallicTexture);
|
||||
addTextureToList(material.emissiveTexture);
|
||||
addTextureToList(material.occlusionTexture);
|
||||
addTextureToList(material.scatteringTexture);
|
||||
addTextureToList(material.lightmapTexture);
|
||||
}
|
||||
|
||||
QDir textureDir(textureFolder.isEmpty() ? fbxInfo.absoluteDir() : textureFolder);
|
||||
|
@ -152,7 +152,6 @@ AvatarProject::AvatarProject(FST* fst) {
|
|||
_fst->setScriptPaths(getScriptPaths(QDir(_directory.path() + "/scripts")));
|
||||
_fst->write();
|
||||
|
||||
//_projectFiles = _directory.entryList();
|
||||
refreshProjectFiles();
|
||||
|
||||
_projectPath = fileInfo.absoluteDir().absolutePath();
|
||||
|
|
|
@ -28,10 +28,10 @@ class AvatarProject : public QObject {
|
|||
|
||||
Q_PROPERTY(QStringList projectFiles READ getProjectFiles NOTIFY projectFilesChanged)
|
||||
|
||||
Q_PROPERTY(QString projectFolderPath READ getProjectPath)
|
||||
Q_PROPERTY(QString projectFSTPath READ getFSTPath)
|
||||
Q_PROPERTY(QString projectFBXPath READ getFBXPath)
|
||||
Q_PROPERTY(QString name READ getProjectName NOTIFY nameChanged)
|
||||
Q_PROPERTY(QString projectFolderPath READ getProjectPath CONSTANT)
|
||||
Q_PROPERTY(QString projectFSTPath READ getFSTPath CONSTANT)
|
||||
Q_PROPERTY(QString projectFBXPath READ getFBXPath CONSTANT)
|
||||
Q_PROPERTY(QString name READ getProjectName WRITE setProjectName NOTIFY nameChanged)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting);
|
||||
|
@ -39,6 +39,13 @@ public:
|
|||
Q_INVOKABLE QStringList getProjectFiles() const;
|
||||
|
||||
Q_INVOKABLE QString getProjectName() const { return _fst->getName(); }
|
||||
Q_INVOKABLE void setProjectName(const QString& newProjectName) {
|
||||
if (newProjectName.trimmed().length() > 0) {
|
||||
_fst->setName(newProjectName);
|
||||
_fst->write();
|
||||
emit nameChanged();
|
||||
}
|
||||
}
|
||||
Q_INVOKABLE QString getProjectPath() const { return _projectPath; }
|
||||
Q_INVOKABLE QString getFSTPath() const { return _fst->getPath(); }
|
||||
Q_INVOKABLE QString getFBXPath() const { return _fst->getModelPath(); }
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <QFileInfo>
|
||||
#include <hfm/HFM.h>
|
||||
|
||||
FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) {
|
||||
FST::FST(QString fstPath, QVariantHash data) : _fstPath(std::move(fstPath)) {
|
||||
|
||||
auto setValueFromFSTData = [&data] (const QString& propertyID, auto &targetProperty) mutable {
|
||||
if (data.contains(propertyID)) {
|
||||
|
@ -38,7 +38,7 @@ FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) {
|
|||
_other = data;
|
||||
}
|
||||
|
||||
FST* FST::createFSTFromModel(QString fstPath, QString modelFilePath, const hfm::Model& hfmModel) {
|
||||
FST* FST::createFSTFromModel(const QString& fstPath, const QString& modelFilePath, const hfm::Model& hfmModel) {
|
||||
QVariantHash mapping;
|
||||
|
||||
// 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 {
|
|||
Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID)
|
||||
Q_PROPERTY(bool hasMarketplaceID READ getHasMarketplaceID NOTIFY marketplaceIDChanged)
|
||||
public:
|
||||
FST(const QString& fstPath, QVariantHash data);
|
||||
FST(QString fstPath, QVariantHash data);
|
||||
|
||||
static FST* createFSTFromModel(QString fstPath, QString modelFilePath, const hfm::Model& hfmModel);
|
||||
static FST* createFSTFromModel(const QString& fstPath, const QString& modelFilePath, const hfm::Model& hfmModel);
|
||||
|
||||
QString absoluteModelPath() const;
|
||||
|
||||
|
|
Loading…
Reference in a new issue