- rename functionality

- avatar packager works in tablet now
This commit is contained in:
Thijs Wenker 2018-12-29 03:31:56 +01:00
parent 1ec8aa8fa5
commit cb33a91a34
18 changed files with 543 additions and 372 deletions

View file

@ -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
}
}
}
}

View 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
}
}

View file

@ -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
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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"
}

View file

@ -178,7 +178,7 @@ Item {
}
});
root.uploader.send();
avatarPackager.state = "project-upload";
avatarPackager.state = AvatarPackagerState.projectUpload;
}
function showConfirmUploadPopup() {

View file

@ -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
}

View file

@ -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;
}
}
}

View file

@ -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"
}
}
}

View file

@ -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()
}
}
}

View file

@ -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,

View 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
}
}

View file

@ -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;
}

View file

@ -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() };

View file

@ -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();

View file

@ -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(); }

View file

@ -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

View file

@ -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;