diff options
Diffstat (limited to 'src/Authoring')
17 files changed, 596 insertions, 198 deletions
diff --git a/src/Authoring/Studio/MainFrm.cpp b/src/Authoring/Studio/MainFrm.cpp index 52928e89..86c165a9 100644 --- a/src/Authoring/Studio/MainFrm.cpp +++ b/src/Authoring/Studio/MainFrm.cpp @@ -57,6 +57,7 @@ #include "StudioTutorialWidget.h" #include "remotedeploymentsender.h" #include "InspectorControlView.h" +#include "ProjectView.h" #include <QtGui/qevent.h> #include <QtGui/qdesktopservices.h> @@ -95,6 +96,7 @@ CMainFrame::CMainFrame() connect(m_ui->actionSave_As, &QAction::triggered, this, &CMainFrame::OnFileSaveAs); connect(m_ui->actionSave_a_Copy, &QAction::triggered, this, &CMainFrame::OnFileSaveCopy); connect(m_ui->action_Revert, &QAction::triggered, this, &CMainFrame::OnFileRevert); + connect(m_ui->actionImportAssets, &QAction::triggered, this, &CMainFrame::OnFileImportAssets); connect(m_ui->action_Connect_to_Device, &QAction::triggered, this, &CMainFrame::OnFileConnectToDevice); connect(m_remoteDeploymentSender, &RemoteDeploymentSender::connectionChanged, @@ -326,6 +328,7 @@ int CMainFrame::OnCreate() m_ui->actionSave_a_Copy->setEnabled(false); m_ui->action_Connect_to_Device->setEnabled(false); m_ui->action_Revert->setEnabled(false); + m_ui->actionImportAssets->setEnabled(false); setCentralWidget(m_SceneView); return 0; @@ -353,6 +356,7 @@ void CMainFrame::OnNewPresentation() m_ui->actionSave_a_Copy->setEnabled(true); m_ui->action_Connect_to_Device->setEnabled(true); m_ui->action_Revert->setEnabled(true); + m_ui->actionImportAssets->setEnabled(true); } //============================================================================== @@ -1660,6 +1664,11 @@ void CMainFrame::OnFileRevert() g_StudioApp.OnRevert(); } +void CMainFrame::OnFileImportAssets() +{ + m_PaletteManager->projectView()->assetImportAction(0); +} + void CMainFrame::OnFileConnectToDevice() { if (m_remoteDeploymentSender->isConnected()) { diff --git a/src/Authoring/Studio/MainFrm.h b/src/Authoring/Studio/MainFrm.h index 2aae6bdb..ef8164f1 100644 --- a/src/Authoring/Studio/MainFrm.h +++ b/src/Authoring/Studio/MainFrm.h @@ -108,6 +108,7 @@ public: void OnFileSaveCopy(); void OnFileNew(); void OnFileRevert(); + void OnFileImportAssets(); void OnFileConnectToDevice(); void OnFileOpenRecent(int nID); diff --git a/src/Authoring/Studio/MainFrm.ui b/src/Authoring/Studio/MainFrm.ui index 11def626..f944eeb3 100644 --- a/src/Authoring/Studio/MainFrm.ui +++ b/src/Authoring/Studio/MainFrm.ui @@ -43,6 +43,7 @@ <addaction name="actionSave_As"/> <addaction name="actionSave_a_Copy"/> <addaction name="action_Revert"/> + <addaction name="actionImportAssets"/> <addaction name="action_Connect_to_Device"/> <addaction name="separator"/> <addaction name="menuRecent_Projects"/> @@ -811,6 +812,17 @@ <string>Preview with Runtime 2</string> </property> </action> + <action name="actionImportAssets"> + <property name="text"> + <string>Import Assets...</string> + </property> + <property name="iconText"> + <string>Import Assets</string> + </property> + <property name="toolTip"> + <string>Import Assets</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp index 1d807884..7c8c8a14 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp +++ b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp @@ -48,6 +48,12 @@ ProjectContextMenu::ProjectContextMenu(ProjectView *parent, int index) connect(action, &QAction::triggered, this, &ProjectContextMenu::handleCopyFullPath); addAction(action); + addSeparator(); + + action = new QAction(tr("Import Assets...")); + connect(action, &QAction::triggered, this, &ProjectContextMenu::handleImportAssets); + addAction(action); + if (m_view->isRefreshable(m_index)) { addSeparator(); action = new QAction(tr("Refresh Import...")); @@ -79,3 +85,8 @@ void ProjectContextMenu::handleRefreshImport() { m_view->refreshImport(m_index); } + +void ProjectContextMenu::handleImportAssets() +{ + m_view->assetImportInContext(m_index); +} diff --git a/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h index 51dc8c5d..965767ac 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h +++ b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h @@ -45,6 +45,7 @@ private Q_SLOTS: void handleCopyPath(); void handleCopyFullPath(); void handleRefreshImport(); + void handleImportAssets(); private: ProjectView *m_view; diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp index f383b6f0..8d2a39b7 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp +++ b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp @@ -161,6 +161,36 @@ void ProjectFileSystemModel::updateReferences(bool emitDataChanged) Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {IsReferencedRole}); } +Q3DStudio::DocumentEditorFileType::Enum ProjectFileSystemModel::assetTypeForRow(int row) +{ + if (row <= 0 || row >= m_items.size()) + return Q3DStudio::DocumentEditorFileType::Unknown; + + const QString rootPath = m_items[0].index.data(QFileSystemModel::FilePathRole).toString(); + QString path = m_items[row].index.data(QFileSystemModel::FilePathRole).toString(); + QFileInfo fi(path); + if (!fi.isDir()) + path = fi.absolutePath(); + path = path.mid(rootPath.length() + 1); + const int slash = path.indexOf(QStringLiteral("/")); + if (slash >= 0) + path = path.left(slash); + if (path == QStringLiteral("effects")) + return Q3DStudio::DocumentEditorFileType::Effect; + else if (path == QStringLiteral("fonts")) + return Q3DStudio::DocumentEditorFileType::Font; + else if (path == QStringLiteral("maps")) + return Q3DStudio::DocumentEditorFileType::Image; + else if (path == QStringLiteral("materials")) + return Q3DStudio::DocumentEditorFileType::Material; + else if (path == QStringLiteral("models")) + return Q3DStudio::DocumentEditorFileType::DAE; + else if (path == QStringLiteral("scripts")) + return Q3DStudio::DocumentEditorFileType::Behavior; + + return Q3DStudio::DocumentEditorFileType::Unknown; +} + void ProjectFileSystemModel::setRootPath(const QString &path) { setRootIndex(m_model->setRootPath(path)); @@ -186,6 +216,7 @@ void ProjectFileSystemModel::setRootIndex(const QModelIndex &rootIndex) void ProjectFileSystemModel::clearModelData() { beginResetModel(); + m_defaultDirToAbsPathMap.clear(); m_items.clear(); endResetModel(); } @@ -286,24 +317,53 @@ bool ProjectFileSystemModel::hasValidUrlsForDropping(const QList<QUrl> &urls) co return false; } -void ProjectFileSystemModel::dropUrls(const QList<QUrl> &urls, int row) +void ProjectFileSystemModel::importUrls(const QList<QUrl> &urls, int row, bool autoSort) { - Q_ASSERT(row >= 0 && row < m_items.size()); - - const auto &item = m_items.at(row); - const QString targetPath = item.index.data(QFileSystemModel::FilePathRole).toString(); + if (row < 0 || row >= m_items.size()) + row = 0; // Import to root folder row not specified + + // If importing via buttons or doing in-context import to project root, + // sort imported items to default folders according to their type + const bool sortToDefaults = autoSort || row == 0; + if (sortToDefaults) + updateDefaultDirMap(); + + const TreeItem &item = m_items.at(row); + QString targetPath = item.index.data(QFileSystemModel::FilePathRole).toString(); + QFileInfo fi(targetPath); + if (!fi.isDir()) + targetPath = fi.absolutePath(); const QDir targetDir(targetPath); - if (targetDir.exists()) { - for (const auto& url : urls) - dropUrl(targetPath, url); + QStringList expandPaths; + + for (const auto &url : urls) { + QString sortedPath = targetPath; + QDir sortedDir = targetDir; + + if (sortToDefaults) { + const QString defaultDir = m_defaultDirToAbsPathMap.value( + g_StudioApp.GetDialogs()->defaultDirForUrl(url)); + if (!defaultDir.isEmpty()) { + sortedPath = defaultDir; + sortedDir.setPath(sortedPath); + } + } + + if (sortedDir.exists()) { + expandPaths << sortedPath; + importUrl(sortedPath, url); + } + } - if (!item.expanded) - expand(row); + for (const QString &expandPath : qAsConst(expandPaths)) { + int expandRow = rowForPath(expandPath); + if (expandRow >= 0 && !m_items[expandRow].expanded) + expand(expandRow); } } -void ProjectFileSystemModel::dropUrl(const QDir &targetDir, const QUrl &url) const +void ProjectFileSystemModel::importUrl(const QDir &targetDir, const QUrl &url) const { using namespace Q3DStudio; using namespace qt3dsimp; @@ -395,6 +455,16 @@ void ProjectFileSystemModel::dropUrl(const QDir &targetDir, const QUrl &url) con } } +int ProjectFileSystemModel::rowForPath(const QString &path) const +{ + for (int i = m_items.size() - 1; i >= 0 ; --i) { + const QString itemPath = m_items[i].index.data(QFileSystemModel::FilePathRole).toString(); + if (path == itemPath) + return i; + } + return -1; +} + void ProjectFileSystemModel::collapse(int row) { Q_ASSERT(row >= 0 && row < m_items.size()); @@ -593,3 +663,31 @@ void ProjectFileSystemModel::modelLayoutChanged() Q_EMIT dataChanged(index(0), index(itemCount - 1)); } + +void ProjectFileSystemModel::updateDefaultDirMap() +{ + if (m_defaultDirToAbsPathMap.isEmpty()) { + m_defaultDirToAbsPathMap.insert(QStringLiteral("effects"), QString()); + m_defaultDirToAbsPathMap.insert(QStringLiteral("fonts"), QString()); + m_defaultDirToAbsPathMap.insert(QStringLiteral("maps"), QString()); + m_defaultDirToAbsPathMap.insert(QStringLiteral("materials"), QString()); + m_defaultDirToAbsPathMap.insert(QStringLiteral("models"), QString()); + m_defaultDirToAbsPathMap.insert(QStringLiteral("scripts"), QString()); + } + + const QString rootPath = m_items[0].index.data(QFileSystemModel::FilePathRole).toString(); + const QStringList keys = m_defaultDirToAbsPathMap.keys(); + for (const QString &key : keys) { + QString currentValue = m_defaultDirToAbsPathMap[key]; + if (currentValue.isEmpty()) { + const QString defaultPath = rootPath + QStringLiteral("/") + key; + const QFileInfo fi(defaultPath); + if (fi.exists() && fi.isDir()) + m_defaultDirToAbsPathMap.insert(key, defaultPath); + } else { + const QFileInfo fi(currentValue); + if (!fi.exists()) + m_defaultDirToAbsPathMap.insert(key, QString()); + } + } +} diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h index 87375342..d32f5695 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h +++ b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h @@ -29,11 +29,12 @@ #define TREEVIEWADAPTOR_H #include "StudioObjectTypes.h" +#include "DocumentEditorEnumerations.h" -#include <QFileSystemModel> -#include <QAbstractListModel> -#include <QList> -#include <QUrl> +#include <QtWidgets/qfilesystemmodel.h> +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/qlist.h> +#include <QtCore/qurl.h> QT_FORWARD_DECLARE_CLASS(QFileSystemModel) @@ -63,11 +64,12 @@ public: bool isRefreshable(int row) const; void updateReferences(bool emitDataChanged); + Q3DStudio::DocumentEditorFileType::Enum assetTypeForRow(int row); Q_INVOKABLE void expand(int row); Q_INVOKABLE void collapse(int row); - Q_INVOKABLE void dropUrls(const QList<QUrl> &urls, int row); + Q_INVOKABLE void importUrls(const QList<QUrl> &urls, int row, bool autoSort = true); Q_INVOKABLE bool hasValidUrlsForDropping(const QList<QUrl> &urls) const; Q_SIGNALS: @@ -84,13 +86,16 @@ private: EStudioObjectType getIconType(const QString &path) const; bool isVisible(const QModelIndex& modelIndex) const; bool hasVisibleChildren(const QModelIndex &modelIndex) const; - void dropUrl(const QDir &targetDir, const QUrl &url) const; + void importUrl(const QDir &targetDir, const QUrl &url) const; + int rowForPath(const QString &path) const; void modelRowsInserted(const QModelIndex &parent, int start, int end); void modelRowsRemoved(const QModelIndex &parent, int start, int end); void modelRowsMoved(const QModelIndex &parent, int start, int end); void modelLayoutChanged(); + void updateDefaultDirMap(); + struct TreeItem { QPersistentModelIndex index; int depth; @@ -103,6 +108,7 @@ private: QPersistentModelIndex m_rootIndex; QList<TreeItem> m_items; QStringList m_references; + QHash<QString, QString> m_defaultDirToAbsPathMap; }; #endif // TREEVIEWADAPTOR_H diff --git a/src/Authoring/Studio/Palettes/Project/ProjectView.cpp b/src/Authoring/Studio/Palettes/Project/ProjectView.cpp index 0ea1bb56..840295bd 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectView.cpp +++ b/src/Authoring/Studio/Palettes/Project/ProjectView.cpp @@ -56,24 +56,21 @@ ProjectView::ProjectView(const QSize &preferredSize, QWidget *parent) : QQuickWi const QString theApplicationPath = Qt3DSFile::GetApplicationDirectory().GetPath().toQString(); - m_BehaviorDir = Qt3DSFile( - Q3DStudio::CString::fromQString(theApplicationPath - + QLatin1String("/Content/Behavior Library"))); - m_EffectDir = Qt3DSFile( - Q3DStudio::CString::fromQString(theApplicationPath - + QLatin1String("/Content/Effect Library"))); - m_FontDir = Qt3DSFile( - Q3DStudio::CString::fromQString(theApplicationPath - + QLatin1String("/Content/Font Library"))); - m_ImageDir = Qt3DSFile( - Q3DStudio::CString::fromQString(theApplicationPath - + QLatin1String("/Content/Maps Library"))); - m_MaterialDir = Qt3DSFile( - Q3DStudio::CString::fromQString(theApplicationPath - + QLatin1String("/Content/Material Library"))); - m_ModelDir = Qt3DSFile( - Q3DStudio::CString::fromQString(theApplicationPath - + QLatin1String("/Content/Models Library"))); + m_defaultBehaviorDir = theApplicationPath + QStringLiteral("/Content/Behavior Library"); + m_defaultEffectDir = theApplicationPath + QStringLiteral("/Content/Effect Library"); + m_defaultFontDir = theApplicationPath + QStringLiteral("/Content/Font Library"); + m_defaultImageDir = theApplicationPath + QStringLiteral("/Content/Maps Library"); + m_defaultMaterialDir = theApplicationPath + QStringLiteral("/Content/Material Library"); + m_defaultModelDir = theApplicationPath + QStringLiteral("/Content/Models Library"); + + m_BehaviorDir = m_defaultBehaviorDir; + m_EffectDir = m_defaultEffectDir; + m_FontDir = m_defaultFontDir; + m_ImageDir = m_defaultImageDir; + m_MaterialDir = m_defaultMaterialDir; + m_ModelDir = m_defaultModelDir; + + m_assetImportDir = theApplicationPath + QStringLiteral("/Content"); setResizeMode(QQuickWidget::SizeRootObjectToView); QTimer::singleShot(0, this, &ProjectView::initialize); @@ -107,34 +104,92 @@ void ProjectView::initialize() setSource(QUrl("qrc:/Palettes/Project/ProjectView.qml"_L1)); } -void ProjectView::effectAction() +void ProjectView::effectAction(int row) { - m_EffectDir.Execute(); + m_EffectDir = m_defaultEffectDir; + QList<QUrl> urls = g_StudioApp.GetDialogs()->SelectAssets( + m_EffectDir, Q3DStudio::DocumentEditorFileType::Effect); + m_ProjectModel->importUrls(urls, row); } -void ProjectView::fontAction() +void ProjectView::fontAction(int row) { - m_FontDir.Execute(); + m_FontDir = m_defaultFontDir; + QList<QUrl> urls = g_StudioApp.GetDialogs()->SelectAssets( + m_FontDir, Q3DStudio::DocumentEditorFileType::Font); + m_ProjectModel->importUrls(urls, row); } -void ProjectView::imageAction() +void ProjectView::imageAction(int row) { - m_ImageDir.Execute(); + m_ImageDir = m_defaultImageDir; + QList<QUrl> urls = g_StudioApp.GetDialogs()->SelectAssets( + m_ImageDir, Q3DStudio::DocumentEditorFileType::Image); + m_ProjectModel->importUrls(urls, row); } -void ProjectView::materialAction() +void ProjectView::materialAction(int row) { - m_MaterialDir.Execute(); + m_MaterialDir = m_defaultMaterialDir; + QList<QUrl> urls = g_StudioApp.GetDialogs()->SelectAssets( + m_MaterialDir, Q3DStudio::DocumentEditorFileType::Material); + m_ProjectModel->importUrls(urls, row); } -void ProjectView::modelAction() +void ProjectView::modelAction(int row) { - m_ModelDir.Execute(); + m_ModelDir = m_defaultModelDir; + QList<QUrl> urls = g_StudioApp.GetDialogs()->SelectAssets( + m_ModelDir, Q3DStudio::DocumentEditorFileType::DAE); + m_ProjectModel->importUrls(urls, row); } -void ProjectView::behaviorAction() +void ProjectView::behaviorAction(int row) { - m_BehaviorDir.Execute(); + m_BehaviorDir = m_defaultBehaviorDir; + QList<QUrl> urls = g_StudioApp.GetDialogs()->SelectAssets( + m_BehaviorDir, Q3DStudio::DocumentEditorFileType::Behavior); + m_ProjectModel->importUrls(urls, row); +} + +void ProjectView::assetImportAction(int row) +{ + QList<QUrl> urls = g_StudioApp.GetDialogs()->SelectAssets( + m_assetImportDir, Q3DStudio::DocumentEditorFileType::Unknown); + m_ProjectModel->importUrls(urls, row); +} + +void ProjectView::assetImportInContext(int row) +{ + // If the context is a default directory, select the correct directory + Q3DStudio::DocumentEditorFileType::Enum assetType = m_ProjectModel->assetTypeForRow(row); + QString *assetDir = &m_assetImportDir; + switch (assetType) { + case Q3DStudio::DocumentEditorFileType::Effect: + assetDir = &m_EffectDir; + break; + case Q3DStudio::DocumentEditorFileType::Font: + assetDir = &m_FontDir; + break; + case Q3DStudio::DocumentEditorFileType::Image: + assetDir = &m_ImageDir; + break; + case Q3DStudio::DocumentEditorFileType::Material: + assetDir = &m_MaterialDir; + break; + case Q3DStudio::DocumentEditorFileType::DAE: + assetDir = &m_ModelDir; + break; + case Q3DStudio::DocumentEditorFileType::Behavior: + assetDir = &m_BehaviorDir; + break; + default: + break; + } + + QList<QUrl> urls; + urls = g_StudioApp.GetDialogs()->SelectAssets(*assetDir, assetType); + m_ProjectModel->importUrls(urls, row, false); } void ProjectView::OnNewPresentation() diff --git a/src/Authoring/Studio/Palettes/Project/ProjectView.h b/src/Authoring/Studio/Palettes/Project/ProjectView.h index 1bba87b4..d743c46b 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectView.h +++ b/src/Authoring/Studio/Palettes/Project/ProjectView.h @@ -54,12 +54,14 @@ public: QAbstractItemModel *projectModel() const; - Q_INVOKABLE void effectAction(); - Q_INVOKABLE void fontAction(); - Q_INVOKABLE void imageAction(); - Q_INVOKABLE void materialAction(); - Q_INVOKABLE void modelAction(); - Q_INVOKABLE void behaviorAction(); + Q_INVOKABLE void effectAction(int row); + Q_INVOKABLE void fontAction(int row); + Q_INVOKABLE void imageAction(int row); + Q_INVOKABLE void materialAction(int row); + Q_INVOKABLE void modelAction(int row); + Q_INVOKABLE void behaviorAction(int row); + Q_INVOKABLE void assetImportAction(int row); + void assetImportInContext(int row); Q_INVOKABLE void startDrag(QQuickItem *item, int row); @@ -93,12 +95,19 @@ private: ProjectFileSystemModel *m_ProjectModel = nullptr; QColor m_BaseColor = QColor::fromRgb(75, 75, 75); - Qt3DSFile m_BehaviorDir{""}; - Qt3DSFile m_EffectDir{""}; - Qt3DSFile m_FontDir{""}; - Qt3DSFile m_ImageDir{""}; - Qt3DSFile m_MaterialDir{""}; - Qt3DSFile m_ModelDir{""}; + QString m_defaultBehaviorDir; + QString m_defaultEffectDir; + QString m_defaultFontDir; + QString m_defaultImageDir; + QString m_defaultMaterialDir; + QString m_defaultModelDir; + QString m_BehaviorDir; + QString m_EffectDir; + QString m_FontDir; + QString m_ImageDir; + QString m_MaterialDir; + QString m_ModelDir; + QString m_assetImportDir; QSize m_preferredSize; }; diff --git a/src/Authoring/Studio/Palettes/Project/ProjectView.qml b/src/Authoring/Studio/Palettes/Project/ProjectView.qml index 2ee79a8d..85c3410c 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectView.qml +++ b/src/Authoring/Studio/Palettes/Project/ProjectView.qml @@ -56,11 +56,18 @@ Rectangle { id: projectTree anchors.fill: parent + clip: true ScrollBar.vertical: ScrollBar {} model: _projectView.projectModel + onCurrentIndexChanged: { + // Try to keep something selected always + if ((currentIndex < 0 || currentIndex >= count) && count > 0) + currentIndex = 0; + } + delegate: Rectangle { id: delegateItem @@ -122,22 +129,18 @@ Rectangle { } DropArea { - id: dropArea - anchors.fill: parent onEntered: { - if (drag.hasUrls && projectTree.model.hasValidUrlsForDropping(drag.urls)) { + if (drag.hasUrls && projectTree.model.hasValidUrlsForDropping(drag.urls)) drag.accept(Qt.CopyAction) - } else { + else drag.accepted = false; - } } onDropped: { - if (drop.hasUrls) { - projectTree.model.dropUrls(drop.urls, index) - } + if (drop.hasUrls) + projectTree.model.importUrls(drop.urls, index, false) } } @@ -177,6 +180,23 @@ Rectangle { } } } + DropArea { + // Leftover listview area. Dropping here is equivalent to dropping to root + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: parent.height - parent.contentHeight + onEntered: { + if (drag.hasUrls && projectTree.model.hasValidUrlsForDropping(drag.urls)) + drag.accept(Qt.CopyAction) + else + drag.accepted = false; + } + onDropped: { + if (drop.hasUrls) + projectTree.model.importUrls(drop.urls, 0, false) + } + } } } @@ -187,44 +207,50 @@ Rectangle { Layout.margins: 4 Layout.rightMargin: 12 + StyledToolButton { + enabledImage: "Asset-import-Normal.png"; + onClicked: _projectView.assetImportAction(projectTree.currentIndex); + toolTipText: qsTr("Import Assets"); + } + Item { Layout.fillWidth: true } StyledToolButton { enabledImage: "Objects-Effect-Normal.png"; - onClicked: _projectView.effectAction() - toolTipText: qsTr("Open Effect Library directory") + onClicked: _projectView.effectAction(projectTree.currentIndex) + toolTipText: qsTr("Open Effect Library") } StyledToolButton { enabledImage: "Objects-Text-Normal.png"; - onClicked: _projectView.fontAction() - toolTipText: qsTr("Open Font Library directory") + onClicked: _projectView.fontAction(projectTree.currentIndex) + toolTipText: qsTr("Open Font Library") } StyledToolButton { enabledImage: "Objects-Image-Normal.png"; - onClicked: _projectView.imageAction() - toolTipText: qsTr("Open Maps Library directory") + onClicked: _projectView.imageAction(projectTree.currentIndex) + toolTipText: qsTr("Open Map Library") } StyledToolButton { enabledImage: "Objects-Material-Normal.png"; - onClicked: _projectView.materialAction() - toolTipText: qsTr("Open Material Library directory") + onClicked: _projectView.materialAction(projectTree.currentIndex) + toolTipText: qsTr("Open Material Library") } StyledToolButton { enabledImage: "Objects-Model-Normal.png"; - onClicked: _projectView.modelAction() - toolTipText: qsTr("Open Models Library directory") + onClicked: _projectView.modelAction(projectTree.currentIndex) + toolTipText: qsTr("Open Model Library") } StyledToolButton { enabledImage: "Objects-Behavior-Normal.png"; - onClicked: _projectView.behaviorAction() - toolTipText: qsTr("Open Behavior Library directory") + onClicked: _projectView.behaviorAction(projectTree.currentIndex) + toolTipText: qsTr("Open Behavior Library") } } } diff --git a/src/Authoring/Studio/Workspace/Dialogs.h b/src/Authoring/Studio/Workspace/Dialogs.h index f5a8af0c..99b6c9cd 100644 --- a/src/Authoring/Studio/Workspace/Dialogs.h +++ b/src/Authoring/Studio/Workspace/Dialogs.h @@ -43,6 +43,7 @@ #include "Qt3DSMessageBox.h" #include "Qt3DSFileTools.h" #include "CColor.h" +#include "DocumentEditorEnumerations.h" #include <QtWidgets/qmessagebox.h> @@ -72,6 +73,16 @@ public: void DisplayRefreshResourceFailed(const Q3DStudio::CString &inResourceName, const Q3DStudio::CString &inDescription); QString ConfirmRefreshModelFile(const QString &inOriginalPath); + QList<QUrl> SelectAssets(QString &outPath, Q3DStudio::DocumentEditorFileType::Enum assetType); + + QString defaultDirForUrl(const QUrl &url); + + static QStringList effectExtensions(); + static QStringList fontExtensions(); + static QStringList mapExtensions(); + static QStringList materialExtensions(); + static QStringList modelExtensions(); + static QStringList behaviorExtensions(); // This is not an appropriate place for these, but better // in an inappropriate place than duplicated @@ -157,7 +168,8 @@ public: const Q3DStudio::CString &inRecommendedVersion); protected: - QString CreateAllowedTypesString(long inFileTypeFilter, bool inForImport); + QString CreateAllowedTypesString(Q3DStudio::DocumentEditorFileType::Enum fileTypeFilter, + QString &outInitialFilter, bool forImport, bool exclusive); static void DisplayGLVersionDialog(const Q3DStudio::CString &inGLVersion, const Q3DStudio::CString &inRecommendedVersion, bool inError); @@ -166,5 +178,7 @@ protected: bool m_ShowGUI; Q3DStudio::CString m_LastSaveFile; ///< Path to the file was previously saved + + QHash<QString, QString> m_defaultDirForSuffixMap; }; #endif // INCLUDED_DIALOGS_H diff --git a/src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp b/src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp index cd148646..284a0873 100644 --- a/src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp +++ b/src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp @@ -64,11 +64,11 @@ CPaletteManager::CPaletteManager(CMainFrame *inMainFrame) m_projectDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea); // Give the preferred size as percentages of the mainframe size - auto projectView = new ProjectView(QSize(inMainFrame->width() * 0.2, - inMainFrame->height() * 0.8), - m_projectDock); - projectView->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - m_projectDock->setWidget(projectView); + m_projectView = new ProjectView(QSize(inMainFrame->width() * 0.2, + inMainFrame->height() * 0.8), + m_projectDock); + m_projectView->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + m_projectDock->setWidget(m_projectView); inMainFrame->addDockWidget(Qt::RightDockWidgetArea, m_projectDock); m_ControlList.insert(std::make_pair(CONTROLTYPE_PROJECT, m_projectDock)); @@ -294,6 +294,11 @@ void CPaletteManager::onTimeChanged(long time) m_timeLineToolbar->onTimeChanged(time); } +ProjectView *CPaletteManager::projectView() const +{ + return m_projectView; +} + void CPaletteManager::EnablePalettes() { m_basicObjectsDock->setEnabled(true); diff --git a/src/Authoring/Studio/_Win/Palettes/PaletteManager.h b/src/Authoring/Studio/_Win/Palettes/PaletteManager.h index 151e58df..37055533 100644 --- a/src/Authoring/Studio/_Win/Palettes/PaletteManager.h +++ b/src/Authoring/Studio/_Win/Palettes/PaletteManager.h @@ -44,6 +44,7 @@ class CMainFrame; class WidgetControl; class TimeLineToolbar; +class ProjectView; QT_FORWARD_DECLARE_CLASS(QDockWidget) @@ -86,6 +87,7 @@ protected: WidgetControl *m_timeLineWidgetControl; TimeLineToolbar *m_timeLineToolbar; + ProjectView *m_projectView = nullptr; public: CPaletteManager(CMainFrame *inMainFrame); @@ -101,6 +103,7 @@ public: bool tabNavigateFocusedWidget(bool tabForward); CTimelineControl *GetTimelineControl() const; void onTimeChanged(long time); + ProjectView *projectView() const; // Commands void EnablePalettes(); diff --git a/src/Authoring/Studio/_Win/Workspace/Dialogs.cpp b/src/Authoring/Studio/_Win/Workspace/Dialogs.cpp index e6e045ae..71fc5a4d 100644 --- a/src/Authoring/Studio/_Win/Workspace/Dialogs.cpp +++ b/src/Authoring/Studio/_Win/Workspace/Dialogs.cpp @@ -58,7 +58,110 @@ #include <iostream> -//============================================================================= +namespace { + +inline Q3DStudio::CString CreateExtensionsList(const char **extList) +{ + Q3DStudio::CString retval; + for (const char **ext = extList; *ext != nullptr; ++ext) { + if (retval.Length()) + retval += " "; + retval += Q3DStudio::CString("*.") + *ext; + } + return retval; +} + +struct SAllowedTypesEntry +{ + Q3DStudio::DocumentEditorFileType::Enum m_FileType; + QString m_ResourceString; // Model Files, Image Files, etc + const char **m_FileExtensions; +}; + +const char *imgExts[] = { + "png", "jpg", "jpeg", "dds", "bmp", "gif", "hdr", nullptr, +}; + +const wchar_t *wideImgExts[] = { + L"png", L"jpg", L"jpeg", L"dds", L"bmp", L"gif", L"hdr", nullptr, +}; + +const char *modelExts[] = { + CDialogs::GetDAEFileExtension(), + #ifdef QT_3DSTUDIO_FBX + CDialogs::GetFbxFileExtension(), + #endif + nullptr, +}; + +const char *meshExts[] = { + CDialogs::GetMeshFileExtension(), nullptr, +}; + +const char *importExts[] = { + CDialogs::GetImportFileExtension(), nullptr, +}; + +const char *behaviorExts[] = { + CDialogs::GetQmlFileExtension(), nullptr, +}; + +const char *fontExts[] = { + "ttf", "otf", nullptr, +}; +const wchar_t *wideFontExts[] = { + L"ttf", L"otf", nullptr, +}; + +const char *effectExts[] = { + "effect", nullptr, +}; + +const wchar_t *wideEffectExts[] = { + L"effect", nullptr, +}; + +const char *materialExts[] = { + "material", nullptr, +}; + +const wchar_t *wideMaterialExts[] = { + L"material", nullptr, +}; + +const char *soundExts[] = { + "wav", nullptr, +}; + +const wchar_t *wideSoundExts[] = { + L"wav", nullptr, +}; + +// List of file types allowed during import +// Note: Despite its name, Q3DStudio::DocumentEditorFileType::DAE type includes +// all supported model types +SAllowedTypesEntry g_AllowedImportTypes[] = { + { Q3DStudio::DocumentEditorFileType::DAE, QObject::tr("Model Files"), modelExts }, + { Q3DStudio::DocumentEditorFileType::Image, QObject::tr("Image Files"), imgExts }, + { Q3DStudio::DocumentEditorFileType::Behavior, QObject::tr("Behavior Scripts"), behaviorExts }, + { Q3DStudio::DocumentEditorFileType::Effect, QObject::tr("Effect Files"), effectExts }, + { Q3DStudio::DocumentEditorFileType::Font, QObject::tr("Font Files"), fontExts }, + { Q3DStudio::DocumentEditorFileType::Material, QObject::tr("Material Files"), materialExts }, +}; +size_t g_NumAllowedImportTypes = sizeof(g_AllowedImportTypes) / sizeof(*g_AllowedImportTypes); + +// List of file types allowed for file references +SAllowedTypesEntry g_AllowedFileReferencesTypes[] = { + { Q3DStudio::DocumentEditorFileType::Image, QObject::tr("Image Files"), imgExts }, + { Q3DStudio::DocumentEditorFileType::Behavior, QObject::tr("Behavior Scripts"), behaviorExts }, + { Q3DStudio::DocumentEditorFileType::Mesh, QObject::tr("Mesh Files"), meshExts }, + { Q3DStudio::DocumentEditorFileType::Import, QObject::tr("Import Files"), importExts }, + { Q3DStudio::DocumentEditorFileType::Effect, QObject::tr("Effect Files"), effectExts }, +}; +size_t g_NumAllowedFileReferencesTypes = + sizeof(g_AllowedFileReferencesTypes) / sizeof(*g_AllowedFileReferencesTypes); +} + /** * Constructor * @param inShowGUI true if dialogs should be displayed or piped to std:cout instead @@ -68,6 +171,25 @@ CDialogs::CDialogs(bool inShowGUI /*= true*/) , m_ShowGUI(inShowGUI) , m_LastSaveFile(Q3DStudio::CString(".")) { + const auto effectExt = effectExtensions(); + const auto fontExt = fontExtensions(); + const auto mapExt = mapExtensions(); + const auto materialExt = materialExtensions(); + const auto modelExt = modelExtensions(); + const auto behaviorExt = behaviorExtensions(); + + for (const auto ext : effectExt) + m_defaultDirForSuffixMap.insert(ext, QStringLiteral("effects")); + for (const auto ext : fontExt) + m_defaultDirForSuffixMap.insert(ext, QStringLiteral("fonts")); + for (const auto ext : mapExt) + m_defaultDirForSuffixMap.insert(ext, QStringLiteral("maps")); + for (const auto ext : materialExt) + m_defaultDirForSuffixMap.insert(ext, QStringLiteral("materials")); + for (const auto ext : modelExt) + m_defaultDirForSuffixMap.insert(ext, QStringLiteral("models")); + for (const auto ext : behaviorExt) + m_defaultDirForSuffixMap.insert(ext, QStringLiteral("scripts")); } //============================================================================= @@ -235,115 +357,15 @@ void CDialogs::DisplayImportFailed(const QUrl &inURL, const QString &inDescripti } } -namespace { - -inline Q3DStudio::CString CreateExtensionsList(const char **extList) -{ - Q3DStudio::CString retval; - for (const char **ext = extList; *ext != nullptr; ++ext) { - if (retval.Length()) - retval += " "; - retval += Q3DStudio::CString("*.") + *ext; - } - return retval; -} - -struct SAllowedTypesEntry -{ - Q3DStudio::DocumentEditorFileType::Enum m_FileType; - QString m_ResourceString; // Model Files, Image Files, etc - const char **m_FileExtensions; -}; - -const char *imgExts[] = { - "png", "jpg", "jpeg", "dds", "bmp", "gif", "hdr", nullptr, -}; - -const wchar_t *wideImgExts[] = { - L"png", L"jpg", L"jpeg", L"dds", L"bmp", L"gif", L"hdr", nullptr, -}; - -const char *modelExts[] = { - CDialogs::GetDAEFileExtension(), - #ifdef QT_3DSTUDIO_FBX - CDialogs::GetFbxFileExtension(), - #endif - // TODO CDialogs::GetImportFileExtension(), - // TODO CDialogs::GetMeshFileExtension(), - nullptr, -}; - -const char *meshExts[] = { - CDialogs::GetMeshFileExtension(), nullptr, -}; - -const char *importExts[] = { - CDialogs::GetImportFileExtension(), nullptr, -}; - -const char *behaviorExts[] = { - CDialogs::GetQmlFileExtension(), nullptr, -}; - -const char *fontExts[] = { - "ttf", "otf", nullptr, -}; -const wchar_t *wideFontExts[] = { - L"ttf", L"otf", nullptr, -}; - -const char *effectExts[] = { - "effect", nullptr, -}; - -const wchar_t *wideEffectExts[] = { - L"effect", nullptr, -}; - -const char *materialExts[] = { - "material", nullptr, -}; - -const wchar_t *wideMaterialExts[] = { - L"material", nullptr, -}; - -const char *soundExts[] = { - "wav", nullptr, -}; - -const wchar_t *wideSoundExts[] = { - L"wav", nullptr, -}; - -// List of file types allowed during import -SAllowedTypesEntry g_AllowedImportTypes[] = { - { Q3DStudio::DocumentEditorFileType::DAE, QObject::tr("Model Files"), modelExts }, - #ifdef QT_3DSTUDIO_FBX - { Q3DStudio::DocumentEditorFileType::FBX, QObject::tr("Model Files"), modelExts }, - #endif -}; -size_t g_NumAllowedImportTypes = sizeof(g_AllowedImportTypes) / sizeof(*g_AllowedImportTypes); - -// List of file types allowed for file references -SAllowedTypesEntry g_AllowedFileReferencesTypes[] = { - { Q3DStudio::DocumentEditorFileType::Image, QObject::tr("Image Files"), imgExts }, - { Q3DStudio::DocumentEditorFileType::Behavior, QObject::tr("Behavior Scripts"), behaviorExts }, - { Q3DStudio::DocumentEditorFileType::Mesh, QObject::tr("Mesh Files"), meshExts }, - { Q3DStudio::DocumentEditorFileType::Import, QObject::tr("Import Files"), importExts }, - { Q3DStudio::DocumentEditorFileType::Effect, QObject::tr("Effect Files"), effectExts }, -}; -size_t g_NumAllowedFileReferencesTypes = - sizeof(g_AllowedFileReferencesTypes) / sizeof(*g_AllowedFileReferencesTypes); -} - QString CDialogs::ConfirmRefreshModelFile(const QString &inFile) { // this produces an extension string which contains all allowed formats specified in // g_AllowedImportTypes // currently DAE and FBX + QString initialFilter; QString theFileFilter = - CreateAllowedTypesString(Q3DStudio::DocumentEditorFileType::DAE, true); + CreateAllowedTypesString(Q3DStudio::DocumentEditorFileType::DAE, initialFilter, + true, true); return QFileDialog::getOpenFileName(qApp->activeWindow(), QObject::tr("Open"), @@ -351,6 +373,43 @@ QString CDialogs::ConfirmRefreshModelFile(const QString &inFile) QFileDialog::DontUseNativeDialog); } +// outPath parameter is changed to last selected path if dialog was accepted +QList<QUrl> CDialogs::SelectAssets(QString &outPath, + Q3DStudio::DocumentEditorFileType::Enum assetType) +{ + QFileDialog fd(qApp->activeWindow()); + fd.setDirectory(outPath); + fd.setFileMode(QFileDialog::ExistingFiles); + fd.setOption(QFileDialog::DontUseNativeDialog, true); + QString initialFilter; + fd.setNameFilter(CreateAllowedTypesString(assetType, initialFilter, true, false)); + fd.selectNameFilter(initialFilter); + fd.setWindowTitle(QObject::tr("Import Assets")); + + QList<QUrl> files; + if (fd.exec()) { + files = fd.selectedUrls(); + // Return the new path + outPath = fd.directory().absolutePath(); + } + + return files; +} + +QString CDialogs::defaultDirForUrl(const QUrl &url) +{ + QString defaultDir; + if (!url.isLocalFile()) + return defaultDir; + + const QFileInfo fi(url.toLocalFile()); + const QString suffix = fi.suffix(); + + defaultDir = m_defaultDirForSuffixMap.value(suffix); + + return defaultDir; +} + //============================================================================== /** * Notify the user that the presentation we tried to load has failed. @@ -666,24 +725,39 @@ bool CDialogs::IsSoundFileExtension(const wchar_t *inExt) //============================================================================== /** - * CreateAllowedTypesString: Creates the string used to determine allowable types for import or - *for filereferences - * @return the string that dynamicly created with the extensions supported. + * CreateAllowedTypesString: Creates the string used to determine allowable types + * for import or for filereferences + * @return the string that dynamically created with the extensions supported. */ -QString CDialogs::CreateAllowedTypesString(long inFileTypeFilter, bool inForImport) +QString CDialogs::CreateAllowedTypesString(Q3DStudio::DocumentEditorFileType::Enum fileTypeFilter, + QString &outInitialFilter, bool forImport, + bool exclusive) { QString theReturnString; - size_t theCount = inForImport ? g_NumAllowedImportTypes : g_NumAllowedFileReferencesTypes; + QString combinedFilter; + size_t theCount = forImport ? g_NumAllowedImportTypes : g_NumAllowedFileReferencesTypes; for (size_t idx = 0; idx < theCount; ++idx) { const SAllowedTypesEntry &entry = - inForImport ? g_AllowedImportTypes[idx] : g_AllowedFileReferencesTypes[idx]; - if (inFileTypeFilter == Q3DStudio::DocumentEditorFileType::Unknown - || inFileTypeFilter == entry.m_FileType) { + forImport ? g_AllowedImportTypes[idx] : g_AllowedFileReferencesTypes[idx]; + if (!exclusive || fileTypeFilter == entry.m_FileType) { QString theTypeString(entry.m_ResourceString); QString theExtensions(CreateExtensionsList(entry.m_FileExtensions).toQString()); - theReturnString += theTypeString + " (" + theExtensions + ");;"; + const QString filterString = theTypeString + " (" + theExtensions + ");;"; + theReturnString += filterString; + if (exclusive) + outInitialFilter = filterString; + else + combinedFilter += theExtensions + " "; } } + if (!combinedFilter.isEmpty()) { + combinedFilter.chop(1); // Remove last separator + theReturnString.prepend(QObject::tr("All Supported Asset types") + + " (" + combinedFilter + ");;"); + outInitialFilter = combinedFilter; + } + theReturnString.chop(2); + outInitialFilter.chop(2); return theReturnString; } @@ -1056,3 +1130,75 @@ void CDialogs::DisplayGLVersionDialog(const Q3DStudio::CString &inGLVersion, if (theGLVersionDlg.GetDontShowAgain()) CStudioPreferences::SetDontShowGLVersionDialog(true); } + +QStringList CDialogs::effectExtensions() +{ + static QStringList exts; + if (exts.isEmpty()) { + for (const char *ext : effectExts) { + if (ext) + exts << QString::fromLatin1(ext); + } + } + return exts; +} + +QStringList CDialogs::fontExtensions() +{ + static QStringList exts; + if (exts.isEmpty()) { + for (const char *ext : fontExts) { + if (ext) + exts << QString::fromLatin1(ext); + } + } + return exts; +} + +QStringList CDialogs::mapExtensions() +{ + static QStringList exts; + if (exts.isEmpty()) { + for (const char *ext : imgExts) { + if (ext) + exts << QString::fromLatin1(ext); + } + } + return exts; +} + +QStringList CDialogs::materialExtensions() +{ + static QStringList exts; + if (exts.isEmpty()) { + for (const char *ext : materialExts) { + if (ext) + exts << QString::fromLatin1(ext); + } + } + return exts; +} + +QStringList CDialogs::modelExtensions() +{ + static QStringList exts; + if (exts.isEmpty()) { + for (const char *ext : modelExts) { + if (ext) + exts << QString::fromLatin1(ext); + } + } + return exts; +} + +QStringList CDialogs::behaviorExtensions() +{ + static QStringList exts; + if (exts.isEmpty()) { + for (const char *ext : behaviorExts) { + if (ext) + exts << QString::fromLatin1(ext); + } + } + return exts; +} diff --git a/src/Authoring/Studio/images.qrc b/src/Authoring/Studio/images.qrc index b7568d8e..3fb74769 100644 --- a/src/Authoring/Studio/images.qrc +++ b/src/Authoring/Studio/images.qrc @@ -253,6 +253,8 @@ <file>images/Objects-DataInput-Normal@2x.png</file> <file>images/Objects-DataInput-Disabled.png</file> <file>images/Objects-DataInput-Disabled@2x.png</file> + <file>images/Asset-import-Normal.png</file> + <file>images/Asset-import-Normal@2x.png</file> </qresource> <qresource prefix="/startup"> <file alias="open_dialog.png">images/open_dialog.png</file> diff --git a/src/Authoring/Studio/images/Asset-import-Normal.png b/src/Authoring/Studio/images/Asset-import-Normal.png Binary files differnew file mode 100644 index 00000000..a5337aec --- /dev/null +++ b/src/Authoring/Studio/images/Asset-import-Normal.png diff --git a/src/Authoring/Studio/images/Asset-import-Normal@2x.png b/src/Authoring/Studio/images/Asset-import-Normal@2x.png Binary files differnew file mode 100644 index 00000000..f161890a --- /dev/null +++ b/src/Authoring/Studio/images/Asset-import-Normal@2x.png |