summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Qt3DStudio/Palettes/Inspector
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/Inspector')
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserDelegate.qml98
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.cpp630
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.h122
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/DataInputChooser.qml212
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooser.qml72
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.cpp50
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.h46
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.cpp137
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.h70
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.cpp351
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.h78
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerFilesChooser.qml48
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerGenericChooser.qml47
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/IInspectableItem.h211
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooser.qml72
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.cpp73
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.h51
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.cpp152
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.h76
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectableBase.h54
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp1973
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.h258
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp930
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.h186
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.qml1289
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.cpp45
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.h52
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialDropDown.qml69
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.cpp152
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.h61
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooser.qml67
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.cpp67
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.h47
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.cpp148
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.h80
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.cpp199
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.h76
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowser.qml196
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.cpp177
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.h108
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.cpp534
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.h147
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.cpp225
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.h64
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.cpp52
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.h57
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.cpp51
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.h57
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.cpp50
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.h58
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.cpp120
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.h65
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooser.qml72
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.cpp138
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.h69
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.cpp113
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.h73
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.ui113
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.cpp242
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.h85
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.cpp104
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.h62
62 files changed, 11381 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserDelegate.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserDelegate.qml
new file mode 100644
index 00000000..71462f48
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserDelegate.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: item
+
+ signal clicked(string filePath)
+ signal doubleClicked(string filePath)
+
+ width: parent.width
+ height: 20
+ color: isCurrentFile ? _selectionColor : "transparent"
+
+ Row {
+ x: depth * 28 - (item.width <= _valueWidth ? 14 : 0)
+ anchors.verticalCenter: item.verticalCenter
+
+ Image {
+ source: _resDir + (expanded ? "arrow_down.png" : "arrow.png")
+ opacity: isExpandable ? 1 : 0
+
+ MouseArea {
+ visible: listView.model && isExpandable
+ anchors.fill: parent
+ onClicked: {
+ if (expanded)
+ listView.model.collapse(index)
+ else
+ listView.model.expand(index)
+ }
+ }
+ }
+
+ Image {
+ source: listView.model ? fileIcon : ""
+ width: 16
+ height: 16
+ }
+
+ StyledLabel {
+ text: listView.model ? fileName : ""
+ color: _textColor
+ leftPadding: 5
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onClicked: {
+ if (isSelectable) {
+ listView.model.setCurrentFile(filePath);
+ item.clicked(filePath);
+ }
+ }
+ onDoubleClicked: {
+ if (isSelectable) {
+ listView.model.setCurrentFile(filePath);
+ item.doubleClicked(filePath);
+ } else if (isExpandable) {
+ if (expanded)
+ listView.model.collapse(index);
+ else
+ listView.model.expand(index);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.cpp
new file mode 100644
index 00000000..3a1a008b
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.cpp
@@ -0,0 +1,630 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtCore/qset.h>
+
+#include "Qt3DSCommonPrecompile.h"
+
+#include "ChooserModelBase.h"
+#include "Core.h"
+#include "Dispatch.h"
+#include "Doc.h"
+#include "StudioUtils.h"
+#include "Qt3DSFileTools.h"
+#include "ImportUtils.h"
+#include "StudioApp.h"
+
+ChooserModelBase::ChooserModelBase(QObject *parent) : QAbstractListModel(parent)
+ , m_model(new QFileSystemModel(this))
+{
+ connect(m_model, &QAbstractItemModel::rowsInserted, this, &ChooserModelBase::modelRowsInserted);
+ connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ChooserModelBase::modelRowsRemoved);
+ connect(m_model, &QAbstractItemModel::layoutChanged, this, &ChooserModelBase::modelLayoutChanged);
+
+ g_StudioApp.GetCore()->GetDispatch()->AddPresentationChangeListener(this);
+
+ rebuild();
+}
+
+ChooserModelBase::~ChooserModelBase()
+{
+ g_StudioApp.GetCore()->GetDispatch()->RemovePresentationChangeListener(this);
+}
+
+QHash<int, QByteArray> ChooserModelBase::roleNames() const
+{
+ auto modelRoleNames = m_model->roleNames();
+ modelRoleNames.insert(IsExpandableRole, "isExpandable");
+ modelRoleNames.insert(DepthRole, "depth");
+ modelRoleNames.insert(ExpandedRole, "expanded");
+ modelRoleNames.insert(IsSelectableRole, "isSelectable");
+ modelRoleNames.insert(IsCurrentFile, "isCurrentFile");
+ return modelRoleNames;
+}
+
+int ChooserModelBase::rowCount(const QModelIndex &) const
+{
+ return getFixedItems().count() + m_items.count();
+}
+
+QVariant ChooserModelBase::data(const QModelIndex &index, int role) const
+{
+ const int row = index.row();
+
+ const auto fixedItems = getFixedItems();
+ const int fixedItemCount = fixedItems.count();
+
+ if (row < fixedItemCount) {
+ const auto &item = fixedItems.at(row);
+
+ switch (role) {
+ case Qt::DecorationRole:
+ if (!item.iconSource.isEmpty()) {
+ return StudioUtils::resourceImageUrl() + item.iconSource;
+ } else {
+ return StudioUtils::resourceImageUrl()
+ + CStudioObjectTypes::GetNormalIconName(item.iconType);
+ }
+
+ case IsExpandableRole:
+ return false;
+
+ case DepthRole:
+ return 0;
+
+ case ExpandedRole:
+ return false;
+
+ case IsSelectableRole:
+ return true;
+
+ case IsCurrentFile:
+ return item.name == m_currentFile;
+
+ default:
+ return item.name;
+ }
+ } else {
+ const auto &item = m_items.at(row - fixedItemCount);
+
+ switch (role) {
+ case Qt::DecorationRole: {
+ QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
+ return StudioUtils::resourceImageUrl() + getIconName(path);
+ }
+
+ case IsExpandableRole: {
+ QFileInfo fileInfo(item.index.data(QFileSystemModel::FilePathRole).toString());
+ return fileInfo.isDir();
+ }
+
+ case DepthRole:
+ return item.depth;
+
+ case ExpandedRole:
+ return item.expanded;
+
+ case IsSelectableRole: {
+ QFileInfo fileInfo(item.index.data(QFileSystemModel::FilePathRole).toString());
+ return fileInfo.isFile();
+ }
+
+ case IsCurrentFile: {
+ QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
+ return path == m_currentFile;
+ }
+
+ case QFileSystemModel::FileNameRole: {
+ QString displayName = specialDisplayName(item);
+ if (displayName.isEmpty())
+ displayName = m_model->data(item.index, QFileSystemModel::FileNameRole).toString();
+ return displayName;
+ }
+
+ default:
+ return m_model->data(item.index, role).toString();
+ }
+ }
+}
+
+void ChooserModelBase::setCurrentFile(const QString &path)
+{
+ const auto fixedItems = getFixedItems();
+ const int fixedItemCount = fixedItems.count();
+ const auto getFixedItemIndex = [fixedItemCount, &fixedItems](const QString &path) -> int {
+ for (int i = 0; i < fixedItemCount; ++i) {
+ const auto &item = fixedItems.at(i);
+ if (item.name == path)
+ return i;
+ }
+ return -1;
+ };
+ int fixedItemIndex = getFixedItemIndex(path);
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const QDir documentDir(doc->GetDocumentDirectory());
+ const QString fullPath = fixedItemIndex == -1 ? QDir::cleanPath(documentDir.filePath(path))
+ : path;
+
+ if (fullPath != m_currentFile) {
+ const auto fileRow = [this, getFixedItemIndex, fixedItemCount](const QString &path) -> int
+ {
+ const int fixedItemIndex = getFixedItemIndex(path);
+ if (fixedItemIndex != -1)
+ return fixedItemIndex;
+
+ const int itemCount = m_items.count();
+
+ for (int i = 0; i < itemCount; ++i) {
+ const auto &item = m_items.at(i);
+
+ if (item.index.data(QFileSystemModel::FilePathRole).toString() == path)
+ return fixedItemCount + i;
+ }
+
+ return -1;
+ };
+
+ int previousRow = fileRow(m_currentFile);
+ int currentRow = fileRow(fullPath);
+
+ m_currentFile = fullPath;
+
+ if (previousRow != -1)
+ Q_EMIT dataChanged(index(previousRow), index(previousRow));
+
+ if (currentRow != -1)
+ Q_EMIT dataChanged(index(currentRow), index(currentRow));
+ }
+
+ // expand parent folder if current file is hidden
+ auto matched = m_model->match(m_rootIndex, QFileSystemModel::FilePathRole, path, 1,
+ Qt::MatchExactly|Qt::MatchRecursive);
+ if (!matched.isEmpty()) {
+ auto modelIndex = matched.first();
+ if (modelIndexRow(modelIndex) == -1)
+ expand(m_model->parent(modelIndex));
+ }
+}
+
+void ChooserModelBase::expand(const QModelIndex &modelIndex)
+{
+ if (modelIndex == m_rootIndex)
+ return;
+
+ int row = modelIndexRow(modelIndex);
+ if (row == -1) {
+ QModelIndex parentIndex = m_model->parent(modelIndex);
+ expand(parentIndex);
+
+ row = modelIndexRow(modelIndex);
+ Q_ASSERT(row != -1);
+ }
+
+ if (!m_items.at(row).expanded)
+ expand(row + getFixedItems().count());
+}
+
+void ChooserModelBase::setRootPath(const QString &path)
+{
+ // Delete the old model. If the new project is in a totally different directory tree, not
+ // doing this will result in unexplicable crashes when trying to parse something that should
+ // not be parsed.
+ disconnect(m_model, &QAbstractItemModel::rowsInserted,
+ this, &ChooserModelBase::modelRowsInserted);
+ disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
+ this, &ChooserModelBase::modelRowsRemoved);
+ disconnect(m_model, &QAbstractItemModel::layoutChanged,
+ this, &ChooserModelBase::modelLayoutChanged);
+ delete m_model;
+ m_model = new QFileSystemModel(this);
+ connect(m_model, &QAbstractItemModel::rowsInserted,
+ this, &ChooserModelBase::modelRowsInserted);
+ connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
+ this, &ChooserModelBase::modelRowsRemoved);
+ connect(m_model, &QAbstractItemModel::layoutChanged,
+ this, &ChooserModelBase::modelLayoutChanged);
+
+ setRootIndex(m_model->setRootPath(path));
+}
+
+void ChooserModelBase::setRootIndex(const QModelIndex &rootIndex)
+{
+ if (rootIndex != m_rootIndex) {
+ clearModelData();
+ m_rootIndex = rootIndex;
+ showModelTopLevelItems();
+ }
+}
+
+void ChooserModelBase::clearModelData()
+{
+ if (!m_items.isEmpty()) {
+ const auto fixedItemCount = getFixedItems().count();
+ beginRemoveRows({}, fixedItemCount, fixedItemCount + m_items.count() - 1);
+ m_items.clear();
+ endRemoveRows();
+ }
+}
+
+void ChooserModelBase::showModelTopLevelItems()
+{
+ int rowCount = m_model->rowCount(m_rootIndex);
+
+ if (rowCount == 0) {
+ if (m_model->hasChildren(m_rootIndex) && m_model->canFetchMore(m_rootIndex))
+ m_model->fetchMore(m_rootIndex);
+ } else {
+ showModelChildItems(m_rootIndex, 0, rowCount - 1);
+
+ for (int i = 0; i < rowCount; ++i) {
+ const auto &childIndex = m_model->index(i, 0, m_rootIndex);
+ if (m_model->hasChildren(childIndex) && m_model->canFetchMore(childIndex))
+ m_model->fetchMore(childIndex);
+ }
+ }
+}
+
+void ChooserModelBase::showModelChildItems(const QModelIndex &parentIndex, int start, int end)
+{
+ QVector<QModelIndex> rowsToInsert;
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (isVisible(childIndex))
+ rowsToInsert.append(childIndex);
+ }
+
+ const int insertCount = rowsToInsert.count();
+
+ if (insertCount != 0) {
+ TreeItem *parent;
+ int depth, startRow;
+
+ if (parentIndex == m_rootIndex) {
+ parent = nullptr;
+ depth = 0;
+ startRow = 0;
+ } else {
+ const int parentRow = modelIndexRow(parentIndex);
+ Q_ASSERT(parentRow != -1 && isVisible(parentIndex));
+ parent = &m_items[parentRow];
+ depth = parent->depth + 1;
+ startRow = parentRow + parent->childCount + 1;
+ }
+
+ const int fixedItemCount = getFixedItems().count();
+ beginInsertRows({}, startRow + fixedItemCount, startRow + fixedItemCount + insertCount - 1);
+
+ for (auto it = rowsToInsert.rbegin(); it != rowsToInsert.rend(); ++it)
+ m_items.insert(startRow, { *it, depth, false, parent, 0 });
+
+ for (; parent != nullptr; parent = parent->parent)
+ parent->childCount += insertCount;
+
+ endInsertRows();
+ }
+}
+
+void ChooserModelBase::expand(int row)
+{
+ const int fixedItemCount = getFixedItems().count();
+ Q_ASSERT(row >= fixedItemCount && row < fixedItemCount + m_items.count());
+
+ auto &item = m_items[row - fixedItemCount];
+ Q_ASSERT(item.expanded == false);
+
+ const auto &modelIndex = item.index;
+
+ const int rowCount = m_model->rowCount(modelIndex);
+ if (rowCount == 0) {
+ if (m_model->hasChildren(modelIndex) && m_model->canFetchMore(modelIndex))
+ m_model->fetchMore(modelIndex);
+ } else {
+ showModelChildItems(modelIndex, 0, rowCount - 1);
+ }
+
+ item.expanded = true;
+ Q_EMIT dataChanged(index(row), index(row));
+}
+
+void ChooserModelBase::collapse(int row)
+{
+ const int fixedItemCount = getFixedItems().count();
+ Q_ASSERT(row >= fixedItemCount && row < fixedItemCount + m_items.count());
+
+ auto &item = m_items[row - fixedItemCount];
+ Q_ASSERT(item.expanded == true);
+
+ const int childCount = item.childCount;
+
+ if (childCount > 0) {
+ beginRemoveRows({}, row + 1, row + childCount);
+
+ auto first = std::begin(m_items) + row - fixedItemCount + 1;
+ m_items.erase(first, first + childCount);
+
+ for (auto parent = &item; parent != nullptr; parent = parent->parent)
+ parent->childCount -= childCount;
+
+ endRemoveRows();
+ }
+
+ item.expanded = false;
+ Q_EMIT dataChanged(index(row), index(row));
+}
+
+void ChooserModelBase::OnNewPresentation()
+{
+ rebuild();
+}
+
+int ChooserModelBase::modelIndexRow(const QModelIndex &modelIndex) const
+{
+ auto it = std::find_if(std::begin(m_items), std::end(m_items),
+ [&modelIndex](const TreeItem &item)
+ {
+ return item.index == modelIndex;
+ });
+
+ return it != std::end(m_items) ? std::distance(std::begin(m_items), it) : -1;
+}
+
+bool ChooserModelBase::isExpanded(const QModelIndex &modelIndex) const
+{
+ if (modelIndex == m_rootIndex) {
+ return true;
+ } else {
+ const int row = modelIndexRow(modelIndex);
+ return row != -1 && m_items.at(row).expanded;
+ }
+}
+
+EStudioObjectType ChooserModelBase::getIconType(const QString &path) const
+{
+ return Q3DStudio::ImportUtils::GetObjectFileTypeForFile(path).m_IconType;
+}
+
+QString ChooserModelBase::specialDisplayName(const ChooserModelBase::TreeItem &item) const
+{
+ Q_UNUSED(item)
+ return {};
+}
+
+QString ChooserModelBase::getIconName(const QString &path) const
+{
+ QString iconName;
+
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile()) {
+ EStudioObjectType type = getIconType(path);
+ if (type != OBJTYPE_UNKNOWN)
+ iconName = CStudioObjectTypes::GetNormalIconName(type);
+ else
+ iconName = QStringLiteral("Objects-Layer-Normal.png");
+ } else {
+ iconName = QStringLiteral("Objects-Folder-Normal.png");
+ }
+
+ return iconName;
+}
+
+bool ChooserModelBase::isVisible(const QModelIndex &modelIndex) const
+{
+ QString path = modelIndex.data(QFileSystemModel::FilePathRole).toString();
+ QFileInfo fileInfo(path);
+
+ if (fileInfo.isFile()) {
+ return isVisible(path);
+ } else {
+ return hasVisibleChildren(modelIndex);
+ }
+}
+
+bool ChooserModelBase::hasVisibleChildren(const QModelIndex &modelIndex) const
+{
+ int rowCount = m_model->rowCount(modelIndex);
+
+ for (int i = 0; i < rowCount; ++i) {
+ const auto &childIndex = m_model->index(i, 0, modelIndex);
+
+ if (m_model->hasChildren(childIndex)) {
+ if (hasVisibleChildren(childIndex))
+ return true;
+ } else {
+ QString path = childIndex.data(QFileSystemModel::FilePathRole).toString();
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile() && isVisible(path))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ChooserModelBase::modelRowsInserted(const QModelIndex &parentIndex, int start, int end)
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ if (isExpanded(parentIndex)) {
+ showModelChildItems(parentIndex, start, end);
+ } else {
+ if (modelIndexRow(parentIndex) == -1) {
+ // parent wasn't inserted in model yet, check if any of the new rows is visible
+
+ bool visible = false;
+
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ QString path = childIndex.data(QFileSystemModel::FilePathRole).toString();
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile() && isVisible(path)) {
+ visible = true;
+ break;
+ }
+ }
+
+ // if any of the new rows is visible, insert parent folder index into model
+
+ if (visible) {
+ QModelIndex index = parentIndex, parent = m_model->parent(parentIndex);
+
+ while (parent != m_rootIndex && modelIndexRow(parent) == -1) {
+ index = parent;
+ parent = m_model->parent(parent);
+ }
+
+ if (isExpanded(parent) && modelIndexRow(index) == -1) {
+ const int row = index.row();
+ showModelChildItems(parent, row, row);
+ }
+ }
+ }
+
+ // if one of the new rows is the current file expand parent folder
+
+ bool containsCurrent = false;
+
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (childIndex.data(QFileSystemModel::FilePathRole).toString() == m_currentFile) {
+ containsCurrent = true;
+ break;
+ }
+ }
+
+ if (containsCurrent)
+ expand(parentIndex);
+ }
+
+ // fetch children so we're notified when files are added or removed
+
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (m_model->hasChildren(childIndex) && m_model->canFetchMore(childIndex))
+ m_model->fetchMore(childIndex);
+ }
+}
+
+void ChooserModelBase::modelRowsRemoved(const QModelIndex &parentIndex, int start, int end)
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ if (isExpanded(parentIndex)) {
+ const auto fixedItems = getFixedItems();
+
+ const auto removeRow = [this, &fixedItems](int row)
+ {
+ const auto &item = m_items.at(row);
+
+ const int fixedItemCount = fixedItems.count();
+ beginRemoveRows({}, row + fixedItemCount, row + fixedItemCount + item.childCount);
+
+ for (auto parent = item.parent; parent != nullptr; parent = parent->parent)
+ parent->childCount -= 1 + item.childCount;
+
+ m_items.erase(std::begin(m_items) + row, std::begin(m_items) + row + item.childCount + 1);
+
+ endRemoveRows();
+ };
+
+ // remove rows
+
+ for (int i = start; i <= end; ++i) {
+ const int row = modelIndexRow(m_model->index(i, 0, parentIndex));
+ if (row != -1)
+ removeRow(row);
+ }
+
+ // also remove folder row if there are no more visible children
+
+ QModelIndex index = parentIndex;
+
+ while (index != m_rootIndex && !hasVisibleChildren(index)) {
+ const int row = modelIndexRow(index);
+ Q_ASSERT(row != -1);
+ removeRow(row);
+ index = m_model->parent(index);
+ }
+ }
+}
+
+void ChooserModelBase::modelLayoutChanged()
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ QSet<QPersistentModelIndex> expandedItems;
+ for (const auto &item : m_items) {
+ if (item.expanded)
+ expandedItems.insert(item.index);
+ }
+
+ const std::function<int(const QModelIndex &, TreeItem *)> insertChildren =
+ [this, &expandedItems, &insertChildren](const QModelIndex &parentIndex, TreeItem *parent)
+ {
+ Q_ASSERT(parentIndex == m_rootIndex || isVisible(parentIndex));
+
+ const int rowCount = m_model->rowCount(parentIndex);
+ const int depth = parent == nullptr ? 0 : parent->depth + 1;
+
+ int childCount = 0;
+
+ for (int i = 0; i < rowCount; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (isVisible(childIndex)) {
+ const bool expanded = expandedItems.contains(childIndex);
+ m_items.append({ childIndex, depth, expanded, parent, 0 });
+ auto &item = m_items.last();
+ if (expanded) {
+ item.childCount = insertChildren(childIndex, &item);
+ childCount += item.childCount;
+ }
+ ++childCount;
+ }
+ }
+
+ return childCount;
+ };
+
+ const int itemCount = m_items.count();
+
+ m_items.clear();
+ m_items.reserve(itemCount);
+
+ insertChildren(m_rootIndex, nullptr);
+ Q_ASSERT(m_items.count() == itemCount);
+
+ const int fixedItemCount = getFixedItems().count();
+ Q_EMIT dataChanged(index(fixedItemCount), index(fixedItemCount + itemCount - 1));
+}
+
+void ChooserModelBase::rebuild()
+{
+ setRootPath(g_StudioApp.GetCore()->getProjectFile().getProjectPath());
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.h
new file mode 100644
index 00000000..01553ca4
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ChooserModelBase.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef CHOOSERMODELBASE_H
+#define CHOOSERMODELBASE_H
+
+#include "DispatchListeners.h"
+#include "StudioObjectTypes.h"
+
+#include <QFileSystemModel>
+#include <QAbstractListModel>
+#include <QList>
+#include <QVector>
+
+QT_FORWARD_DECLARE_CLASS(QFileSystemModel)
+
+class ChooserModelBase : public QAbstractListModel, public CPresentationChangeListener
+{
+ Q_OBJECT
+
+public:
+ explicit ChooserModelBase(QObject *parent = nullptr);
+ ~ChooserModelBase();
+
+ enum {
+ IsExpandableRole = QFileSystemModel::FilePermissions + 1,
+ DepthRole,
+ ExpandedRole,
+ IsSelectableRole,
+ IsCurrentFile
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = {}) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ Q_INVOKABLE void expand(int row);
+ Q_INVOKABLE void collapse(int row);
+
+ Q_INVOKABLE void setCurrentFile(const QString &path);
+ static QString noneString() { return tr("[None]"); }
+
+ // CPresentationChangeListener
+ void OnNewPresentation() override;
+
+Q_SIGNALS:
+ void modelChanged(QAbstractItemModel *model);
+
+protected:
+ EStudioObjectType getIconType(const QString &path) const;
+
+ virtual bool isVisible(const QString &path) const = 0;
+
+ struct FixedItem
+ {
+ EStudioObjectType iconType;
+ QString iconSource;
+ QString name;
+ };
+
+ struct TreeItem {
+ QPersistentModelIndex index;
+ int depth;
+ bool expanded;
+ TreeItem *parent;
+ int childCount;
+ };
+
+ virtual const QVector<FixedItem> getFixedItems() const = 0;
+ virtual QString specialDisplayName(const TreeItem &item) const;
+
+private:
+ void setRootPath(const QString &path);
+ void setRootIndex(const QModelIndex &rootIndex);
+ void clearModelData();
+ void showModelTopLevelItems();
+ void showModelChildItems(const QModelIndex &parentItem, int start, int end);
+ int modelIndexRow(const QModelIndex &modelIndex) const;
+ bool isExpanded(const QModelIndex &modelIndex) const;
+ QString getIconName(const QString &path) const;
+ bool isVisible(const QModelIndex &modelIndex) const;
+ bool hasVisibleChildren(const QModelIndex &modelIndex) const;
+ void expand(const QModelIndex &modelIndex);
+
+ 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 rebuild();
+
+ QFileSystemModel *m_model;
+ QPersistentModelIndex m_rootIndex;
+ QList<TreeItem> m_items;
+ QString m_currentFile;
+};
+
+#endif // CHOOSERMODELBASE_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/DataInputChooser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/DataInputChooser.qml
new file mode 100644
index 00000000..3681a102
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/DataInputChooser.qml
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+
+ border.color: _studioColor3
+
+ StyledLabel {
+ id: title
+ color: _dataInputColor
+ text: qsTr("Select Controlling Data Input")
+ leftPadding: 8
+ height: 20
+ }
+
+ StyledMenuSeparator {
+ id: separator
+ anchors.top: title.bottom
+ leftPadding: 8
+ rightPadding: 8
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.topMargin: 30
+ spacing: 10
+ RowLayout {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ StyledComboBox {
+ id: filterCombo
+ readonly property int numOfFixedChoices: 2
+ Layout.leftMargin: 8
+ Layout.preferredWidth: 150
+
+ // Data type list must match with EDataType enum so we can use enum
+ // index directly without going through string -> int table lookup
+ model: [qsTr("[Compatible types]"), qsTr("[All types]"), qsTr("Boolean"),
+ qsTr("Float"), qsTr("Ranged Number"), qsTr("String"), qsTr("Variant"),
+ qsTr("Vector2"), qsTr("Vector3")]
+
+ onCurrentIndexChanged: _parentView.setTypeFilter(currentIndex - numOfFixedChoices);
+
+ MouseArea {
+ id: filterBoxMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ // pass through mouse click to Combobox
+ onPressed: {
+ mouse.accepted = false;
+ }
+ }
+
+ StyledTooltip {
+ text: qsTr("Filter the list by Data Input type or\n"
+ + "by compatibility with current property")
+ enabled: filterBoxMouseArea.containsMouse && !filterCombo.popup.activeFocus
+ }
+ Connections {
+ target: _parentView
+ // Filter type can be changed also from cpp side
+ onFilterChanged: {
+ filterCombo.currentIndex = _parentView.typeFilter
+ + filterCombo.numOfFixedChoices;
+ }
+ }
+ }
+
+ StyledTextField {
+ Layout.leftMargin: 8
+ Layout.rightMargin: 8
+ Layout.preferredWidth: 200
+ Layout.fillWidth: true
+ id: searchField
+ placeholderText: qsTr("[search]")
+ horizontalAlignment: TextInput.AlignLeft
+
+ property string value
+
+ rightPadding: clearText.width + 2
+
+ onTextChanged: _parentView.setSearchString(text);
+
+ MouseArea {
+ id: searchMouseArea
+ anchors.fill: parent
+ propagateComposedEvents: true
+ hoverEnabled: true
+ onClicked: {
+ searchField.forceActiveFocus();
+ }
+ }
+
+ StyledTooltip {
+ id: searchTt
+ text: qsTr("Search for Data Input")
+ enabled: searchMouseArea.containsMouse && !searchField.focus
+ }
+
+ Image {
+ anchors { verticalCenter: parent.verticalCenter; right: parent.right; }
+ id: clearText
+ fillMode: Image.PreserveAspectFit
+ smooth: true;
+ source: _resDir + "add.png"
+ rotation: 45
+
+ MouseArea {
+ id: clear
+ anchors {
+ horizontalCenter: parent.horizontalCenter;
+ verticalCenter: parent.verticalCenter
+ }
+ height: clearText.height; width: clearText.height
+ onClicked: {
+ searchField.text = ""
+ searchField.forceActiveFocus()
+ }
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: listView
+ Layout.leftMargin: 8
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _dataInputSelectModel
+
+ delegate: Row {
+ height: 20
+ Image {
+ // do not show item icon for fixed items
+ visible: index >= _dataInputSelectModel.fixedItemCount
+ source: index === _parentView.selected
+ ? _dataInputSelectModel.getActiveIconPath()
+ : _dataInputSelectModel.getInactiveIconPath();
+ }
+ StyledLabel {
+ leftPadding: 5
+ text: model.display
+ width: listView.width / 2;
+ color: (index >= _dataInputSelectModel.fixedItemCount)
+ && (index === _parentView.selected)
+ ? _dataInputColor : _textColor;
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onClicked: _parentView.setSelection(index)
+ }
+ }
+ StyledLabel {
+ leftPadding: 5
+ visible: index >= _dataInputSelectModel.fixedItemCount
+ text: "(" + model.datatype + ")"
+ color: (index >= _dataInputSelectModel.fixedItemCount)
+ && (index === _parentView.selected)
+ ? _dataInputColor : _textColor;
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onClicked: _parentView.setSelection(index)
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooser.qml
new file mode 100644
index 00000000..b9ef07ab
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooser.qml
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+ ListView {
+ id: listView
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 4
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _fileChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: {
+ _fileChooserView.fileSelected(_fileChooserView.handle,
+ _fileChooserView.instance, filePath);
+ }
+ onDoubleClicked: {
+ _fileChooserView.fileSelected(_fileChooserView.handle,
+ _fileChooserView.instance, filePath);
+ _fileChooserView.hide();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.cpp
new file mode 100644
index 00000000..46e3b3a6
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "FileChooserModel.h"
+
+FileChooserModel::FileChooserModel(QObject *parent)
+ : ChooserModelBase(parent)
+{
+
+}
+
+FileChooserModel::~FileChooserModel()
+{
+}
+
+bool FileChooserModel::isVisible(const QString &path) const
+{
+ return getIconType(path) == OBJTYPE_GROUP;
+}
+
+const QVector<ChooserModelBase::FixedItem> FileChooserModel::getFixedItems() const
+{
+ static const QVector<FixedItem> items = { { OBJTYPE_GROUP, "", noneString() } };
+ return items;
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.h
new file mode 100644
index 00000000..c019c4a1
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserModel.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FILECHOOSERMODEL_H
+#define FILECHOOSERMODEL_H
+
+#include "ChooserModelBase.h"
+
+class FileChooserModel : public ChooserModelBase
+{
+ Q_OBJECT
+
+public:
+ explicit FileChooserModel(QObject *parent = nullptr);
+ virtual ~FileChooserModel();
+private:
+ bool isVisible(const QString &path) const override;
+ const QVector<FixedItem> getFixedItems() const override;
+};
+
+#endif // IMAGECHOOSERMODEL_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.cpp
new file mode 100644
index 00000000..fc4612c9
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.cpp
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "FileChooserView.h"
+#include "FileChooserModel.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "IDocumentEditor.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "Qt3DSDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "StudioPreferences.h"
+
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtCore/qtimer.h>
+
+FileChooserView::FileChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new FileChooserModel(this))
+{
+ setWindowTitle(tr("Imports"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &FileChooserView::initialize);
+}
+
+void FileChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty(QStringLiteral("_resDir"),
+ StudioUtils::resourceImageUrl());
+ rootContext()->setContextProperty(QStringLiteral("_fileChooserView"), this);
+ rootContext()->setContextProperty(QStringLiteral("_fileChooserModel"), m_model);
+ engine()->addImportPath(StudioUtils::qmlImportPath());
+ setSource(QUrl(QStringLiteral("qrc:/Palettes/Inspector/FileChooser.qml")));
+}
+
+void FileChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+int FileChooserView::handle() const
+{
+ return m_handle;
+}
+
+void FileChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+int FileChooserView::instance() const
+{
+ return m_instance;
+}
+
+void FileChooserView::updateSelection()
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ qt3dsdm::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ QString valueStr = qt3dsdm::get<QString>(value);
+ if (valueStr.isEmpty())
+ valueStr = ChooserModelBase::noneString();
+
+ m_model->setCurrentFile(valueStr);
+}
+
+void FileChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ // Don't lose focus because of progress dialog pops up which happens e.g. when importing mesh
+ // in response to file selection
+ if (g_StudioApp.isOnProgress()) {
+ if (!m_focusOutTimer) {
+ m_focusOutTimer = new QTimer(this);
+ connect(m_focusOutTimer, &QTimer::timeout, [this]() {
+ // Periodically check if progress is done to refocus the chooser view
+ if (!g_StudioApp.isOnProgress()) {
+ m_focusOutTimer->stop();
+ m_focusOutTimer->deleteLater();
+ m_focusOutTimer = nullptr;
+ this->activateWindow();
+ }
+ });
+ m_focusOutTimer->start(250);
+ }
+ } else {
+ QTimer::singleShot(0, this, &FileChooserView::close);
+ }
+}
+
+void FileChooserView::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Escape)
+ QTimer::singleShot(0, this, &FileChooserView::close);
+
+ QQuickWidget::keyPressEvent(event);
+}
+
+void FileChooserView::showEvent(QShowEvent *event)
+{
+ updateSelection();
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.h
new file mode 100644
index 00000000..6b99343e
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/FileChooserView.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FILECHOOSERVIEW_H
+#define FILECHOOSERVIEW_H
+
+#include <QtQuickWidgets/qquickwidget.h>
+#include <QtCore/qtimer.h>
+
+class FileChooserModel;
+
+class FileChooserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int instance READ instance)
+ Q_PROPERTY(int handle READ handle)
+
+public:
+ explicit FileChooserView(QWidget *parent = nullptr);
+
+ void setHandle(int handle);
+ int handle() const;
+
+ void setInstance(int instance);
+ int instance() const;
+
+ void updateSelection();
+
+Q_SIGNALS:
+ void fileSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ int m_handle = -1;
+ int m_instance = -1;
+ FileChooserModel *m_model = nullptr;
+ QTimer *m_focusOutTimer = nullptr;
+};
+
+#endif // IMAGECHOOSERVIEW_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.cpp
new file mode 100644
index 00000000..b10e4c1e
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.cpp
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "GuideInspectable.h"
+#include "InspectableBase.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Doc.h"
+#include "Qt3DSDMGuides.h"
+#include "InspectorGroup.h"
+#include "IDocumentEditor.h"
+#include "Qt3DSDMDataTypes.h"
+#include "IInspectableItem.h"
+#include "Qt3DSDMValue.h"
+
+typedef std::function<qt3dsdm::SValue()> TGetterFunc;
+typedef std::function<void(qt3dsdm::SValue)> TSetterFunc;
+typedef std::function<void()> TCommitFunc;
+typedef std::function<void()> TCancelFunc;
+
+struct SInspectableDataInfo
+{
+ QString m_Name;
+ QString m_FormalName;
+ QString m_Description;
+ TGetterFunc m_Getter;
+ TSetterFunc m_Setter;
+ TCommitFunc m_Commit;
+ TCancelFunc m_Cancel;
+
+ SInspectableDataInfo(const QString &name, const QString &formalName,
+ const QString &description, TGetterFunc getter, TSetterFunc setter,
+ TCommitFunc commit, TCancelFunc inCancel)
+ : m_Name(name)
+ , m_FormalName(formalName)
+ , m_Description(description)
+ , m_Getter(getter)
+ , m_Setter(setter)
+ , m_Commit(commit)
+ , m_Cancel(inCancel)
+ {
+ }
+};
+
+struct SComboAttItem : public IInspectableAttributeItem
+{
+ SInspectableDataInfo m_BaseInspectableInfo;
+ qt3dsdm::TMetaDataStringList m_MetaDataTypes;
+ SComboAttItem(const SInspectableDataInfo &inInfo, const qt3dsdm::TMetaDataStringList &inTypes)
+ : m_BaseInspectableInfo(inInfo)
+ , m_MetaDataTypes(inTypes)
+ {
+ }
+ qt3dsdm::HandlerArgumentType::Value GetInspectableSubType() const override
+ {
+ return qt3dsdm::HandlerArgumentType::Property;
+ }
+ QString GetInspectableName() const override { return m_BaseInspectableInfo.m_Name; }
+ QString GetInspectableFormalName() const override
+ {
+ return m_BaseInspectableInfo.m_FormalName;
+ }
+ QString GetInspectableDescription() const override
+ {
+ return m_BaseInspectableInfo.m_Description;
+ }
+
+ qt3dsdm::SValue GetInspectableData() const override { return m_BaseInspectableInfo.m_Getter(); }
+ void SetInspectableData(const qt3dsdm::SValue &inValue) override
+ {
+ m_BaseInspectableInfo.m_Setter(inValue);
+ m_BaseInspectableInfo.m_Commit();
+ }
+
+ void ChangeInspectableData(const qt3dsdm::SValue &inValue) override
+ {
+ m_BaseInspectableInfo.m_Setter(inValue);
+ }
+ void CancelInspectableData() override { m_BaseInspectableInfo.m_Cancel(); }
+
+ float GetInspectableMin() const override { return 0; }
+ float GetInspectableMax() const override { return 0; }
+ qt3dsdm::TMetaDataStringList GetInspectableList() const override { return m_MetaDataTypes; }
+ qt3dsdm::DataModelDataType::Value GetInspectableType() const override
+ {
+ return qt3dsdm::DataModelDataType::String;
+ }
+ qt3dsdm::AdditionalMetaDataType::Value GetInspectableAdditionalType() const override
+ {
+ return qt3dsdm::AdditionalMetaDataType::StringList;
+ }
+};
+
+struct SFloatIntItem : public IInspectableAttributeItem
+{
+ SInspectableDataInfo m_BaseInspectableInfo;
+ qt3dsdm::DataModelDataType::Value m_DataType;
+ float m_Min;
+ float m_Max;
+ SFloatIntItem(const SInspectableDataInfo &inInfo, qt3dsdm::DataModelDataType::Value inType,
+ float inMin = 0, float inMax = 0)
+ : m_BaseInspectableInfo(inInfo)
+ , m_DataType(inType)
+ , m_Min(inMin)
+ , m_Max(inMax)
+ {
+ }
+ qt3dsdm::HandlerArgumentType::Value GetInspectableSubType() const override
+ {
+ return qt3dsdm::HandlerArgumentType::Property;
+ }
+ QString GetInspectableName() const override { return m_BaseInspectableInfo.m_Name; }
+ QString GetInspectableFormalName() const override
+ {
+ return m_BaseInspectableInfo.m_FormalName;
+ }
+ QString GetInspectableDescription() const override
+ {
+ return m_BaseInspectableInfo.m_Description;
+ }
+
+ qt3dsdm::SValue GetInspectableData() const override { return m_BaseInspectableInfo.m_Getter(); }
+ void SetInspectableData(const qt3dsdm::SValue &inValue) override
+ {
+ m_BaseInspectableInfo.m_Setter(inValue);
+ m_BaseInspectableInfo.m_Commit();
+ }
+
+ void ChangeInspectableData(const qt3dsdm::SValue &inValue) override
+ {
+ m_BaseInspectableInfo.m_Setter(inValue);
+ }
+ void CancelInspectableData() override { m_BaseInspectableInfo.m_Cancel(); }
+
+ float GetInspectableMin() const override { return m_Min; }
+ float GetInspectableMax() const override { return m_Max; }
+ qt3dsdm::TMetaDataStringList GetInspectableList() const override
+ {
+ return qt3dsdm::TMetaDataStringList();
+ }
+ qt3dsdm::DataModelDataType::Value GetInspectableType() const override { return m_DataType; }
+ qt3dsdm::AdditionalMetaDataType::Value GetInspectableAdditionalType() const override
+ {
+ if (m_Max > 0)
+ return qt3dsdm::AdditionalMetaDataType::Range;
+ else
+ return qt3dsdm::AdditionalMetaDataType::None;
+ }
+};
+
+GuideInspectable::GuideInspectable(qt3dsdm::Qt3DSDMGuideHandle inGuide)
+ : m_Guide(inGuide)
+ , m_Editor(*g_StudioApp.GetCore()->GetDoc())
+{
+}
+
+Q3DStudio::IDocumentReader &GuideInspectable::Reader() const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetDocumentReader();
+}
+
+EStudioObjectType GuideInspectable::getObjectType() const
+{
+ return OBJTYPE_GUIDE;
+}
+
+Q3DStudio::CString GuideInspectable::getName()
+{
+ return L"Guide";
+}
+
+long GuideInspectable::getGroupCount() const
+{
+ return 1;
+}
+
+CInspectorGroup *GuideInspectable::getGroup(long)
+{
+ TCommitFunc theCommiter = std::bind(&GuideInspectable::Commit, this);
+ TCancelFunc theCanceler = std::bind(&GuideInspectable::Rollback, this);
+ m_Properties.push_back(
+ std::make_shared<SFloatIntItem>(
+ SInspectableDataInfo(QStringLiteral("Position"), QObject::tr("Position"),
+ QObject::tr("Position of the guide"),
+ std::bind(&GuideInspectable::GetPosition, this),
+ std::bind(&GuideInspectable::SetPosition, this,
+ std::placeholders::_1),
+ theCommiter, theCanceler),
+ qt3dsdm::DataModelDataType::Float));
+ qt3dsdm::TMetaDataStringList theComboItems;
+ theComboItems.push_back(L"Horizontal");
+ theComboItems.push_back(L"Vertical");
+
+ m_Properties.push_back(
+ std::make_shared<SComboAttItem>(
+ SInspectableDataInfo(QStringLiteral("Orientation"), QObject::tr("Orientation"),
+ QObject::tr("Orientation of the guide"),
+ std::bind(&GuideInspectable::GetDirection, this),
+ std::bind(&GuideInspectable::SetDirection, this,
+ std::placeholders::_1),
+ theCommiter, theCanceler),
+ theComboItems));
+
+ m_Properties.push_back(
+ std::make_shared<SFloatIntItem>(
+ SInspectableDataInfo(QStringLiteral("Width"), QObject::tr("Width"),
+ QObject::tr("Width of the guide"),
+ std::bind(&GuideInspectable::GetWidth, this),
+ std::bind(&GuideInspectable::SetWidth, this,
+ std::placeholders::_1),
+ theCommiter, theCanceler),
+ qt3dsdm::DataModelDataType::Long, 1.0f, 50.0f));
+
+ CInspectorGroup *theNewGroup = new CInspectorGroup(QObject::tr("Basic"));
+ return theNewGroup;
+}
+
+bool GuideInspectable::isValid() const
+{
+ return Reader().IsGuideValid(m_Guide);
+}
+
+bool GuideInspectable::isMaster() const
+{
+ return true;
+}
+
+qt3dsdm::Qt3DSDMInstanceHandle GuideInspectable::getInstance() const
+{
+ return 0; // guide has no instance
+}
+
+void GuideInspectable::SetDirection(const qt3dsdm::SValue &inValue)
+{
+ qt3dsdm::TDataStrPtr theData = inValue.getData<qt3dsdm::TDataStrPtr>();
+ qt3dsdm::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ if (theData) {
+ if (qt3dsdm::AreEqual(theData->GetData(), L"Horizontal"))
+ theSetter.m_Direction = qt3dsdm::GuideDirections::Horizontal;
+ else if (qt3dsdm::AreEqual(theData->GetData(), L"Vertical"))
+ theSetter.m_Direction = qt3dsdm::GuideDirections::Vertical;
+ }
+ Editor().UpdateGuide(m_Guide, theSetter);
+ FireRefresh();
+}
+
+qt3dsdm::SValue GuideInspectable::GetDirection()
+{
+ switch (Reader().GetGuideInfo(m_Guide).m_Direction) {
+ case qt3dsdm::GuideDirections::Horizontal:
+ return std::make_shared<qt3dsdm::CDataStr>(L"Horizontal");
+ case qt3dsdm::GuideDirections::Vertical:
+ return std::make_shared<qt3dsdm::CDataStr>(L"Vertical");
+ default:
+ return std::make_shared<qt3dsdm::CDataStr>(L"");
+ }
+}
+
+void GuideInspectable::SetPosition(const qt3dsdm::SValue &inValue)
+{
+ float thePos = inValue.getData<float>();
+ qt3dsdm::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ theSetter.m_Position = thePos;
+ Editor().UpdateGuide(m_Guide, theSetter);
+ FireRefresh();
+}
+
+qt3dsdm::SValue GuideInspectable::GetPosition()
+{
+ qt3dsdm::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ return theSetter.m_Position;
+}
+
+void GuideInspectable::SetWidth(const qt3dsdm::SValue &inValue)
+{
+ auto theData = inValue.getData<qt3ds::QT3DSI32>();
+
+ qt3dsdm::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ theSetter.m_Width = theData;
+ Editor().UpdateGuide(m_Guide, theSetter);
+ FireRefresh();
+
+}
+
+qt3dsdm::SValue GuideInspectable::GetWidth()
+{
+ qt3dsdm::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ return theSetter.m_Width;
+}
+
+Q3DStudio::IDocumentEditor &GuideInspectable::Editor()
+{
+ return m_Editor.EnsureEditor(QObject::tr("Set Property"), __FILE__, __LINE__);
+}
+
+void GuideInspectable::Commit()
+{
+ m_Editor.CommitEditor();
+}
+
+void GuideInspectable::Rollback()
+{
+ m_Editor.RollbackEditor();
+}
+
+void GuideInspectable::FireRefresh()
+{
+ m_Editor.FireImmediateRefresh(qt3dsdm::Qt3DSDMInstanceHandle());
+}
+
+void GuideInspectable::Destroy()
+{
+ m_Editor.EnsureEditor(QObject::tr("Delete Guide"), __FILE__, __LINE__).DeleteGuide(m_Guide);
+ m_Editor.CommitEditor();
+}
+
+bool GuideInspectable::isHorizontal() const
+{
+ return Reader().GetGuideInfo(m_Guide).m_Direction == qt3dsdm::GuideDirections::Horizontal;
+}
+
+const std::vector<std::shared_ptr<IInspectableAttributeItem>> &
+GuideInspectable::properties() const
+{
+ return m_Properties;
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.h
new file mode 100644
index 00000000..7c9c0c77
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/GuideInspectable.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef GUIDEINSPECTABLE_H
+#define GUIDEINSPECTABLE_H
+
+#include "Qt3DSDMHandles.h"
+#include "InspectableBase.h"
+#include "IDocumentEditor.h"
+#include "IInspectableItem.h"
+
+class GuideInspectable : public CInspectableBase
+{
+public:
+ GuideInspectable(qt3dsdm::Qt3DSDMGuideHandle inGuide);
+
+ Q3DStudio::IDocumentReader &Reader() const;
+ // Interface
+ EStudioObjectType getObjectType() const override;
+ Q3DStudio::CString getName() override;
+ long getGroupCount() const override;
+ CInspectorGroup *getGroup(long) override;
+ bool isValid() const override;
+ bool isMaster() const override;
+ qt3dsdm::Qt3DSDMInstanceHandle getInstance() const override;
+
+ // Implementation to get/set properties
+
+ void SetDirection(const qt3dsdm::SValue &inValue);
+ void SetPosition(const qt3dsdm::SValue &inValue);
+ void SetWidth(const qt3dsdm::SValue &inValue);
+
+ qt3dsdm::SValue GetDirection();
+ qt3dsdm::SValue GetPosition();
+ qt3dsdm::SValue GetWidth();
+
+ Q3DStudio::IDocumentEditor &Editor();
+ void Commit();
+ void Rollback();
+ void FireRefresh();
+ void Destroy();
+
+ bool isHorizontal() const;
+
+ const std::vector<std::shared_ptr<IInspectableAttributeItem>> &properties() const;
+
+private:
+ qt3dsdm::Qt3DSDMGuideHandle m_Guide;
+ Q3DStudio::CUpdateableDocumentEditor m_Editor;
+ std::vector<std::shared_ptr<IInspectableAttributeItem>> m_Properties;
+};
+
+#endif
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerFilesChooser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerFilesChooser.qml
new file mode 100644
index 00000000..b1514d03
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerFilesChooser.qml
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.2
+import "../controls"
+
+RowLayout {
+ id: root
+
+ signal showBrowser
+ property string value: ""
+ property alias activeBrowser: browser.activeBrowser
+
+ BrowserCombo {
+ id: browser
+ Layout.preferredWidth: _valueWidth
+ Layout.fillWidth: true
+ value: root.value === "" ? qsTr("Select...") : root.value
+ onShowBrowser: root.showBrowser()
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerGenericChooser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerGenericChooser.qml
new file mode 100644
index 00000000..15857584
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/HandlerGenericChooser.qml
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.2
+import "../controls"
+
+RowLayout {
+ id: root
+
+ signal showBrowser
+ property alias value: browser.value
+ property alias activeBrowser: browser.activeBrowser
+
+ BrowserCombo {
+ id: browser
+ Layout.preferredWidth: _valueWidth
+ Layout.fillWidth: true
+ onShowBrowser: root.showBrowser()
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/IInspectableItem.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/IInspectableItem.h
new file mode 100644
index 00000000..9ed87f73
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/IInspectableItem.h
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __IINSPECTABLEITEM_H__
+#define __IINSPECTABLEITEM_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSDMDataTypes.h"
+#include "Qt3DSDMHandles.h"
+#include "Qt3DSDMActionInfo.h"
+#include "Qt3DSDMMetaData.h"
+#include "Qt3DSString.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CStudioApp;
+class IInspectableItem;
+
+//==============================================================================
+// Abstract Base Classes
+//==============================================================================
+
+enum EInspectableItemTypes {
+ INSPECTABLEITEMTYPE_VANILLA = 1,
+ INSPECTABLEITEMTYPE_PROPERTY,
+ INSPECTABLEITEMTYPE_DEPENDENT,
+ INSPECTABLEITEMTYPE_SLIDE,
+ INSPECTABLEITEMTYPE_OBJECTREFERENCE,
+ INSPECTABLEITEMTYPE_EVENTSOURCE,
+ INSPECTABLEITEMTYPE_ACTION,
+ INSPECTABLEITEMTYPE_CONDITIONS,
+};
+
+//==============================================================================
+/**
+ * @class IInspectableItemChangeListener
+ * @brief Listener class for inspectable item changes.
+ */
+class IInspectableItemChangeListener
+{
+public:
+ virtual void OnInspectablePropertyChanged(IInspectableItem *inProperty) = 0;
+};
+
+class IInspectableObject
+{
+public:
+ virtual qt3dsdm::Qt3DSDMInstanceHandle GetInspectableBaseInstance() = 0;
+ virtual void SetInspectableObject(const qt3dsdm::SObjectRefType &) = 0;
+ virtual qt3dsdm::SObjectRefType GetInspectableObject() = 0;
+};
+
+class IInspectableEvent
+{
+public:
+ virtual qt3dsdm::Qt3DSDMInstanceHandle GetInspectableInstance() = 0;
+ virtual qt3dsdm::Qt3DSDMEventHandle GetInspectableEvent() = 0;
+ virtual void SetInspectableEvent(const qt3dsdm::Qt3DSDMEventHandle &inEventHandle) = 0;
+};
+
+class IInspectableTargetSection : public IInspectableObject
+{
+public:
+ virtual qt3dsdm::Qt3DSDMActionHandle GetInspectableAction() const = 0;
+};
+
+class IInspectableEventSection : public IInspectableObject, public IInspectableEvent
+{
+public:
+ virtual qt3dsdm::Qt3DSDMActionHandle GetInspectableAction() const = 0;
+};
+
+class IInspectableHandlerSection
+{
+public:
+ virtual qt3dsdm::Qt3DSDMActionHandle GetInspectableAction() const = 0;
+ virtual qt3dsdm::Qt3DSDMHandlerHandle GetInspectableHandler() = 0;
+ virtual void SetInspectableHandler(const qt3dsdm::Qt3DSDMHandlerHandle &inHandlerHandle) = 0;
+
+ virtual qt3dsdm::THandlerHandleList GetInspectableHandlerList() = 0;
+ virtual long GetArgumentCount() = 0;
+ virtual IInspectableItem *GetArgument(long inIndex) = 0;
+ virtual Q3DStudio::CString GetInspectableDescription() = 0;
+};
+
+//==============================================================================
+/**
+ * @class IInspectableItem
+ * @brief Abstract base class for inspectable items.
+ */
+class IInspectableItem
+{
+public:
+ virtual ~IInspectableItem() {}
+ virtual EInspectableItemTypes GetInspectableKind() { return INSPECTABLEITEMTYPE_VANILLA; }
+
+ virtual qt3dsdm::HandlerArgumentType::Value
+ GetInspectableSubType() const = 0; // TODO : Make this method name correct
+ virtual QString GetInspectableName() const = 0;
+ virtual QString GetInspectableFormalName() const = 0;
+ virtual QString GetInspectableDescription() const = 0;
+
+ virtual qt3dsdm::SValue GetInspectableData() const = 0;
+ virtual void SetInspectableData(const qt3dsdm::SValue &) = 0;
+
+ // TODO: Remove from here onwards after cleaning up the rest of the UI classes
+ // This is the non-commital version of SetInspectableData, which must be called
+ // after ChangeInspectableData to commit the action.
+ virtual bool GetInspectableReadOnly() const { return false; }
+
+ virtual void ChangeInspectableData(const qt3dsdm::SValue & /*inAttr*/){};
+ virtual void CancelInspectableData(){}
+
+ virtual void AddInspectableChangeListener(IInspectableItemChangeListener * /*inListener*/){};
+ virtual void RemoveInspectableChangeListener(IInspectableItemChangeListener * /*inListener*/){};
+};
+
+//==============================================================================
+/**
+ * Property specialization
+ */
+class IInspectablePropertyItem : public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_PROPERTY; }
+ virtual void GetInspectablePropertyList(qt3dsdm::TPropertyHandleList &outList) = 0;
+ virtual qt3dsdm::Qt3DSDMInstanceHandle GetInspectableInstance() = 0;
+};
+
+//==============================================================================
+/**
+ * Attribute specialization
+ */
+class IInspectableAttributeItem : public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_DEPENDENT; }
+ virtual float GetInspectableMin() const = 0;
+ virtual float GetInspectableMax() const = 0;
+ virtual qt3dsdm::TMetaDataStringList GetInspectableList() const = 0;
+ virtual qt3dsdm::DataModelDataType::Value GetInspectableType() const = 0;
+ virtual qt3dsdm::AdditionalMetaDataType::Value GetInspectableAdditionalType() const = 0;
+};
+
+//==============================================================================
+/**
+ * Slide specialization
+ */
+class IInspectableSlideItem : public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_SLIDE; }
+ virtual void GetSlideNames(std::list<Q3DStudio::CString> &outSlideNames) = 0;
+};
+
+//==============================================================================
+/**
+ * ObjectReference specialiaztion
+ */
+class IInspectableObjectRefItem : public IInspectableObject, public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override
+ {
+ return INSPECTABLEITEMTYPE_OBJECTREFERENCE;
+ }
+};
+
+//==============================================================================
+/**
+ * Event specialization
+ */
+class IInspectableEventItem : public IInspectableEvent, public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_EVENTSOURCE; }
+};
+
+#endif // #ifndef __IINSPECTABLEITEM_H__
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooser.qml
new file mode 100644
index 00000000..6cfab327
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooser.qml
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+ ListView {
+ id: listView
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 4
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _imageChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: {
+ _imageChooserView.imageSelected(_imageChooserView.handle,
+ _imageChooserView.instance, filePath);
+ }
+ onDoubleClicked: {
+ _imageChooserView.imageSelected(_imageChooserView.handle,
+ _imageChooserView.instance, filePath);
+ _imageChooserView.hide();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.cpp
new file mode 100644
index 00000000..dd7ed605
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ImageChooserModel.h"
+#include "StudioApp.h"
+#include "ProjectFile.h"
+#include "Core.h"
+
+ImageChooserModel::ImageChooserModel(bool showQmls, QObject *parent)
+ : ChooserModelBase(parent)
+ , m_showQmls(showQmls)
+{
+ connect(&g_StudioApp.GetCore()->getProjectFile(), &ProjectFile::presentationIdChanged,
+ this, &ImageChooserModel::handlePresentationIdChange);
+}
+
+ImageChooserModel::~ImageChooserModel()
+{
+}
+
+bool ImageChooserModel::isVisible(const QString &path) const
+{
+ return getIconType(path) == OBJTYPE_IMAGE
+ || !g_StudioApp.getPresentationId(path).isEmpty()
+ || (m_showQmls && !g_StudioApp.getQmlId(path).isEmpty());
+}
+
+const QVector<ChooserModelBase::FixedItem> ImageChooserModel::getFixedItems() const
+{
+ static const QVector<FixedItem> items = { { OBJTYPE_IMAGE, "", noneString() } };
+ return items;
+}
+
+QString ImageChooserModel::specialDisplayName(const ChooserModelBase::TreeItem &item) const
+{
+ // Renderable items display the id instead of file name
+ const QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
+ return g_StudioApp.getRenderableId(path);
+}
+
+void ImageChooserModel::handlePresentationIdChange(const QString &path, const QString &id)
+{
+ Q_UNUSED(path)
+ Q_UNUSED(id)
+
+ // Simply update the filename for all rows
+ Q_EMIT dataChanged(index(0), index(rowCount() - 1), {QFileSystemModel::FileNameRole});
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.h
new file mode 100644
index 00000000..ceb34b29
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserModel.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IMAGECHOOSERMODEL_H
+#define IMAGECHOOSERMODEL_H
+
+#include "ChooserModelBase.h"
+
+class ImageChooserModel : public ChooserModelBase
+{
+ Q_OBJECT
+
+public:
+ explicit ImageChooserModel(bool showQmls, QObject *parent = nullptr);
+ virtual ~ImageChooserModel();
+
+private:
+ bool isVisible(const QString &path) const override;
+ const QVector<FixedItem> getFixedItems() const override;
+ QString specialDisplayName(const TreeItem &item) const override;
+ void handlePresentationIdChange(const QString &path, const QString &id);
+
+ bool m_showQmls = false;
+};
+
+#endif // IMAGECHOOSERMODEL_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.cpp
new file mode 100644
index 00000000..e8180f94
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ImageChooserView.h"
+#include "ImageChooserModel.h"
+#include "StudioPreferences.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "IDocumentEditor.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "Qt3DSDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+ImageChooserView::ImageChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new ImageChooserModel(true, this))
+{
+ setWindowTitle(tr("Images"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &ImageChooserView::initialize);
+}
+
+void ImageChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty(QStringLiteral("_resDir"),
+ StudioUtils::resourceImageUrl());
+ rootContext()->setContextProperty(QStringLiteral("_imageChooserView"), this);
+ rootContext()->setContextProperty(QStringLiteral("_imageChooserModel"), m_model);
+ engine()->addImportPath(StudioUtils::qmlImportPath());
+ setSource(QUrl(QStringLiteral("qrc:/Palettes/Inspector/ImageChooser.qml")));
+}
+
+void ImageChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+int ImageChooserView::handle() const
+{
+ return m_handle;
+}
+
+void ImageChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+int ImageChooserView::instance() const
+{
+ return m_instance;
+}
+
+QString ImageChooserView::currentDataModelPath() const
+{
+ QString cleanPath;
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ qt3dsdm::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ const auto guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+
+ const auto imageInstance = doc->GetDocumentReader().GetInstanceForGuid(guid);
+ if (imageInstance.Valid()) {
+ const QString path = doc->GetDocumentReader().GetSourcePath(imageInstance).toQString();
+
+ // If path is renderable id, we need to resolve the actual path
+ const QString renderablePath = g_StudioApp.getRenderableAbsolutePath(path);
+
+ if (renderablePath.isEmpty())
+ cleanPath = path;
+ else
+ cleanPath = renderablePath;
+
+ cleanPath = QDir::cleanPath(QDir(doc->GetDocumentDirectory()).filePath(cleanPath));
+ } else {
+ cleanPath = ChooserModelBase::noneString();
+ }
+
+ return cleanPath;
+}
+
+void ImageChooserView::updateSelection()
+{
+ m_model->setCurrentFile(currentDataModelPath());
+}
+
+bool ImageChooserView::isFocused() const
+{
+ return hasFocus();
+}
+
+void ImageChooserView::focusInEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusInEvent(event);
+ emit focusChanged();
+}
+
+void ImageChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ emit focusChanged();
+ QTimer::singleShot(0, this, &QQuickWidget::close);
+}
+
+void ImageChooserView::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Escape)
+ QTimer::singleShot(0, this, &ImageChooserView::close);
+
+ QQuickWidget::keyPressEvent(event);
+}
+
+void ImageChooserView::showEvent(QShowEvent *event)
+{
+ updateSelection();
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.h
new file mode 100644
index 00000000..bbf8eff5
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ImageChooserView.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IMAGECHOOSERVIEW_H
+#define IMAGECHOOSERVIEW_H
+
+#include <QQuickWidget>
+
+class ImageChooserModel;
+
+class ImageChooserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(bool focused READ isFocused NOTIFY focusChanged)
+ Q_PROPERTY(int instance READ instance)
+ Q_PROPERTY(int handle READ handle)
+
+public:
+ explicit ImageChooserView(QWidget *parent = nullptr);
+
+ void setHandle(int handle);
+ int handle() const;
+
+ void setInstance(int instance);
+ int instance() const;
+ QString currentDataModelPath() const;
+
+ void updateSelection();
+
+Q_SIGNALS:
+ void imageSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+
+Q_SIGNALS:
+ void focusChanged();
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ bool isFocused() const;
+
+ int m_handle = -1;
+ int m_instance = -1;
+ ImageChooserModel *m_model = nullptr;
+};
+
+#endif // IMAGECHOOSERVIEW_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectableBase.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectableBase.h
new file mode 100644
index 00000000..fa21b57e
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectableBase.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef INSPECTABLEBASE_H
+#define INSPECTABLEBASE_H
+
+class CInspectorGroup;
+
+#include "StudioObjectTypes.h"
+#include "Qt3DSString.h"
+#include "Qt3DSDMHandles.h"
+
+ // Parent class of Inspectable types that will appear in the Inspector Palette.
+class CInspectableBase
+{
+public:
+ CInspectableBase() {}
+ virtual ~CInspectableBase() {}
+
+ virtual EStudioObjectType getObjectType() const = 0;
+ virtual Q3DStudio::CString getName() = 0;
+ virtual long getGroupCount() const = 0;
+ virtual CInspectorGroup *getGroup(long inIndex) = 0;
+ virtual bool isValid() const = 0;
+ virtual bool isMaster() const = 0;
+ virtual qt3dsdm::Qt3DSDMInstanceHandle getInstance() const = 0;
+};
+
+#endif // INSPECTABLEBASE_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp
new file mode 100644
index 00000000..53519573
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp
@@ -0,0 +1,1973 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "InspectorControlModel.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Doc.h"
+#include "InspectorGroup.h"
+#include "Qt3DSDMInspectorGroup.h"
+#include "Qt3DSDMInspectorRow.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "Qt3DSDMInspectable.h"
+#include "Qt3DSDMDataCore.h"
+#include "IDocumentEditor.h"
+#include "Qt3DSDMMetaData.h"
+#include "Qt3DSDMSignals.h"
+#include "CmdDataModelDeanimate.h"
+#include "GuideInspectable.h"
+#include "Qt3DSDMDataTypes.h"
+#include "IObjectReferenceHelper.h"
+#include "SlideSystem.h"
+#include "Qt3DSDMMaterialInspectable.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentReader.h"
+#include "IStudioRenderer.h"
+#include "Dialogs.h"
+#include "Dispatch.h"
+#include "VariantsGroupModel.h"
+#include "StudioProjectSettings.h"
+#include "Literals.h"
+
+#include <QtCore/qfileinfo.h>
+
+static QStringList renderableItems()
+{
+ QStringList renderables;
+ renderables.push_back(QObject::tr("No renderable item"));
+ const CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ Q3DStudio::CString docDir = Q3DStudio::CString::fromQString(doc->GetDocumentDirectory());
+
+ for (SubPresentationRecord r : qAsConst(g_StudioApp.m_subpresentations))
+ renderables.push_back(r.m_id);
+
+ // second step, find the renderable plugins.
+ {
+ Q3DStudio::CFilePath pluginDir
+ = Q3DStudio::CFilePath::CombineBaseAndRelative(docDir, "plugins");
+ if (pluginDir.Exists() && pluginDir.IsDirectory()) {
+ std::vector<Q3DStudio::CFilePath> dirFiles;
+ pluginDir.ListFilesAndDirectories(dirFiles);
+ for (size_t idx = 0, end = dirFiles.size(); idx < end; ++idx) {
+ if (dirFiles[idx].IsFile()) {
+ Q3DStudio::CFilePath relPath =
+ Q3DStudio::CFilePath::GetRelativePathFromBase(docDir, dirFiles[idx]);
+ renderables.push_back(relPath.toQString());
+ }
+ }
+ }
+ }
+ std::sort(renderables.begin() + 1, renderables.end());
+ return renderables;
+}
+
+static std::pair<bool, bool> getSlideCharacteristics(qt3dsdm::Qt3DSDMInstanceHandle instance,
+ const qt3dsdm::ISlideCore &slideCore,
+ const qt3dsdm::ISlideSystem &slideSystem)
+{
+ // Get the slide from the instance.
+ qt3dsdm::Qt3DSDMSlideHandle slide = slideCore.GetSlideByInstance(instance);
+ qt3dsdm::Qt3DSDMSlideHandle master = slideSystem.GetMasterSlide(slide);
+ int index = int(slideSystem.GetSlideIndex(slide));
+ int count = int(slideSystem.GetSlideCount(master));
+ bool hasNextSlide = index > 0 && index < count - 1;
+ bool hasPreviousSlide = index > 1;
+ return std::make_pair(hasNextSlide, hasPreviousSlide);
+}
+
+InspectorControlModel::InspectorControlModel(VariantsGroupModel *variantsModel, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_UpdatableEditor(*g_StudioApp.GetCore()->GetDoc())
+ , m_variantsModel(variantsModel)
+{
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+}
+
+void InspectorControlModel::setInspectable(CInspectableBase *inInspectable)
+{
+ // Commit any pending transactions on selection change
+ m_UpdatableEditor.CommitEditor();
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+ m_previouslyCommittedValue = {};
+
+ if (m_inspectableBase != inInspectable) {
+ m_inspectableBase = inInspectable;
+ rebuildTree();
+ }
+}
+
+CInspectableBase *InspectorControlModel::inspectable() const
+{
+ return m_inspectableBase;
+}
+
+qt3dsdm::Qt3DSDMInstanceHandle InspectorControlModel::getReferenceMaterial(
+ CInspectableBase *inspectable) const
+{
+ if (inspectable)
+ return getBridge()->getMaterialReference(inspectable->getInstance());
+
+ return 0;
+}
+
+void InspectorControlModel::notifyPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance,
+ qt3dsdm::Qt3DSDMPropertyHandle inProperty)
+{
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ if (!getBridge()->IsSceneGraphInstance(inInstance))
+ return;
+
+ if (inProperty == getBridge()->getVariantsProperty(inInstance)) {
+ // variants model is updated upon edit but this is needed to handle undoing
+ m_variantsModel->refresh();
+ return;
+ }
+
+ bool changed = false;
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ qt3dsdm::Qt3DSDMInstanceHandle imageInstance;
+ if (property->m_dataType == qt3dsdm::DataModelDataType::Long4
+ && property->m_property.Valid()) {
+ imageInstance = doc->GetDocumentReader().GetImageInstanceForProperty(
+ property->m_instance, property->m_property);
+ }
+ if (property->m_property == inProperty || imageInstance == inInstance) {
+ updatePropertyValue(property);
+ changed = true;
+ }
+ }
+ }
+ if (changed)
+ Q_EMIT dataChanged(index(0), index(rowCount() - 1));
+}
+
+bool InspectorControlModel::hasInstanceProperty(long instance, int handle)
+{
+ for (const auto &group : qAsConst(m_groupElements)) {
+ for (const auto &element : qAsConst(group.controlElements)) {
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property == qt3dsdm::CDataModelHandle(handle)
+ && property->m_instance == qt3dsdm::CDataModelHandle(instance)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool InspectorControlModel::isInsideMaterialContainer() const
+{
+ return isInsideMaterialContainer(m_inspectableBase);
+}
+
+bool InspectorControlModel::isInsideMaterialContainer(CInspectableBase *inspectable) const
+{
+ if (!inspectable || !inspectable->getInstance())
+ return false;
+
+ return getBridge()->isInsideMaterialContainer(inspectable->getInstance());
+}
+
+bool InspectorControlModel::isDefaultMaterial() const
+{
+ if (m_inspectableBase)
+ return getBridge()->isDefaultMaterial(m_inspectableBase->getInstance());
+
+ return false;
+}
+
+bool InspectorControlModel::isAnimatableMaterial() const
+{
+ return isAnimatableMaterial(m_inspectableBase);
+}
+
+bool InspectorControlModel::isAnimatableMaterial(CInspectableBase *inspectable) const
+{
+ if (!inspectable)
+ return false;
+
+ return inspectable->getObjectType() & (OBJTYPE_CUSTOMMATERIAL | OBJTYPE_MATERIAL);
+}
+
+bool InspectorControlModel::isBasicMaterial() const
+{
+ return isBasicMaterial(m_inspectableBase);
+}
+
+bool InspectorControlModel::isBasicMaterial(CInspectableBase *inspectable) const
+{
+ if (!inspectable)
+ return false;
+
+ if (inspectable->getObjectType() == OBJTYPE_REFERENCEDMATERIAL) {
+ const auto instance = inspectable->getInstance();
+ if (!instance.Valid())
+ return false;
+
+ const auto refMaterial = getBridge()->getMaterialReference(instance);
+ if (refMaterial.Valid() && getBridge()->isInsideMaterialContainer(refMaterial))
+ return true;
+ }
+
+ return false;
+}
+
+bool InspectorControlModel::isMaterial() const
+{
+ if (m_inspectableBase)
+ return m_inspectableBase->getObjectType() & OBJTYPE_IS_MATERIAL;
+
+ return false;
+}
+
+void InspectorControlModel::addMaterial()
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ qt3dsdm::Qt3DSDMInstanceHandle instance = m_inspectableBase->getInstance();
+ if (!instance.Valid())
+ return;
+
+ QString path = doc->getSceneEditor()->getMaterialDirectoryPath() + QStringLiteral("/Material");
+ QString extension = QStringLiteral(".materialdef");
+
+ auto absPath = path + extension;
+ int i = 1;
+ while (QFileInfo(absPath).exists()) {
+ i++;
+ absPath = path + QString::number(i) + extension;
+ }
+
+ qt3dsdm::Qt3DSDMInstanceHandle newMaterial;
+ {
+ newMaterial = Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, {})
+ ->getOrCreateMaterial(absPath, false);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+
+ saveIfMaterial(newMaterial);
+
+ Q3DStudio::ScopedDocumentEditor sceneEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+ doc->SelectDataModelObject(newMaterial);
+
+ const auto type = getBridge()->GetObjectType(instance);
+ if (type == OBJTYPE_REFERENCEDMATERIAL) {
+ sceneEditor->setMaterialReferenceByPath(instance, absPath);
+ const auto relPath = doc->GetRelativePathToDoc(absPath);
+ sceneEditor->setMaterialSourcePath(instance, Q3DStudio::CString::fromQString(relPath));
+ sceneEditor->SetName(instance, getBridge()->GetName(newMaterial, true));
+
+ doc->GetStudioSystem()->GetFullSystemSignalSender()->SendInstancePropertyValue(
+ instance, getBridge()->GetNameProperty());
+ }
+}
+
+void InspectorControlModel::duplicateMaterial()
+{
+ qt3dsdm::Qt3DSDMInstanceHandle instance = m_inspectableBase->getInstance();
+ if (!instance.Valid())
+ return;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto type = m_inspectableBase->getObjectType();
+
+ if (type & ~OBJTYPE_IS_MATERIAL)
+ return;
+
+ auto material = instance;
+ if (type == OBJTYPE_REFERENCEDMATERIAL)
+ material = getReferenceMaterial(m_inspectableBase);
+
+ if (material.Valid()) {
+ const auto sceneEditor = doc->getSceneEditor();
+ auto originalMaterialName = sceneEditor->GetName(material).toQString()
+ + QStringLiteral(" Copy");
+ int slashIndex = originalMaterialName.lastIndexOf(QLatin1Char('/'));
+ if (slashIndex != -1)
+ originalMaterialName = originalMaterialName.mid(slashIndex + 1);
+ auto materialName = originalMaterialName;
+ int i = 1;
+ auto absPath = sceneEditor->getMaterialFilePath(materialName);
+ while (QFileInfo(absPath).exists()) {
+ i++;
+ materialName = originalMaterialName + QString::number(i);
+ absPath = sceneEditor->getMaterialFilePath(materialName);
+ }
+
+ qt3dsdm::Qt3DSDMInstanceHandle duplicate;
+ {
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, {}));
+ duplicate = scopedEditor->getOrCreateMaterial(materialName, false);
+ scopedEditor->copyMaterialProperties(material, duplicate);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+
+ saveIfMaterial(duplicate);
+
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+ doc->SelectDataModelObject(duplicate);
+
+ if (type == OBJTYPE_REFERENCEDMATERIAL) {
+ scopedEditor->setMaterialReferenceByPath(instance, absPath);
+ const auto relPath = doc->GetRelativePathToDoc(absPath);
+ scopedEditor->setMaterialSourcePath(instance, Q3DStudio::CString::fromQString(relPath));
+ scopedEditor->SetName(instance, getBridge()->GetName(duplicate, true));
+ doc->GetStudioSystem()->GetFullSystemSignalSender()->SendInstancePropertyValue(
+ instance, getBridge()->GetNameProperty());
+ }
+ }
+}
+
+void InspectorControlModel::updateMaterialValues(const QStringList &values, int elementIndex,
+ bool updatingShaders)
+{
+ // Find if there are any material items and update the values of those
+ int startIndex = 0;
+ bool isReferenced = !isAnimatableMaterial() && updatingShaders;
+ if (isReferenced && m_groupElements.count() > 0)
+ startIndex = m_groupElements.count() - 1; // Update the last group for referenced materials
+ for (int row = startIndex; row < m_groupElements.count(); ++row) {
+ const CInspectorGroup *inspectorGroup = m_inspectableBase->getGroup(row);
+ const auto group = dynamic_cast<const Qt3DSDMInspectorGroup *>(inspectorGroup);
+ const auto materialGroup = dynamic_cast<const Qt3DSDMMaterialInspectorGroup *>(group);
+ if (materialGroup && (materialGroup->isMaterialGroup() || isReferenced)) {
+ if (m_groupElements[row].controlElements.size()) {
+ auto item = m_groupElements[row].controlElements[elementIndex]
+ .value<InspectorControlBase *>();
+ item->m_values = values;
+ Q_EMIT item->valuesChanged();
+ // Changing values resets the selected index, so pretend the value has also changed
+ Q_EMIT item->valueChanged();
+ }
+ }
+ }
+}
+
+void InspectorControlModel::updateShaderValues()
+{
+ int index = 0;
+ if (isAnimatableMaterial() && !isInsideMaterialContainer())
+ index = 1;
+ updateMaterialValues(shaderValues(), index, true);
+}
+
+void InspectorControlModel::updateMatDataValues()
+{
+ int index = 0;
+ if (!isInsideMaterialContainer())
+ index = 1;
+ updateMaterialValues(matDataValues(), index);
+}
+
+void InspectorControlModel::setMaterials(std::vector<Q3DStudio::CFilePath> &materials)
+{
+ m_materials.clear();
+ for (Q3DStudio::CFilePath &path : materials) {
+ const QString absolutePath = g_StudioApp.GetCore()->GetDoc()->GetResolvedPathToDoc(path);
+ const QString name = g_StudioApp.GetCore()->GetDoc()->GetDocumentReader()
+ .GetCustomMaterialName(absolutePath).toQString();
+
+ m_materials.push_back({name, path.toQString()});
+ }
+
+ if (!isDefaultMaterial())
+ updateShaderValues();
+}
+
+void InspectorControlModel::setMatDatas(const std::vector<Q3DStudio::CFilePath> &matDatas)
+{
+ m_matDatas.clear();
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ bool isDocModified = doc->isModified();
+ const auto sceneEditor = doc->getSceneEditor();
+ if (!sceneEditor)
+ return;
+
+ bool newMaterialSelected = false;
+ for (const Q3DStudio::CFilePath &path : matDatas) {
+ bool isNewFile = true;
+ for (auto &oldPath : m_cachedMatDatas) {
+ if (path.toQString() == oldPath.toQString()) {
+ isNewFile = false;
+ break;
+ }
+ }
+
+ const QString relativePath = path.toQString();
+ const Q3DStudio::CFilePath absolutePath
+ = Q3DStudio::CFilePath::CombineBaseAndRelative(doc->GetDocumentDirectory(), path);
+
+ QString name;
+ QMap<QString, QString> values;
+ QMap<QString, QMap<QString, QString>> textureValues;
+ sceneEditor->getMaterialInfo(
+ absolutePath.toQString(), name, values, textureValues);
+
+ m_matDatas.push_back({name, relativePath, values, textureValues});
+
+ bool needRewrite = false;
+ if (values.contains(QStringLiteral("path"))) {
+ const QString oldPath = values[QStringLiteral("path")];
+ needRewrite = oldPath != absolutePath.toQString();
+ if (!QFileInfo(oldPath).exists()) {
+ const auto instance = sceneEditor->getMaterial(oldPath);
+ if (instance.Valid()) {
+ const QString oldName = sceneEditor->GetName(instance).toQString();
+ const QString newName = sceneEditor
+ ->getMaterialNameFromFilePath(relativePath);
+ const QString actualPath = sceneEditor
+ ->getFilePathFromMaterialName(oldName);
+ if (actualPath == oldPath) {
+ doc->queueMaterialRename(oldName, newName);
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Name"))
+ ->setMaterialNameByPath(instance, relativePath);
+ }
+ }
+ }
+ }
+
+ auto material = sceneEditor->getMaterial(relativePath);
+ if (isNewFile && !newMaterialSelected && !material.Valid()) {
+ {
+ material = Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QString())
+ ->getOrCreateMaterial(relativePath, false);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+ }
+
+ if (material.Valid())
+ sceneEditor->setMaterialValues(relativePath, values, textureValues);
+
+ if (isNewFile && !newMaterialSelected) {
+ doc->SelectDataModelObject(material);
+ newMaterialSelected = true;
+ }
+
+ if (needRewrite && material.Valid())
+ sceneEditor->writeMaterialFile(material, name, false, absolutePath.toQString());
+ }
+
+ if (isBasicMaterial())
+ updateMatDataValues();
+
+ sceneEditor->removeDeletedFromMaterialContainer();
+ // Modified flag has to be restored because of the hidden transaction
+ doc->SetModifiedFlag(isDocModified);
+
+ m_cachedMatDatas = matDatas;
+}
+
+QString InspectorControlModel::getBasicMaterialString() const
+{
+ return QObject::tr("Basic Material");
+}
+
+QString InspectorControlModel::getAnimatableMaterialString() const
+{
+ return QObject::tr("Animatable Material");
+}
+
+QString InspectorControlModel::getReferencedMaterialString() const
+{
+ return QObject::tr("Referenced Material");
+}
+
+QString InspectorControlModel::getStandardMaterialString() const
+{
+ return QObject::tr("Standard");
+}
+
+QString InspectorControlModel::getDefaultMaterialString() const
+{
+ return QObject::tr("Default");
+}
+
+bool InspectorControlModel::isGroupCollapsed(int groupIdx) const
+{
+ if (m_inspectableBase) {
+ auto instance = m_inspectableBase->getInstance();
+ if (instance && groupIdx > -1 && groupIdx < m_groupElements.size()
+ && m_collapseMap.contains(instance)) {
+ return m_collapseMap[instance].contains(groupIdx);
+ }
+ }
+
+ return false;
+}
+
+void InspectorControlModel::updateGroupCollapseState(int groupIdx, bool isCollapsed)
+{
+ if (m_inspectableBase) {
+ auto instance = m_inspectableBase->getInstance();
+ if (instance && groupIdx > -1 && groupIdx < m_groupElements.size()) {
+ if (isCollapsed)
+ m_collapseMap[instance][groupIdx] = true;
+ else
+ m_collapseMap[instance].remove(groupIdx);
+ }
+ }
+}
+
+void InspectorControlModel::updateFontValues(InspectorControlBase *element) const
+{
+ // Find if there are any font items and update the values of those
+ QVector<InspectorControlBase *> fontElements;
+ if (element) {
+ fontElements.append(element);
+ } else {
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant &element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_propertyType == qt3dsdm::AdditionalMetaDataType::Font)
+ fontElements.append(property);
+ }
+ }
+ }
+
+ if (fontElements.size()) {
+ std::vector<QString> fontNames;
+ g_StudioApp.GetCore()->GetDoc()->GetProjectFonts(fontNames);
+ QStringList possibleValues;
+ for (const auto &fontName : fontNames)
+ possibleValues.append(fontName);
+ for (auto fontElement : qAsConst(fontElements)) {
+ fontElement->m_values = possibleValues;
+ Q_EMIT fontElement->valuesChanged();
+ // Changing values resets the selected index, so pretend the value has also changed
+ Q_EMIT fontElement->valueChanged();
+ }
+ }
+}
+
+QStringList InspectorControlModel::materialTypeValues() const
+{
+ QStringList values;
+ values.push_back(getBasicMaterialString());
+ values.push_back(getAnimatableMaterialString());
+ values.push_back(getReferencedMaterialString());
+ return values;
+}
+
+QStringList InspectorControlModel::shaderValues() const
+{
+ QStringList values;
+ values.push_back(getStandardMaterialString());
+ for (size_t matIdx = 0, end = m_materials.size(); matIdx < end; ++matIdx)
+ values.push_back(m_materials[matIdx].m_name);
+ return values;
+}
+
+QStringList InspectorControlModel::matDataValues() const
+{
+ QStringList values;
+ QStringList names;
+ const QString defaultMaterialShownName = getDefaultMaterialString();
+ values.push_back(defaultMaterialShownName);
+ names.push_back(defaultMaterialShownName);
+ for (size_t matIdx = 0, end = m_matDatas.size(); matIdx < end; ++matIdx) {
+ QString shownName = m_matDatas[matIdx].m_name;
+ int slashIndex = shownName.lastIndexOf(QLatin1Char('/'));
+ if (slashIndex != -1)
+ shownName = shownName.mid(slashIndex + 1);
+ if (names.contains(shownName))
+ shownName += QLatin1String(" (") + m_matDatas[matIdx].m_relativePath + QLatin1Char(')');
+ else
+ names.push_back(shownName);
+ values.push_back(shownName);
+ }
+ return values;
+}
+
+InspectorControlBase *InspectorControlModel::createMaterialTypeItem(
+ Qt3DSDMInspectable *inspectable, int groupIndex)
+{
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ item->m_title = tr("Material Type");
+ item->m_dataType = qt3dsdm::DataModelDataType::StringRef;
+ item->m_propertyType = qt3dsdm::AdditionalMetaDataType::None;
+ item->m_tooltip = tr("Type of material being used");
+ item->m_animatable = false;
+
+ const QStringList values = materialTypeValues();
+ item->m_values = values;
+
+ QString sourcePath = getBridge()->GetSourcePath(item->m_instance);
+
+ switch (inspectable->getObjectType()) {
+ case OBJTYPE_MATERIAL:
+ case OBJTYPE_CUSTOMMATERIAL:
+ item->m_value = getAnimatableMaterialString();
+ break;
+
+ case OBJTYPE_REFERENCEDMATERIAL:
+ item->m_value = getReferencedMaterialString();
+ if (sourcePath == getBridge()->getDefaultMaterialName())
+ item->m_value = getBasicMaterialString();
+ for (int matIdx = 0, end = int(m_matDatas.size()); matIdx < end; ++matIdx) {
+ if (QString::compare(m_matDatas[matIdx].m_relativePath,
+ sourcePath, Qt::CaseInsensitive) == 0) {
+ item->m_value = getBasicMaterialString();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return item;
+}
+
+InspectorControlBase *InspectorControlModel::createShaderItem(
+ Qt3DSDMInspectable *inspectable, int groupIndex)
+{
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ item->m_title = tr("Shader");
+ item->m_dataType = qt3dsdm::DataModelDataType::StringRef;
+ item->m_propertyType = qt3dsdm::AdditionalMetaDataType::Renderable;
+ item->m_tooltip = tr("Shader being used");
+ item->m_animatable = false;
+
+ const QStringList values = shaderValues();
+ item->m_values = values;
+
+ QString sourcePath = getBridge()->GetSourcePath(item->m_instance);
+
+ item->m_value = values[0];
+ for (int matIdx = 0, end = int(m_materials.size()); matIdx < end; ++matIdx) {
+ if (m_materials[matIdx].m_relativePath == sourcePath)
+ item->m_value = values[matIdx + 1];
+ }
+
+ return item;
+}
+
+InspectorControlBase *InspectorControlModel::createMatDataItem(
+ Qt3DSDMInspectable *inspectable, int groupIndex)
+{
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ item->m_title = tr("Source Material");
+ item->m_dataType = qt3dsdm::DataModelDataType::StringRef;
+ item->m_propertyType = qt3dsdm::AdditionalMetaDataType::ObjectRef;
+ item->m_tooltip = tr("Source material definitions used");
+ item->m_animatable = false;
+
+ const QStringList values = matDataValues();
+ item->m_values = values;
+
+ QString sourcePath = getBridge()->GetSourcePath(item->m_instance);
+
+ item->m_value = getDefaultMaterialString();
+ for (int matIdx = 0, end = int(m_matDatas.size()); matIdx < end; ++matIdx) {
+ if (QString::compare(m_matDatas[matIdx].m_relativePath,
+ sourcePath, Qt::CaseInsensitive) == 0) {
+ item->m_value = values[matIdx + 1]; // + 1 for Default basic material
+ }
+ }
+
+ return item;
+}
+
+InspectorControlBase* InspectorControlModel::createItem(Qt3DSDMInspectable *inspectable,
+ Q3DStudio::Qt3DSDMInspectorRow *row,
+ int groupIndex)
+{
+ return createItem(inspectable, row->GetMetaDataPropertyInfo(), groupIndex);
+}
+
+InspectorControlBase* InspectorControlModel::createItem(Qt3DSDMInspectable *inspectable,
+ const qt3dsdm::SMetaDataPropertyInfo &metaProperty,
+ int groupIndex)
+{
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ if (metaProperty.m_IsHidden)
+ return nullptr;
+
+ Q3DStudio::CString title;
+ title.Assign(metaProperty.m_FormalName.c_str());
+ if (title.IsEmpty())
+ title.Assign(metaProperty.m_Name.c_str());
+
+ // Hide name for basic materials
+ if (title == "Name" && isBasicMaterial())
+ return nullptr;
+
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_property = metaProperty.m_Property;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+ item->m_metaProperty = metaProperty;
+
+ item->m_title = title.toQString();
+
+ const auto propertySystem = studio->GetPropertySystem();
+ item->m_dataType = propertySystem->GetDataType(metaProperty.m_Property);
+ item->m_propertyType = static_cast<qt3dsdm::AdditionalMetaDataType::Value>
+ (propertySystem->GetAdditionalMetaDataType(item->m_instance, metaProperty.m_Property));
+ item->m_tooltip = Q3DStudio::CString(metaProperty.m_Description.c_str()).toQString();
+ // \n is parsed as \\n from the material and effect files. Replace them to fix multi-line
+ // tooltips
+ item->m_tooltip.replace(QLatin1String("\\n"), QLatin1String("\n"));
+
+ item->m_animatable = metaProperty.m_Animatable &&
+ studio->GetAnimationSystem()->IsPropertyAnimatable(item->m_instance,
+ metaProperty.m_Property);
+ // If a property is animatable, it should be controllable in addition to
+ // properties explicitly set as controllable in metadata
+ item->m_controllable = item->m_animatable || metaProperty.m_Controllable;
+
+ // disable IBL Override for reference materials
+ if (item->m_title == QLatin1String("IBL Override")
+ && getBridge()->GetObjectType(item->m_instance) == OBJTYPE_REFERENCEDMATERIAL) {
+ item->m_enabled = false;
+ }
+ auto signalProvider = studio->GetFullSystemSignalProvider();
+ if (item->m_animatable) {
+ item->m_animated = studio->GetAnimationSystem()->IsPropertyAnimated(item->m_instance,
+ metaProperty.m_Property);
+
+ // Update the Animate Toggle on undo/redo
+ item->m_connections.push_back(signalProvider->ConnectAnimationCreated(
+ std::bind(&InspectorControlModel::updateAnimateToggleState,
+ this, item)));
+
+ item->m_connections.push_back(signalProvider->ConnectAnimationDeleted(
+ std::bind(&InspectorControlModel::updateAnimateToggleState,
+ this, item)));
+ }
+
+ if (item->m_controllable) {
+ // Set the name of current controller
+ item->m_controller = currentControllerValue(item->m_instance, item->m_property);
+ // Update UI icon state and tooltip
+ updateControlledToggleState(item);
+ item->m_connections.push_back(signalProvider->ConnectControlledToggled(
+ std::bind(&InspectorControlModel::updateControlledToggleState,
+ this, item)));
+ }
+
+ // synchronize the value itself
+ updatePropertyValue(item);
+ return item;
+}
+
+qt3dsdm::SValue InspectorControlModel::currentPropertyValue(long instance, int handle) const
+{
+ auto propertySystem = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetPropertySystem();
+ qt3dsdm::SValue value;
+ propertySystem->GetInstancePropertyValue(instance, handle, value);
+
+ return value;
+}
+
+QString InspectorControlModel::currentControllerValue(long instance, int handle) const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetCurrentController(instance, handle);
+}
+
+void InspectorControlModel::updateControlledToggleState(InspectorControlBase* inItem) const
+{
+ if (inItem->m_instance) {
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ // toggle if controlledproperty contains the name of this property
+ qt3dsdm::SValue currPropVal = currentPropertyValue(
+ inItem->m_instance, studio->GetPropertySystem()->GetAggregateInstancePropertyByName(
+ inItem->m_instance, qt3dsdm::TCharStr(L"controlledproperty")));
+ Q3DStudio::CString currPropValStr;
+ if (!currPropVal.empty())
+ currPropValStr = qt3dsdm::get<qt3dsdm::TDataStrPtr>(currPropVal)->GetData();
+ // Restore original tool tip from metadata when turning control off
+ if (!currPropValStr.size()) {
+ inItem->m_controlled = false;
+ inItem->m_controller = "";
+ } else {
+ Q3DStudio::CString propName
+ = studio->GetPropertySystem()->GetName(inItem->m_property).c_str();
+ // Search specifically for whitespace followed with registered property name.
+ // This avoids finding datainput with same name as the property, as datainput
+ // name is always prepended with "$"
+ long propNamePos = currPropValStr.find(" " + propName);
+ if ((propNamePos == currPropValStr.ENDOFSTRING)
+ && (propNamePos != 0)) {
+ inItem->m_controlled = false;
+ inItem->m_controller = "";
+ } else {
+ inItem->m_controlled = true;
+ // controller name is prepended with "$" to differentiate from property
+ // with same name. Reverse find specifically for $.
+ long posCtrlr = currPropValStr.substr(0, propNamePos).ReverseFind("$");
+
+ // this is the first controller - property pair in controlledproperty
+ if (posCtrlr < 0)
+ posCtrlr = 0;
+
+ // remove $ from controller name for showing it in UI
+ posCtrlr++;
+ const QString ctrlName = currPropValStr.substr(
+ posCtrlr, propNamePos - posCtrlr).toQString();
+
+ inItem->m_controller = ctrlName;
+ }
+ }
+
+ Q_EMIT inItem->tooltipChanged();
+ // Emit signal always to trigger updating of controller name in UI
+ // also when user switches from one controller to another
+ Q_EMIT inItem->controlledChanged();
+ }
+}
+
+void InspectorControlModel::updateAnimateToggleState(InspectorControlBase* inItem)
+{
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ bool animated = studio->GetAnimationSystem()->IsPropertyAnimated(inItem->m_instance,
+ inItem->m_property);
+ if (animated != inItem->m_animated) {
+ inItem->m_animated = animated;
+ Q_EMIT inItem->animatedChanged();
+ }
+}
+
+bool InspectorControlModel::isTreeRebuildRequired(CInspectableBase* inspectBase)
+{
+ if (inspectBase != m_inspectableBase || !inspectBase)
+ return true;
+
+ long theCount = m_inspectableBase->getGroupCount();
+ auto refMaterial = getReferenceMaterial(inspectBase);
+ if (refMaterial != m_refMaterial)
+ return true;
+ long refMaterialGroupCount = 0;
+ if (refMaterial.Valid())
+ refMaterialGroupCount = 1; // Only the last group of the refMaterial is used
+
+ if (m_groupElements.size() != theCount + refMaterialGroupCount)
+ return true;
+
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ const CInspectorGroup *theInspectorGroup = m_inspectableBase->getGroup(theIndex);
+ if (m_groupElements.at(theIndex).groupTitle != theInspectorGroup->GetName())
+ return true;
+ }
+
+ return false;
+}
+
+bool InspectorControlModel::isGroupRebuildRequired(CInspectableBase *inspectable,
+ int theIndex) const
+{
+ Q_ASSERT(theIndex < m_groupElements.size());
+ const CInspectorGroup *theInspectorGroup = inspectable->getGroup(theIndex);
+ const auto existingGroup = m_groupElements.at(theIndex);
+ if (existingGroup.groupTitle != theInspectorGroup->GetName())
+ return true;
+
+ if (const auto cdmInspectable = dynamic_cast<Qt3DSDMInspectable *>(inspectable)) {
+ int existingIndex = 0;
+ if (const auto group = dynamic_cast<const Qt3DSDMInspectorGroup *>(theInspectorGroup)) {
+ const auto materialGroup = dynamic_cast<const Qt3DSDMMaterialInspectorGroup *>(group);
+ if (materialGroup && materialGroup->isMaterialGroup()) {
+ auto i = existingGroup.controlElements.at(existingIndex++).value<InspectorControlBase*>();
+ if (i->m_instance != cdmInspectable->GetGroupInstance(theIndex))
+ return true;
+ if (!isInsideMaterialContainer())
+ existingIndex++; // Add material type dropdown to existing elements
+ }
+
+ if ((existingGroup.controlElements.size() - existingIndex) != group->GetRows().size())
+ return true;
+
+ for (const auto row : group->GetRows()) {
+ auto i = existingGroup.controlElements.at(existingIndex++).value<InspectorControlBase*>();
+ if (i->m_instance != cdmInspectable->GetGroupInstance(theIndex))
+ return true;
+
+ if (i->m_property != row->GetMetaDataPropertyInfo().m_Property)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+CClientDataModelBridge *InspectorControlModel::getBridge() const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+}
+
+auto InspectorControlModel::computeTree(CInspectableBase *inspectBase)
+ -> QVector<GroupInspectorControl>
+{
+ QVector<GroupInspectorControl> result;
+
+ if (inspectBase) {
+ qt3dsdm::Qt3DSDMInstanceHandle instance = inspectBase->getInstance();
+ bool isMatFromFile = instance.Valid() && getBridge()->isInsideMaterialContainer(instance);
+ long groupCount = inspectBase->getGroupCount();
+ for (long idx = 0; idx < groupCount; ++idx)
+ result.append(computeGroup(inspectBase, idx, isMatFromFile, false));
+
+ if (isDefaultMaterial() && result.size() > 0) {
+ result[result.size() - 1].groupInfo = tr("\nDefault material cannot be edited.\n\n"
+ "Create new or import material, then apply.");
+ }
+
+ //Show original material properties for referenced materials
+ auto refMaterial = getReferenceMaterial(inspectBase);
+ if (refMaterial.Valid()) {
+ auto refMaterialInspectable = g_StudioApp.getInspectableFromInstance(refMaterial);
+ if (refMaterialInspectable) {
+ QString materialSrcPath;
+ if (instance.Valid())
+ materialSrcPath = getBridge()->GetSourcePath(instance);
+
+ if (materialSrcPath != getBridge()->getDefaultMaterialName()
+ && getBridge()->GetSourcePath(refMaterial)
+ != getBridge()->getDefaultMaterialName()) {
+ result.append(computeGroup(refMaterialInspectable,
+ refMaterialInspectable->getGroupCount() - 1,
+ true, true));
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+auto InspectorControlModel::computeGroup(CInspectableBase *inspectable, int theIndex,
+ bool disableAnimation, bool isReference)
+ -> GroupInspectorControl
+{
+ CInspectorGroup *theInspectorGroup = inspectable->getGroup(theIndex);
+ GroupInspectorControl result;
+ result.groupTitle = theInspectorGroup->GetName();
+ result.groupInfo.clear();
+
+ if (isReference)
+ result.groupTitle += tr(" (Reference)");
+
+ if (const auto cdmInspectable = dynamic_cast<Qt3DSDMInspectable *>(inspectable)) {
+ if (const auto group = dynamic_cast<Qt3DSDMInspectorGroup *>(theInspectorGroup)) {
+ const auto materialGroup = dynamic_cast<Qt3DSDMMaterialInspectorGroup *>(group);
+ bool isMatData = isBasicMaterial(cdmInspectable);
+ if (materialGroup && materialGroup->isMaterialGroup()) {
+ InspectorControlBase *item = nullptr;
+
+ if (!isInsideMaterialContainer(cdmInspectable) && !isReference) {
+ item = createMaterialTypeItem(cdmInspectable, theIndex);
+ if (item)
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+
+ if (isAnimatableMaterial(cdmInspectable)) {
+ item = createShaderItem(cdmInspectable, theIndex);
+ if (item)
+ result.controlElements.push_back(QVariant::fromValue(item));
+ } else if (isMatData) {
+ item = createMatDataItem(cdmInspectable, theIndex);
+ if (item)
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+ }
+
+ for (const auto row : group->GetRows()) {
+ InspectorControlBase *item = createItem(cdmInspectable, row, theIndex);
+ if (!item)
+ continue;
+
+ if (disableAnimation)
+ item->m_animatable = false;
+
+ if (!isMatData || item->m_title != getReferencedMaterialString())
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+ }
+ } else if (const auto guideInspectable = dynamic_cast<GuideInspectable *>(inspectable)) {
+ // Guide properties don't come from metadata as they are not actual objects
+ m_guideInspectable = guideInspectable;
+ const auto &properties = m_guideInspectable->properties();
+ for (int i = 0, count = int(properties.size()); i < count; ++i) {
+ auto &prop = properties[i];
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_title = prop->GetInspectableFormalName();
+ item->m_dataType = prop->GetInspectableType();
+ item->m_propertyType = prop->GetInspectableAdditionalType();
+ item->m_tooltip = prop->GetInspectableDescription();
+ item->m_animatable = false;
+ item->m_controllable = false;
+ item->m_property = i + 1; // Zero property is considered invalid, so +1
+ result.controlElements.push_back(QVariant::fromValue(item));
+ updatePropertyValue(item);
+ }
+ }
+
+ return result;
+}
+
+void InspectorControlModel::rebuildTree()
+{
+ beginResetModel();
+ m_guideInspectable = nullptr;
+ QVector<QObject *> deleteVector;
+ for (int i = 0; i < m_groupElements.count(); ++i) {
+ auto group = m_groupElements[i];
+ for (int p = 0; p < group.controlElements.count(); ++p)
+ deleteVector.append(group.controlElements[p].value<QObject *>());
+ }
+ m_groupElements = computeTree(m_inspectableBase);
+ endResetModel();
+
+ // Clean the old objects after reset is done so that qml will not freak out about null pointers
+ for (int i = 0; i < deleteVector.count(); ++i)
+ deleteVector[i]->deleteLater();
+
+ m_refMaterial = getReferenceMaterial(m_inspectableBase);
+}
+
+int InspectorControlModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_groupElements.count();
+}
+
+void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) const
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ const auto propertySystem = studioSystem->GetPropertySystem();
+ qt3dsdm::SValue value;
+ const auto instance = element->m_instance;
+ qt3dsdm::Option<qt3dsdm::SMetaDataPropertyInfo> info;
+ if (m_guideInspectable) {
+ value = m_guideInspectable->properties()
+ [handleToGuidePropIndex(element->m_property)]->GetInspectableData();
+ } else {
+ if (!propertySystem->HandleValid(instance))
+ return;
+ propertySystem->GetInstancePropertyValue(instance, element->m_property, value);
+
+ if (value.getType() == qt3dsdm::DataModelDataType::None)
+ return;
+
+ const auto metaDataProvider = doc->GetStudioSystem()->GetActionMetaData();
+ info = metaDataProvider->GetMetaDataPropertyInfo(
+ metaDataProvider->GetMetaDataProperty(instance, element->m_property));
+ }
+
+ bool skipEmits = false;
+ switch (element->m_dataType) {
+ case qt3dsdm::DataModelDataType::String: {
+ QString stringValue = qt3dsdm::get<QString>(value);
+ if (getBridge()->isInsideMaterialContainer(element->m_instance)) {
+ int index = stringValue.lastIndexOf(QLatin1Char('/'));
+ if (index != -1)
+ stringValue = stringValue.mid(index + 1);
+ }
+
+ element->m_value = stringValue;
+ }
+ Q_FALLTHROUGH(); // fall-through for other String-derived datatypes
+
+ case qt3dsdm::DataModelDataType::StringOrInt:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::StringList) {
+ QStringList stringlist;
+ if (m_guideInspectable) {
+ const auto strings = m_guideInspectable->properties()
+ [handleToGuidePropIndex(element->m_property)]->GetInspectableList();
+ for (auto &str : strings)
+ stringlist.append(QString::fromWCharArray(str.wide_str()));
+ } else {
+ stringlist = qt3dsdm::get<QStringList>(info->m_MetaDataData);
+ }
+ auto slideSystem = studioSystem->GetSlideSystem();
+
+ if (element->m_title == QLatin1String("Play Mode")) {
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(element->m_instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ if (!hasNextSlide && !hasPreviousSlide)
+ stringlist.removeAll("Play Through To...");
+ } else if (element->m_title == QLatin1String("Play Through To")) {
+ // the code duplication is intentional as we may ask for slide characteristics
+ // only if the property refers to slides
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(element->m_instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ if (!hasNextSlide)
+ stringlist.removeAll("Next");
+ if (!hasPreviousSlide)
+ stringlist.removeAll("Previous");
+
+ auto itemCount = stringlist.count();
+ QString listOpt;
+ int selectedSlideHandle = 0;
+ int selectedIndex = -1;
+ qt3dsdm::SStringOrInt stringOrInt = qt3dsdm::get<qt3dsdm::SStringOrInt>(value);
+ if (stringOrInt.GetType() == qt3dsdm::SStringOrIntTypes::String)
+ listOpt = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>
+ (stringOrInt.m_Value)->GetData());
+ else
+ selectedSlideHandle = qt3dsdm::get<long>(stringOrInt.m_Value);
+
+ selectedIndex = stringlist.indexOf(listOpt);
+ // Add the slide names (exclude the master slide)
+ auto slideHandle = slideSystem->GetSlideByInstance(instance);
+ auto masterSlide = slideSystem->GetMasterSlide(slideHandle);
+ long slideCount = long(slideSystem->GetSlideCount(masterSlide));
+ for (long slideIndex = 1; slideIndex < slideCount; ++slideIndex) {
+ auto currentSlide = slideSystem->GetSlideByIndex(masterSlide, slideIndex);
+ auto currentInstance = slideSystem->GetSlideInstance(currentSlide);
+
+ QString slideName = getBridge()->GetName(currentInstance).toQString();
+ //hack to add a separator before the item
+ if (slideIndex == 1 && itemCount > 0)
+ slideName += "|separator";
+ stringlist.append(slideName);
+
+ if (currentSlide.GetHandleValue() == selectedSlideHandle)
+ selectedIndex = slideIndex + itemCount - 1;
+ }
+
+ element->m_value = QString(selectedIndex > 0 ? stringlist[selectedIndex]
+ : stringlist.first()).replace(QLatin1String("|separator"),
+ QString());
+ }
+ element->m_values = stringlist;
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Import) {
+ QStringList stringlist = qt3dsdm::get<QStringList>(info->m_MetaDataData);
+ element->m_values = stringlist;
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Renderable) {
+ element->m_values = renderableItems();
+ if (element->m_value.toString().isEmpty())
+ element->m_value = element->m_values.toStringList().at(0);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::MultiLine) {
+ element->m_value = qt3dsdm::get<QString>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Font) {
+ updateFontValues(element);
+ skipEmits = true; // updateFontValues handles emits in correct order
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Mesh) {
+ QString meshValue = QFileInfo(qt3dsdm::get<QString>(value)).fileName();
+ element->m_value = meshValue.startsWith('#'_L1) ? meshValue.mid(1) : meshValue;
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Texture) {
+ QFileInfo fileInfo(qt3dsdm::get<QString>(value));
+ element->m_value = fileInfo.fileName();
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::PathBuffer) {
+ element->m_value = qt3dsdm::get<QString>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::String) {
+ // Basic string already handled, do not warn about that.
+ // If we hit any other datatypes then give a warning
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << " element->m_propertyType : "
+ << element->m_propertyType;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::StringRef:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ element->m_value = qt3dsdm::get<QString>(value);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Bool:
+ element->m_value = qt3dsdm::get<bool>(value);
+ break;
+
+ case qt3dsdm::DataModelDataType::Long4:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Image) {
+ qt3dsdm::Option<qt3dsdm::SLong4> guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+ qt3dsdm::Qt3DSDMInstanceHandle imageInstance = doc->GetDocumentReader()
+ .GetInstanceForGuid(guid);
+ if (imageInstance.Valid()) {
+ Q3DStudio::CString path = doc->GetDocumentReader().GetSourcePath(imageInstance);
+ Q3DStudio::CFilePath relPath(path);
+ element->m_value = QVariant(relPath.GetFileName().toQString());
+ } else {
+ element->m_value = QVariant(QString());
+ }
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << " " << element->m_title;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Long:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Range) {
+ element->m_value = qt3dsdm::get<int>(value);
+ qt3dsdm::SMetaDataRange ranges;
+ if (m_guideInspectable) {
+ const auto prop = m_guideInspectable->properties()
+ [handleToGuidePropIndex(element->m_property)];
+ ranges.m_min = prop->GetInspectableMin();
+ ranges.m_max = prop->GetInspectableMax();
+ } else {
+ ranges = qt3dsdm::get<qt3dsdm::SMetaDataRange>(info->m_MetaDataData);
+ }
+ const QList<double> rangesValues{ranges.m_min, ranges.m_max, double(ranges.m_decimals)};
+ element->m_values = QVariant::fromValue<QList<double> >(rangesValues);
+ }
+ else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::ShadowMapResolution) {
+ element->m_value = qt3dsdm::get<int>(value);
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float3:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Color) {
+ element->m_value = qt3dsdm::get<QColor>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Rotation) {
+ const QVector3D theFloat3 = qt3dsdm::get<QVector3D>(value);
+ const QList<double> float3Values{theFloat3.x(), theFloat3.y(), theFloat3.z()};
+ element->m_values = QVariant::fromValue<QList<double> >(float3Values);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ const QVector3D theFloat3 = qt3dsdm::get<QVector3D>(value);
+ const QList<double> float3Values{theFloat3.x(), theFloat3.y(), theFloat3.z()};
+ element->m_values = QVariant::fromValue<QList<double> >(float3Values);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float4:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Color) {
+ element->m_value = qt3dsdm::get<QColor>(value);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float2:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ const QVector2D theFloat2 = qt3dsdm::get<QVector2D>(value);
+ const QList<double> float2Values{theFloat2.x(), theFloat2.y()};
+ element->m_values = QVariant::fromValue<QList<double> >(float2Values);
+ } else {
+ qWarning() << "TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << element->m_propertyType;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ element->m_value = qt3dsdm::get<float>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Range) {
+ element->m_value = qt3dsdm::get<float>(value);
+ const qt3dsdm::SMetaDataRange ranges = qt3dsdm::get<qt3dsdm::SMetaDataRange>(info->m_MetaDataData);
+ const QList<double> rangesValues{ranges.m_min, ranges.m_max, double(ranges.m_decimals)};
+ element->m_values = QVariant::fromValue<QList<double> >(rangesValues);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::FontSize) {
+ element->m_value = qt3dsdm::get<float>(value);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::ObjectRef:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::ObjectRef) {
+ IObjectReferenceHelper *objRefHelper = doc->GetDataModelObjectReferenceHelper();
+ if (objRefHelper) {
+ qt3dsdm::Qt3DSDMInstanceHandle refInstance = objRefHelper->Resolve(value, instance);
+ QString refName = objRefHelper->LookupObjectFormalName(refInstance).toQString();
+ if (getBridge()->IsReferencedMaterialInstance(instance) && !refName.isEmpty()) {
+ // get the material's object name (parent)
+ auto parentInstance = getBridge()->GetParentInstance(refInstance);
+ qt3dsdm::SValue vParent;
+ propertySystem->GetInstancePropertyValue(parentInstance,
+ getBridge()->GetObjectDefinitions().m_Named.m_NameProp, vParent);
+ QString parentName = qt3dsdm::get<QString>(vParent);
+ refName.append(QLatin1String(" (") + parentName + QLatin1String(")"));
+ }
+ element->m_value = refName;
+ }
+ }
+ break;
+
+ default:
+ qWarning() << "TODO: InspectorControlModel::updatePropertyValue: I've no idea how to handle this datatype"
+ << element->m_dataType;
+ break;
+ }
+
+ if (!skipEmits) {
+ Q_EMIT element->valueChanged();
+ Q_EMIT element->valuesChanged();
+ }
+
+ // Controlled state must be manually set after undo operations,
+ // as only the "controlledproperty" is restored in undo,
+ // not the controlled flag nor the tooltip
+ if (element->m_controllable)
+ updateControlledToggleState(element);
+}
+
+void InspectorControlModel::refreshRenderables()
+{
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property.Valid()
+ && property->m_propertyType == qt3dsdm::AdditionalMetaDataType::Renderable) {
+ updatePropertyValue(property);
+ }
+ }
+ }
+}
+
+void InspectorControlModel::refreshTree()
+{
+ //check if the structure has changed
+ if (isTreeRebuildRequired(m_inspectableBase)) {
+ rebuildTree();
+ } else {
+ // group structure is intact, let's walk to see which rows changed
+ QVector<QObject *> deleteVector;
+ long theCount = m_inspectableBase->getGroupCount();
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ if (isGroupRebuildRequired(m_inspectableBase, theIndex)) {
+ auto group = m_groupElements[theIndex];
+ for (int p = 0; p < group.controlElements.count(); ++p)
+ deleteVector.append(group.controlElements[p].value<QObject *>());
+ m_groupElements[theIndex] = computeGroup(m_inspectableBase, theIndex);
+ Q_EMIT dataChanged(index(theIndex), index(theIndex));
+ }
+ }
+ }
+}
+
+void InspectorControlModel::refresh()
+{
+ refreshTree();
+ // update values
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property.Valid()) {
+ updatePropertyValue(property);
+ updateControlledToggleState(property);
+ }
+ }
+ }
+ Q_EMIT dataChanged(index(0), index(rowCount() - 1));
+}
+
+void InspectorControlModel::saveIfMaterial(qt3dsdm::Qt3DSDMInstanceHandle instance)
+{
+ if (!instance.Valid())
+ return;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto sceneEditor = doc->getSceneEditor();
+
+ const auto studio = doc->GetStudioSystem();
+ EStudioObjectType type = getBridge()->GetObjectType(instance);
+
+ auto material = instance;
+ if (type == EStudioObjectType::OBJTYPE_IMAGE)
+ material = sceneEditor->GetParent(instance);
+
+ if (!material.Valid())
+ return;
+
+ const auto refMaterial = getBridge()->getMaterialReference(material);
+ if (refMaterial.Valid())
+ material = refMaterial;
+
+ if (!getBridge()->isInsideMaterialContainer(material))
+ return;
+
+ type = getBridge()->GetObjectType(material);
+
+ if (type == EStudioObjectType::OBJTYPE_MATERIAL
+ || type == EStudioObjectType::OBJTYPE_CUSTOMMATERIAL) {
+ qt3dsdm::SValue value;
+ studio->GetPropertySystem()->GetInstancePropertyValue(
+ material, getBridge()->GetObjectDefinitions().m_Named.m_NameProp, value);
+ qt3dsdm::TDataStrPtr namePtr(qt3dsdm::get<qt3dsdm::TDataStrPtr>(value));
+ QString materialName = QString::fromWCharArray(namePtr->GetData(),
+ int(namePtr->GetLength()));
+ QString sourcePath;
+ for (int i = 0; i < m_matDatas.size(); ++i) {
+ if (QString::compare(m_matDatas[i].m_name, materialName, Qt::CaseInsensitive) == 0) {
+ sourcePath = doc->GetDocumentDirectory() + QLatin1Char('/')
+ + m_matDatas[i].m_relativePath;
+ }
+ }
+
+ sceneEditor->writeMaterialFile(material, materialName, sourcePath.isEmpty(), sourcePath);
+ }
+}
+
+void InspectorControlModel::setMaterialTypeValue(long instance, int handle, const QVariant &value)
+{
+ Q_UNUSED(handle)
+
+ const QString typeValue = value.toString();
+ QString v;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto sceneEditor = doc->getSceneEditor();
+ const Q3DStudio::CString oldType = sceneEditor->GetObjectTypeName(instance);
+ qt3dsdm::Qt3DSDMInstanceHandle refMaterial;
+ if (oldType == "ReferencedMaterial")
+ refMaterial = getBridge()->getMaterialReference(instance);
+
+ bool changeMaterialFile = false;
+ bool canCopyProperties = false;
+ if (typeValue == getAnimatableMaterialString()) {
+ v = QStringLiteral("Standard Material");
+ if (refMaterial.Valid()) {
+ const auto refSourcePath = getBridge()->GetSourcePath(refMaterial);
+ for (auto &material : m_materials) {
+ if (refSourcePath == material.m_relativePath) {
+ v = material.m_relativePath;
+ break;
+ }
+ }
+ }
+ canCopyProperties = true;
+ } else if (typeValue == getBasicMaterialString()) {
+ v = QStringLiteral("Referenced Material");
+ changeMaterialFile = true;
+ } else if (typeValue == getReferencedMaterialString()) {
+ v = QStringLiteral("Referenced Material");
+ }
+
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+
+ scopedEditor->SetMaterialType(instance, v);
+
+ if (refMaterial.Valid() && canCopyProperties) {
+ const Q3DStudio::CString newType = sceneEditor->GetObjectTypeName(instance);
+ const Q3DStudio::CString refType = sceneEditor->GetObjectTypeName(refMaterial);
+ if (refType == newType)
+ scopedEditor->copyMaterialProperties(refMaterial, instance);
+
+ if (getBridge()->isInsideMaterialContainer(refMaterial)) {
+ const auto name = scopedEditor->GetName(instance);
+ if (!name.toQString().endsWith(QLatin1String("_animatable")))
+ scopedEditor->SetName(instance, name + "_animatable");
+ }
+ }
+
+ if (changeMaterialFile) {
+ scopedEditor->setMaterialProperties(instance, Q3DStudio::CString::fromQString(
+ getBridge()->getDefaultMaterialName()), {}, {});
+
+ // Select the original instance again since potentially creating a material selects the
+ // created one
+ doc->SelectDataModelObject(instance);
+
+ rebuildTree(); // Hack to mimic value changing behavior of the type selector
+ }
+
+ saveIfMaterial(instance);
+}
+
+void InspectorControlModel::setShaderValue(long instance, int handle, const QVariant &value)
+{
+ Q_UNUSED(handle)
+
+ const QString typeValue = value.toString();
+ QString v = QStringLiteral("Standard Material");
+ for (size_t matIdx = 0, end = m_materials.size(); matIdx < end; ++matIdx) {
+ if (m_materials[matIdx].m_name == typeValue)
+ v = m_materials[matIdx].m_relativePath;
+ }
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QObject::tr("Set Material Type"))
+ ->SetMaterialType(instance, v);
+
+ const auto dispatch = g_StudioApp.GetCore()->GetDispatch();
+ QVector<qt3dsdm::Qt3DSDMInstanceHandle> refMats;
+ doc->getSceneReferencedMaterials(doc->GetSceneInstance(), refMats);
+ for (auto &refMat : qAsConst(refMats)) {
+ const auto origMat = getBridge()->getMaterialReference(refMat);
+ if (origMat.Valid() && long(origMat) == instance)
+ dispatch->FireImmediateRefreshInstance(refMat);
+ }
+
+ saveIfMaterial(instance);
+}
+
+void InspectorControlModel::setMatDataValue(long instance, int handle, const QVariant &value)
+{
+ Q_UNUSED(handle)
+
+ const QString typeValue = value.toString();
+ QString v;
+ QString name;
+ Q3DStudio::CString srcPath;
+ QMap<QString, QString> values;
+ QMap<QString, QMap<QString, QString>> textureValues;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+
+ bool changeMaterialFile = false;
+ if (typeValue == getDefaultMaterialString()) {
+ v = QStringLiteral("Referenced Material");
+ name = getBridge()->getDefaultMaterialName();
+ srcPath = Q3DStudio::CString::fromQString(name);
+ changeMaterialFile = true;
+ } else {
+ const auto sceneEditor = doc->getSceneEditor();
+ for (size_t matIdx = 0, end = m_matDatas.size(); matIdx < end; ++matIdx) {
+ QString shownName = m_matDatas[matIdx].m_name;
+ int slashIndex = shownName.lastIndexOf(QLatin1Char('/'));
+ if (slashIndex != -1)
+ shownName = shownName.mid(slashIndex + 1);
+ if (QString::compare(shownName + QLatin1String(" (")
+ + m_matDatas[matIdx].m_relativePath + QLatin1Char(')'),
+ typeValue, Qt::CaseInsensitive) == 0
+ || QString::compare(shownName, typeValue, Qt::CaseInsensitive) == 0) {
+ v = QStringLiteral("Referenced Material");
+ changeMaterialFile = true;
+ name = m_matDatas[matIdx].m_name;
+ srcPath = Q3DStudio::CString::fromQString(m_matDatas[matIdx].m_relativePath);
+ const auto material = sceneEditor->getMaterial(srcPath.toQString());
+ if (material.Valid()) {
+ // Get the correct case source path
+ const auto absPath = sceneEditor->getFilePathFromMaterialName(
+ sceneEditor->GetName(material).toQString());
+ const auto relPath = QDir(doc->GetDocumentDirectory())
+ .relativeFilePath(absPath);
+ srcPath = Q3DStudio::CString::fromQString(relPath);
+ }
+ values = m_matDatas[matIdx].m_values;
+ textureValues = m_matDatas[matIdx].m_textureValues;
+ break;
+ }
+ }
+ }
+
+ if (changeMaterialFile) {
+ {
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QString())
+ ->setMaterialValues(srcPath.toQString(), values, textureValues);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+ }
+
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+ scopedEditor->SetMaterialType(instance, v);
+
+ if (changeMaterialFile) {
+ scopedEditor->setMaterialSourcePath(instance, srcPath);
+ scopedEditor->setMaterialReferenceByPath(instance, srcPath.toQString());
+
+ // Select original instance again since potentially
+ // creating a material selects the created one
+ doc->SelectDataModelObject(instance);
+
+ rebuildTree(); // Hack to mimic value changing behavior of the type selector
+ }
+
+ saveIfMaterial(instance);
+}
+
+void InspectorControlModel::setRenderableValue(long instance, int handle, const QVariant &value)
+{
+ qt3dsdm::SValue oldValue = currentPropertyValue(instance, handle);
+
+ QString v = value.toString();
+ if (v == QObject::tr("No renderable item"))
+ v = QString();
+
+ if (v == qt3dsdm::get<QString>(oldValue))
+ return;
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValueAsRenderable(instance, handle,
+ Q3DStudio::CString::fromQString(v));
+}
+
+void InspectorControlModel::setPropertyValue(long instance, int handle, const QVariant &value,
+ bool commit)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto studio = doc->GetStudioSystem();
+ // Name property needs special handling
+ if (instance && handle == getBridge()->GetNameProperty()) {
+ // Ignore preview of name property change
+ if (!commit)
+ return;
+
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+ m_previouslyCommittedValue = {};
+
+ Q3DStudio::CString currentName = getBridge()->GetName(instance, true);
+ Q3DStudio::CString newName = Q3DStudio::CString::fromQString(value.toString());
+ if (!newName.IsEmpty()) {
+ if (getBridge()->isInsideMaterialContainer(instance)
+ && ((newName.Find('/') != Q3DStudio::CString::ENDOFSTRING
+ || newName.Find('#') != Q3DStudio::CString::ENDOFSTRING
+ || newName.Find(':') != Q3DStudio::CString::ENDOFSTRING)
+ || m_suspendMaterialRename)) {
+ return;
+ }
+ qt3dsdm::Qt3DSDMInstanceHandle parentInstance = getBridge()
+ ->GetParentInstance(instance);
+
+ if (parentInstance == doc->GetSceneInstance()
+ && newName.toQString() == getBridge()->getMaterialContainerName()) {
+ QString theTitle = QObject::tr("Rename Object Error");
+ QString theString = getBridge()->getMaterialContainerName()
+ + QObject::tr(" is a reserved name.");
+ // Display error message box asynchronously so focus loss won't trigger setting
+ // the name again
+ g_StudioApp.GetDialogs()->asyncDisplayMessageBox(theTitle, theString,
+ Qt3DSMessageBox::ICON_ERROR);
+ return;
+ }
+
+ Q3DStudio::CString realNewName = newName;
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ Q3DStudio::CString realName = getBridge()->GetName(instance);
+ int slashIndex = realName.rfind('/');
+ if (slashIndex != Q3DStudio::CString::ENDOFSTRING)
+ realNewName = realName.Left(slashIndex + 1) + newName;
+ }
+
+ if (!getBridge()->CheckNameUnique(parentInstance, instance, realNewName)) {
+ QString origNewName = newName.toQString();
+ realNewName = getBridge()->GetUniqueChildName(parentInstance, instance,
+ realNewName);
+ newName = realNewName;
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ int slashIndex = newName.rfind('/');
+ if (slashIndex != Q3DStudio::CString::ENDOFSTRING)
+ newName = newName.substr(slashIndex + 1);
+ }
+ // Display rename message box asynchronously so focus loss won't trigger setting
+ // the name again
+ g_StudioApp.GetDialogs()->DisplayObjectRenamed(origNewName, newName.toQString(),
+ true);
+ }
+
+ const auto sceneEditor = doc->getSceneEditor();
+
+ // A materialdef with the same name might exists as a file but not in the container,
+ // so an additional check is needed for that case
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ int i = 1;
+ while (QFileInfo(sceneEditor->getFilePathFromMaterialName(
+ realNewName.toQString())).exists()) {
+ ++i;
+ realNewName = Q3DStudio::CString::fromQString(
+ realNewName.toQString() + QString::number(i));
+ if (!getBridge()->CheckNameUnique(parentInstance, instance, realNewName)) {
+ realNewName = getBridge()->GetUniqueChildName(parentInstance, instance,
+ realNewName);
+ }
+ }
+ newName = realNewName;
+ int slashIndex = newName.rfind('/');
+ if (slashIndex != Q3DStudio::CString::ENDOFSTRING)
+ newName = newName.substr(slashIndex + 1);
+ }
+
+ if (newName != currentName) {
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ const auto properOldName = sceneEditor->GetName(instance).toQString();
+ const QString dirPath = doc->GetDocumentDirectory();
+ for (size_t matIdx = 0, end = m_matDatas.size(); matIdx < end; ++matIdx) {
+ if (m_matDatas[matIdx].m_name == properOldName) {
+ QFileInfo fileInfo(dirPath + QLatin1Char('/')
+ + m_matDatas[matIdx].m_relativePath);
+ const QString newFile = fileInfo.absolutePath()
+ + QLatin1Char('/')
+ + newName.toQString()
+ + QStringLiteral(".materialdef");
+ const auto properNewName
+ = sceneEditor->getMaterialNameFromFilePath(newFile);
+ newName = Q3DStudio::CString::fromQString(properNewName);
+ doc->queueMaterialRename(properOldName, properNewName);
+ }
+ }
+ }
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(
+ *g_StudioApp.GetCore()->GetDoc(),
+ QObject::tr("Set Name"))->SetName(instance, newName, false);
+ }
+ }
+ return;
+ }
+
+ qt3dsdm::SValue oldValue = m_guideInspectable
+ ? m_guideInspectable->properties()[handleToGuidePropIndex(handle)]->GetInspectableData()
+ : currentPropertyValue(instance, handle);
+ qt3dsdm::SValue v = value;
+
+ const bool hasPreview = (m_modifiedProperty.first == instance
+ && m_modifiedProperty.second == handle);
+
+ // If this set is a commit for property that was previously changed without
+ // committing, we must let the set go through even if the value hasn't changed
+ // to finish the transaction.
+ if (v == oldValue && !(commit && hasPreview))
+ return;
+
+ if (!commit && !hasPreview) {
+ m_previouslyCommittedValue = oldValue;
+ m_modifiedProperty.first = instance;
+ m_modifiedProperty.second = handle;
+ }
+
+ if (instance) {
+ // If the user enters 0.0 to any (x, y, z) values of camera scale,
+ // we reset the value back to original, because zero scale factor will crash
+ // camera-specific inverse matrix math. (Additionally, scale of zero for a camera
+ // is generally not useful anyway.) We could silently discard zero values also deeper in the
+ // value setter code, but then the inspector panel value would not be updated as opposed
+ // to both rejecting invalid and resetting the original value here.
+ EStudioObjectType theType = getBridge()->GetObjectType(instance);
+
+ if (theType == EStudioObjectType::OBJTYPE_CAMERA &&
+ studio->GetPropertySystem()->GetName(handle) == Q3DStudio::CString("scale")) {
+ const QVector3D theFloat3 = qt3dsdm::get<QVector3D>(v);
+ if (theFloat3.x() == 0.0f || theFloat3.y() == 0.0f || theFloat3.z() == 0.0f )
+ v = oldValue;
+ }
+
+ // some properties may initialize OpenGL resources (e.g. loading meshes will
+ // initialize vertex buffers), so the renderer's OpenGL context must be current
+ Q3DStudio::IStudioRenderer &theRenderer(g_StudioApp.getRenderer());
+ theRenderer.MakeContextCurrent();
+ m_UpdatableEditor.EnsureEditor(QObject::tr("Set Property"), __FILE__, __LINE__)
+ .SetInstancePropertyValue(instance, handle, v);
+
+ theRenderer.ReleaseContext();
+
+ m_UpdatableEditor.FireImmediateRefresh(instance);
+ } else if (m_guideInspectable) {
+ m_guideInspectable->properties()[handleToGuidePropIndex(handle)]->ChangeInspectableData(v);
+ }
+
+ if (commit) {
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+ if (m_previouslyCommittedValue == v) {
+ if (m_guideInspectable)
+ m_guideInspectable->Rollback();
+ else
+ m_UpdatableEditor.RollbackEditor();
+ } else {
+ if (m_guideInspectable) {
+ // If the guide ends up over the matte, destroy it
+ QSize presSize = g_StudioApp.GetCore()->GetStudioProjectSettings()
+ ->getPresentationSize();
+ bool isInPres = true;
+ qt3dsdm::SValue posValue = m_guideInspectable->GetPosition();
+ float position = qt3dsdm::get<float>(posValue);
+ if (m_guideInspectable->isHorizontal())
+ isInPres = 0.f <= position && float(presSize.height()) >= position;
+ else
+ isInPres = 0.f <= position && float(presSize.width()) >= position;
+ if (isInPres)
+ m_guideInspectable->Commit();
+ else
+ m_guideInspectable->Destroy();
+ } else {
+ m_UpdatableEditor.CommitEditor();
+ }
+ }
+
+ m_previouslyCommittedValue = {};
+ refreshTree();
+
+ saveIfMaterial(instance);
+ }
+}
+
+void InspectorControlModel::setSlideSelection(long instance, int handle, int index,
+ const QStringList &list)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ const auto metaDataProvider = doc->GetStudioSystem()->GetActionMetaData();
+ const auto info = metaDataProvider->GetMetaDataPropertyInfo(
+ metaDataProvider->GetMetaDataProperty(instance, handle));
+ QStringList stringlist = qt3dsdm::get<QStringList>(info->m_MetaDataData);
+
+ auto slideSystem = studioSystem->GetSlideSystem();
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ qt3dsdm::SStringOrInt newSelectedData;
+ if (!hasNextSlide)
+ stringlist.removeAll("Next");
+ if (!hasPreviousSlide)
+ stringlist.removeAll("Previous");
+
+ auto itemCount = stringlist.count();
+ if (index < itemCount) {
+ newSelectedData = qt3dsdm::SStringOrInt(std::make_shared<qt3dsdm::CDataStr>
+ (Q3DStudio::CString::fromQString(list[index]).c_str()));
+ } else {
+ auto slideHandle = slideSystem->GetSlideByInstance(instance);
+ auto masterSlide = slideSystem->GetMasterSlide(slideHandle);
+ long slideIndex = index - itemCount + 1;
+ auto newSelectedSlide = slideSystem->GetSlideByIndex(masterSlide, slideIndex);
+ newSelectedData = qt3dsdm::SStringOrInt((long)newSelectedSlide);
+ }
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(instance, handle, newSelectedData);
+}
+
+// temporarily prevent material renaming when opening the colors dialog (fix for QT3DS-3407)
+void InspectorControlModel::suspendMaterialRename(bool flag)
+{
+ m_suspendMaterialRename = flag;
+}
+
+void InspectorControlModel::setPropertyControllerInstance(
+ long instance,int property, Q3DStudio::CString controllerInstance, bool controlled)
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ IObjectReferenceHelper *objRefHelper = doc->GetDataModelObjectReferenceHelper();
+
+ Q3DStudio::CString instancepath = Q3DStudio::CString(
+ objRefHelper->GetObjectReferenceString(doc->GetSceneInstance(),
+ CRelativePathTools::EPATHTYPE_GUID, instance));
+ Q_ASSERT(instancepath.size());
+
+ doc->SetInstancePropertyControlled(instance, instancepath, property,
+ controllerInstance, controlled);
+}
+
+void InspectorControlModel::setPropertyControlled(long instance, int property)
+{
+ const auto signalSender
+ = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystemSignalSender();
+
+ signalSender->SendControlledToggled(instance, property);
+}
+
+bool InspectorControlModel::isLayer(long instance) const
+{
+ return getBridge()->GetObjectType(instance) == EStudioObjectType::OBJTYPE_LAYER;
+}
+
+QString InspectorControlModel::renderableId(const QString &filePath) const
+{
+ return g_StudioApp.getRenderableId(filePath);
+}
+
+void InspectorControlModel::setPropertyAnimated(long instance, int handle, bool animated)
+{
+ CCmd* cmd = nullptr;
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ if (animated)
+ cmd = new CCmdDataModelAnimate(doc, instance, handle);
+ else
+ cmd = new CCmdDataModelDeanimate(doc, instance, handle);
+
+ g_StudioApp.GetCore()->ExecuteCommand(cmd);
+}
+
+QVariant InspectorControlModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(),index.parent()))
+ return {};
+
+ const auto row = index.row();
+
+ switch (role) {
+ case GroupValuesRole:
+ return m_groupElements.at(row).controlElements;
+ case GroupTitleRole:
+ return m_groupElements.at(row).groupTitle;
+ case GroupInfoRole:
+ return m_groupElements.at(row).groupInfo;
+ }
+ return {};
+}
+
+QHash<int, QByteArray> InspectorControlModel::roleNames() const
+{
+ auto names = QAbstractListModel::roleNames();
+ names.insert(GroupValuesRole, "values");
+ names.insert(GroupTitleRole, "title");
+ names.insert(GroupInfoRole, "info");
+ return names;
+}
+
+InspectorControlBase::~InspectorControlBase()
+{
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.h
new file mode 100644
index 00000000..3063f047
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.h
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INSPECTORCONTROLMODEL_H
+#define INSPECTORCONTROLMODEL_H
+
+#include "Qt3DSDMValue.h"
+#include "Qt3DSDMMetaDataValue.h"
+#include "Qt3DSDMMetaDataTypes.h"
+#include "IDocumentEditor.h"
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qvector.h>
+
+class CInspectableBase;
+class Qt3DSDMInspectable;
+class GuideInspectable;
+class VariantsGroupModel;
+class CClientDataModelBridge;
+
+namespace qt3dsdm {
+class ISignalConnection;
+typedef std::shared_ptr<ISignalConnection> TSignalConnectionPtr;
+}
+
+namespace Q3DStudio
+{
+class Qt3DSDMInspectorRow;
+}
+
+class InspectorControlBase : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qt3dsdm::DataModelDataType::Value dataType MEMBER m_dataType CONSTANT)
+ Q_PROPERTY(qt3dsdm::AdditionalMetaDataType::Value propertyType MEMBER m_propertyType CONSTANT)
+ Q_PROPERTY(QVariant value MEMBER m_value NOTIFY valueChanged)
+ Q_PROPERTY(QVariant values MEMBER m_values NOTIFY valuesChanged)
+ Q_PROPERTY(QString title MEMBER m_title CONSTANT)
+ Q_PROPERTY(QString toolTip MEMBER m_tooltip NOTIFY tooltipChanged)
+ Q_PROPERTY(int instance MEMBER m_instance CONSTANT)
+ Q_PROPERTY(int handle MEMBER m_property CONSTANT)
+
+ Q_PROPERTY(bool enabled MEMBER m_enabled CONSTANT)
+ Q_PROPERTY(bool animatable MEMBER m_animatable CONSTANT)
+ Q_PROPERTY(bool animated MEMBER m_animated NOTIFY animatedChanged)
+ Q_PROPERTY(bool controlled MEMBER m_controlled NOTIFY controlledChanged)
+ Q_PROPERTY(bool controllable MEMBER m_controllable CONSTANT)
+ Q_PROPERTY(QString controller MEMBER m_controller NOTIFY controlledChanged)
+
+public:
+ virtual ~InspectorControlBase();
+
+Q_SIGNALS:
+ void valueChanged();
+ void valuesChanged();
+ void animatedChanged();
+ void controlledChanged();
+ void tooltipChanged();
+
+public:
+ qt3dsdm::DataModelDataType::Value m_dataType;
+ qt3dsdm::AdditionalMetaDataType::Value m_propertyType;
+ qt3dsdm::SMetaDataPropertyInfo m_metaProperty;
+ QVariant m_value;
+ QVariant m_values;
+ QString m_title;
+ QString m_tooltip;
+
+ qt3dsdm::Qt3DSDMInstanceHandle m_instance;
+ qt3dsdm::Qt3DSDMPropertyHandle m_property;
+
+ bool m_enabled = true;
+ bool m_animatable = false;
+ bool m_animated = false;
+ bool m_controlled = false;
+ bool m_controllable = false;
+ QString m_controller;
+ std::vector<qt3dsdm::TSignalConnectionPtr> m_connections;
+};
+
+class InspectorControlModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit InspectorControlModel(VariantsGroupModel *variantsModel, QObject *parent);
+ ~InspectorControlModel() override = default;
+
+ enum Roles {
+ GroupValuesRole = Qt::UserRole + 1,
+ GroupTitleRole,
+ GroupInfoRole
+ };
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ QHash<int, QByteArray> roleNames() const override;
+
+ void setInspectable(CInspectableBase *inInspectable);
+ CInspectableBase *inspectable() const;
+ void setMaterials(std::vector<Q3DStudio::CFilePath> &materials);
+ void setMatDatas(const std::vector<Q3DStudio::CFilePath> &matdatas);
+ void updateFontValues(InspectorControlBase *element) const;
+ void refreshRenderables();
+ void refresh();
+ void saveIfMaterial(qt3dsdm::Qt3DSDMInstanceHandle instance);
+
+ bool hasInstanceProperty(long instance, int handle);
+
+ qt3dsdm::SValue currentPropertyValue(long instance, int handle) const;
+ QString currentControllerValue(long instance, int handle) const;
+ void setPropertyControllerInstance(long instance,int handle,
+ Q3DStudio::CString controllerInstance,
+ bool controlled);
+ void notifyPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance,
+ qt3dsdm::Qt3DSDMPropertyHandle inProperty);
+
+ Q_INVOKABLE void setMaterialTypeValue(long instance, int handle, const QVariant &value);
+ Q_INVOKABLE void setShaderValue(long instance, int handle, const QVariant &value);
+ Q_INVOKABLE void setMatDataValue(long instance, int handle, const QVariant &value);
+ Q_INVOKABLE void setRenderableValue(long instance, int handle, const QVariant &value);
+ Q_INVOKABLE void setPropertyValue(long instance, int handle, const QVariant &value, bool commit = true);
+ Q_INVOKABLE void setSlideSelection(long instance, int handle, int index,
+ const QStringList &list);
+ Q_INVOKABLE void suspendMaterialRename(bool flag);
+ Q_INVOKABLE void setPropertyAnimated(long instance, int handle, bool animated);
+ Q_INVOKABLE void setPropertyControlled(long instance, int property);
+ Q_INVOKABLE bool isLayer(long instance) const;
+ Q_INVOKABLE QString renderableId(const QString &filePath) const;
+ Q_INVOKABLE bool isMaterial() const;
+ Q_INVOKABLE bool isDefaultMaterial() const;
+ Q_INVOKABLE void addMaterial();
+ Q_INVOKABLE void duplicateMaterial();
+ Q_INVOKABLE bool isGroupCollapsed(int groupIdx) const;
+ Q_INVOKABLE void updateGroupCollapseState(int groupIdx, bool state);
+
+private:
+ void onSlideRearranged(const qt3dsdm::Qt3DSDMSlideHandle &inMaster, int inOldIndex,
+ int inNewIndex);
+
+
+ struct GroupInspectorControl {
+ QString groupTitle;
+ QVariantList controlElements;
+ QString groupInfo;
+
+ ~GroupInspectorControl() {
+ }
+ };
+
+ QVector<GroupInspectorControl> m_groupElements;
+ CInspectableBase *m_inspectableBase = nullptr;
+ GuideInspectable *m_guideInspectable = nullptr;
+
+ struct MaterialEntry
+ {
+ QString m_name;
+ QString m_relativePath;
+ };
+
+ struct MaterialDataEntry
+ {
+ QString m_name;
+ QString m_relativePath;
+ QMap<QString, QString> m_values;
+ QMap<QString, QMap<QString, QString>> m_textureValues;
+ };
+
+ std::vector<MaterialEntry> m_materials;
+ std::vector<MaterialDataEntry> m_matDatas;
+ std::vector<Q3DStudio::CFilePath> m_cachedMatDatas;
+ qt3dsdm::Qt3DSDMInstanceHandle m_refMaterial;
+
+ Q3DStudio::CUpdateableDocumentEditor m_UpdatableEditor;
+
+ bool m_suspendMaterialRename = false;
+
+ QPair<long, int> m_modifiedProperty;
+
+ qt3dsdm::SValue m_previouslyCommittedValue;
+
+ QHash<int, QHash<int, bool> > m_collapseMap;
+
+ QString getBasicMaterialString() const;
+ QString getAnimatableMaterialString() const;
+ QString getReferencedMaterialString() const;
+ QString getStandardMaterialString() const;
+ QString getDefaultMaterialString() const;
+ bool isInsideMaterialContainer() const;
+ bool isInsideMaterialContainer(CInspectableBase *inspectable) const;
+ bool isAnimatableMaterial() const;
+ bool isAnimatableMaterial(CInspectableBase *inspectable) const;
+ bool isBasicMaterial() const;
+ bool isBasicMaterial(CInspectableBase *inspectable) const;
+ void updateMaterialValues(const QStringList &values, int elementIndex,
+ bool updatingShaders = false);
+ qt3dsdm::Qt3DSDMInstanceHandle getReferenceMaterial(CInspectableBase *inspectable) const;
+ void updateShaderValues();
+ void updateMatDataValues();
+ void updatePropertyValue(InspectorControlBase *element) const;
+ void rebuildTree();
+ void refreshTree();
+ void updateAnimateToggleState(InspectorControlBase *inItem);
+ void updateControlledToggleState(InspectorControlBase *inItem) const;
+
+ QStringList materialTypeValues() const;
+ QStringList shaderValues() const;
+ QStringList matDataValues() const;
+ InspectorControlBase *createMaterialTypeItem(Qt3DSDMInspectable *inspectable, int groupIndex);
+ InspectorControlBase *createShaderItem(Qt3DSDMInspectable *inspectable, int groupIndex);
+ InspectorControlBase *createMatDataItem(Qt3DSDMInspectable *inspectable, int groupIndex);
+ InspectorControlBase *createItem(Qt3DSDMInspectable *inspectable,
+ Q3DStudio::Qt3DSDMInspectorRow *row, int groupIndex);
+ InspectorControlBase *createItem(Qt3DSDMInspectable *inspectable,
+ const qt3dsdm::SMetaDataPropertyInfo &metaProperty,
+ int groupIndex);
+
+ QVector<GroupInspectorControl> computeTree(CInspectableBase *inspectBase);
+ bool isTreeRebuildRequired(CInspectableBase *inspectBase);
+
+ GroupInspectorControl computeGroup(CInspectableBase* inspectBase,
+ int theIndex, bool disableAnimation = false,
+ bool isReference = false);
+ bool isGroupRebuildRequired(CInspectableBase *inspectable, int theIndex) const;
+
+ CClientDataModelBridge *getBridge() const;
+
+ static int handleToGuidePropIndex(int handle) { return handle - 1; }
+
+ VariantsGroupModel *m_variantsModel = nullptr;
+};
+
+#endif // INSPECTORCONTROLMODEL_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp
new file mode 100644
index 00000000..e4e0379f
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp
@@ -0,0 +1,930 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "InspectorControlView.h"
+#include "Literals.h"
+#include "CColor.h"
+#include "Qt3DSDMValue.h"
+#include "StudioUtils.h"
+#include "InspectorControlModel.h"
+#include "StudioPreferences.h"
+#include "Core.h"
+#include "Doc.h"
+#include "IDocumentEditor.h"
+#include "ImageChooserModel.h"
+#include "ImageChooserView.h"
+#include "MeshChooserView.h"
+#include "TextureChooserView.h"
+#include "InspectableBase.h"
+#include "StudioApp.h"
+#include "ObjectListModel.h"
+#include "ObjectBrowserView.h"
+#include "IDirectoryWatchingSystem.h"
+#include "StandardExtensions.h"
+#include "FileChooserView.h"
+#include "IObjectReferenceHelper.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "StudioFullSystem.h"
+#include "ClientDataModelBridge.h"
+#include "MainFrm.h"
+#include "DataInputDlg.h"
+#include "Dialogs.h"
+#include "ProjectFile.h"
+#include "MaterialRefView.h"
+#include "BasicObjectsModel.h"
+#include "Qt3DSDMSlides.h"
+#include "VariantsGroupModel.h"
+#include "VariantTagDialog.h"
+#include "SlideView.h"
+#include "TimelineWidget.h"
+#include "SelectedValue.h"
+#include "Qt3DSDMInspectable.h"
+#include "Qt3DSDMSlides.h"
+#include "Qt3DSDMMaterialInspectable.h"
+#include "GuideInspectable.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtWidgets/qmenu.h>
+#include <QtWidgets/qdesktopwidget.h>
+#include <QtWidgets/qlistwidget.h>
+
+InspectorControlView::InspectorControlView(const QSize &preferredSize, QWidget *parent)
+ : QQuickWidget(parent),
+ TabNavigable(),
+ m_variantsGroupModel(new VariantsGroupModel(this)),
+ m_inspectorControlModel(new InspectorControlModel(m_variantsGroupModel, this)),
+ m_meshChooserView(new MeshChooserView(this)),
+ m_preferredSize(preferredSize)
+{
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &InspectorControlView::initialize);
+ auto dispatch = g_StudioApp.GetCore()->GetDispatch();
+ dispatch->AddPresentationChangeListener(this);
+ dispatch->AddDataModelListener(this);
+
+ connect(m_meshChooserView, &MeshChooserView::meshSelected, this,
+ [this] (int handle, int instance, const QString &name) {
+ if (name.startsWith(QLatin1Char('#'))) {
+ if (m_inspectorControlModel)
+ m_inspectorControlModel->setPropertyValue(instance, handle, name);
+ } else {
+ setPropertyValueFromFilename(instance, handle, name);
+ }
+ });
+}
+
+static bool isInList(const wchar_t **list, const Q3DStudio::CString &inStr)
+{
+ for (const wchar_t **item = list; item && *item; ++item) {
+ if (inStr.Compare(*item, Q3DStudio::CString::ENDOFSTRING, false))
+ return true;
+ }
+ return false;
+}
+
+void InspectorControlView::filterMaterials(std::vector<Q3DStudio::CFilePath> &materials)
+{
+ static const wchar_t *extensions[] = {
+ L"material",
+ L"shader",
+ nullptr
+ };
+ for (size_t i = 0; i < m_fileList.size(); ++i) {
+ if (isInList(extensions, m_fileList[i].GetExtension()))
+ materials.push_back(m_fileList[i]);
+ }
+}
+
+void InspectorControlView::filterMatDatas(std::vector<Q3DStudio::CFilePath> &matDatas)
+{
+ static const wchar_t *extensions[] = {
+ L"materialdef",
+ nullptr
+ };
+ for (size_t i = 0; i < m_fileList.size(); ++i) {
+ if (isInList(extensions, m_fileList[i].GetExtension()))
+ matDatas.push_back(m_fileList[i]);
+ }
+}
+
+void InspectorControlView::OnNewPresentation()
+{
+ auto core = g_StudioApp.GetCore();
+ auto sp = core->GetDoc()->GetStudioSystem()->GetFullSystem()->GetSignalProvider();
+ auto assetGraph = core->GetDoc()->GetAssetGraph();
+
+ m_connections.push_back(core->GetDispatch()->ConnectSelectionChange(
+ std::bind(&InspectorControlView::OnSelectionSet, this, std::placeholders::_1)));
+ m_connections.push_back(g_StudioApp.getDirectoryWatchingSystem().AddDirectory(
+ g_StudioApp.GetCore()->getProjectFile().getProjectPath(),
+ std::bind(&InspectorControlView::onFilesChanged, this, std::placeholders::_1)));
+ m_connections.push_back(sp->ConnectInstancePropertyValue(
+ std::bind(&InspectorControlView::onPropertyChanged, this, std::placeholders::_1,
+ std::placeholders::_2)));
+ m_connections.push_back(assetGraph->ConnectChildAdded(
+ std::bind(&InspectorControlView::onChildAdded, this, std::placeholders::_2)));
+ m_connections.push_back(assetGraph->ConnectChildRemoved(
+ std::bind(&InspectorControlView::onChildRemoved, this)));
+}
+
+void InspectorControlView::OnClosingPresentation()
+{
+ // Image chooser model needs to be deleted, because otherwise it'll try to update the model for
+ // the new presentation before subpresentations are resolved, corrupting the model.
+ // The model also has a connection to project file which needs to refreshed if project changes.
+ delete m_imageChooserView;
+ m_fileList.clear();
+ m_connections.clear();
+}
+
+void InspectorControlView::onFilesChanged(
+ const Q3DStudio::TFileModificationList &inFileModificationList)
+{
+ static const wchar_t *materialExtensions[] = {
+ L"material", L"shader", L"materialdef",
+ nullptr
+ };
+ static const wchar_t *fontExtensions[] = {
+ L"ttf", L"otf",
+ nullptr
+ };
+
+ bool updateFonts = false;
+ for (size_t idx = 0, end = inFileModificationList.size(); idx < end; ++idx) {
+ const Q3DStudio::SFileModificationRecord &record(inFileModificationList[idx]);
+ if (record.m_FileInfo.IsFile()) {
+ if (isInList(materialExtensions, record.m_File.GetExtension())) {
+ Q3DStudio::CFilePath relativePath(
+ Q3DStudio::CFilePath::GetRelativePathFromBase(
+ g_StudioApp.GetCore()->GetDoc()->GetDocumentDirectory(),
+ record.m_File));
+
+ if (record.m_ModificationType == Q3DStudio::FileModificationType::Created)
+ qt3dsdm::binary_sort_insert_unique(m_fileList, relativePath);
+ else if (record.m_ModificationType == Q3DStudio::FileModificationType::Destroyed)
+ qt3dsdm::binary_sort_erase(m_fileList, relativePath);
+ } else if (isInList(fontExtensions, record.m_File.GetExtension())) {
+ if (record.m_ModificationType == Q3DStudio::FileModificationType::Created
+ || record.m_ModificationType == Q3DStudio::FileModificationType::Destroyed) {
+ updateFonts = true;
+ }
+ } else if (record.m_ModificationType == Q3DStudio::FileModificationType::Modified
+ && record.m_File.toQString()
+ == g_StudioApp.GetCore()->getProjectFile().getProjectFilePath()) {
+ g_StudioApp.GetCore()->getProjectFile().loadSubpresentationsAndDatainputs(
+ g_StudioApp.m_subpresentations, g_StudioApp.m_dataInputDialogItems);
+ m_inspectorControlModel->refreshRenderables();
+ }
+ }
+ }
+ std::vector<Q3DStudio::CFilePath> materials;
+ filterMaterials(materials);
+ m_inspectorControlModel->setMaterials(materials);
+
+ std::vector<Q3DStudio::CFilePath> matDatas;
+ filterMatDatas(matDatas);
+ m_inspectorControlModel->setMatDatas(matDatas);
+
+ if (updateFonts) {
+ // The fonts list in doc is not necessarily yet updated, so do update async
+ QTimer::singleShot(0, this, [this]() {
+ m_inspectorControlModel->updateFontValues(nullptr);
+ });
+ }
+}
+
+InspectorControlView::~InspectorControlView()
+{
+ g_StudioApp.GetCore()->GetDispatch()->RemovePresentationChangeListener(this);
+ delete m_dataInputChooserView;
+}
+
+QSize InspectorControlView::sizeHint() const
+{
+ return m_preferredSize;
+}
+
+void InspectorControlView::mousePressEvent(QMouseEvent *event)
+{
+ g_StudioApp.setLastActiveView(this);
+ QQuickWidget::mousePressEvent(event);
+}
+
+void InspectorControlView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty(QStringLiteral("_parentView"), this);
+ rootContext()->setContextProperty(QStringLiteral("_inspectorModel"), m_inspectorControlModel);
+ rootContext()->setContextProperty(QStringLiteral("_variantsGroupModel"), m_variantsGroupModel);
+ rootContext()->setContextProperty(QStringLiteral("_resDir"), StudioUtils::resourceImageUrl());
+ rootContext()->setContextProperty(QStringLiteral("_tabOrderHandler"), tabOrderHandler());
+ rootContext()->setContextProperty(QStringLiteral("_mouseHelper"), &m_mouseHelper);
+ rootContext()->setContextProperty(QStringLiteral("_utils"), &m_qmlUtils);
+ m_mouseHelper.setWidget(this);
+
+ qmlRegisterUncreatableType<qt3dsdm::DataModelDataType>(
+ "Qt3DStudio", 1, 0, "DataModelDataType",
+ QStringLiteral("DataModelDataType is an enum container"));
+ qmlRegisterUncreatableType<qt3dsdm::AdditionalMetaDataType>(
+ "Qt3DStudio", 1, 0, "AdditionalMetaDataType",
+ QStringLiteral("AdditionalMetaDataType is an enum container"));
+ engine()->addImportPath(StudioUtils::qmlImportPath());
+ setSource(QUrl(QStringLiteral("qrc:/Palettes/Inspector/InspectorControlView.qml")));
+}
+
+QAbstractItemModel *InspectorControlView::inspectorControlModel() const
+{
+ return m_inspectorControlModel;
+}
+
+QString InspectorControlView::titleText() const
+{
+ if (m_inspectableBase) {
+ Q3DStudio::CString theName = m_inspectableBase->getName();
+ if (theName == L"PathAnchorPoint")
+ return tr("Anchor Point");
+ else
+ return theName.toQString();
+ }
+ return tr("No Object Selected");
+}
+
+bool InspectorControlView::isRefMaterial(int instance) const
+{
+ auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ return bridge->IsReferencedMaterialInstance(instance);
+}
+
+QString InspectorControlView::noneString() const
+{
+ return ChooserModelBase::noneString();
+}
+
+bool InspectorControlView::canLinkProperty(int instance, int handle) const
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+
+ if (bridge->isInsideMaterialContainer(instance))
+ return false;
+
+ if (bridge->IsMaterialBaseInstance(instance)) // all material types are unlinkable
+ return false;
+
+ if (handle == bridge->GetSceneAsset().m_Eyeball.m_Property) // eyeball is unlinkable
+ return false;
+
+ return doc->GetDocumentReader().CanPropertyBeLinked(instance, handle);
+}
+
+bool InspectorControlView::canOpenInInspector(int instance, int handle) const
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ qt3dsdm::SValue value;
+ doc->GetPropertySystem()->GetInstancePropertyValue(instance, handle, value);
+ if (!value.empty() && value.getType() == qt3dsdm::DataModelDataType::Long4) {
+ qt3dsdm::SLong4 guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+ return guid.Valid();
+ }
+ return false;
+}
+
+void InspectorControlView::openInInspector()
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ qt3dsdm::SValue value;
+ doc->GetPropertySystem()->GetInstancePropertyValue(m_contextMenuInstance, m_contextMenuHandle,
+ value);
+ qt3dsdm::SLong4 guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+ const auto instance = bridge->GetInstanceByGUID(guid);
+ doc->SelectDataModelObject(instance);
+}
+
+void InspectorControlView::onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance,
+ qt3dsdm::Qt3DSDMPropertyHandle inProperty)
+{
+ m_inspectorControlModel->notifyPropertyChanged(inInstance, inProperty);
+
+ auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ // titleChanged implies icon change too, but that will only occur if inspectable type changes,
+ // which will invalidate the inspectable anyway, so in reality we are only interested in name
+ // property here
+ if (inProperty == bridge->GetNameProperty() && m_inspectableBase
+ && m_inspectableBase->isValid()) {
+ Q_EMIT titleChanged();
+ }
+}
+
+void InspectorControlView::onChildAdded(int inChild)
+{
+ // Changes to asset graph invalidate the object browser model, so close it if it is open
+ if (m_activeBrowser.isActive() && m_activeBrowser.m_browser == m_objectReferenceView)
+ m_activeBrowser.clear();
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ if (bridge->IsCustomMaterialInstance(inChild)) {
+ QVector<qt3dsdm::Qt3DSDMInstanceHandle> refMats;
+ doc->getSceneReferencedMaterials(doc->GetSceneInstance(), refMats);
+ for (auto &refMat : qAsConst(refMats)) {
+ if ((int)bridge->getMaterialReference(refMat) == inChild)
+ g_StudioApp.GetCore()->GetDispatch()->FireImmediateRefreshInstance(refMat);
+ }
+ }
+}
+
+void InspectorControlView::onChildRemoved()
+{
+ // Changes to asset graph invalidate the object browser model, so close it if it is open
+ if (m_activeBrowser.isActive() && m_activeBrowser.m_browser == m_objectReferenceView)
+ m_activeBrowser.clear();
+}
+
+QColor InspectorControlView::titleColor(int instance, int handle) const
+{
+ QColor ret = CStudioPreferences::textColor();
+ if (instance != 0) {
+ if (g_StudioApp.GetCore()->GetDoc()->GetDocumentReader()
+ .IsPropertyLinked(instance, handle)) {
+ ret = CStudioPreferences::masterColor();
+ }
+ }
+ return ret;
+}
+
+QString InspectorControlView::titleIcon() const
+{
+ if (m_inspectableBase)
+ return CStudioObjectTypes::GetNormalIconName(m_inspectableBase->getObjectType());
+ return {};
+}
+
+bool InspectorControlView::isEditable(int handle) const
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ if (doc->GetStudioSystem()->GetSlideSystem()->IsMasterSlide(doc->GetActiveSlide())
+ && doc->GetStudioSystem()->GetPropertySystem()->GetName(handle) == L"eyeball") {
+ return false;
+ }
+ return true;
+}
+
+void InspectorControlView::OnSelectionSet(Q3DStudio::SSelectedValue selectable)
+{
+ CInspectableBase *inspectable = createInspectableFromSelectable(selectable);
+
+ if (inspectable && !inspectable->isValid())
+ inspectable = nullptr;
+
+ setInspectable(inspectable);
+}
+
+CInspectableBase *InspectorControlView::createInspectableFromSelectable(
+ Q3DStudio::SSelectedValue selectable)
+{
+ using namespace Q3DStudio;
+
+ CInspectableBase *inspectableBase = nullptr;
+ if (!selectable.empty()) {
+ switch (selectable.getType()) {
+ case SelectedValueTypes::Slide: {
+ // TODO: seems like slides are not directly selectable, this should be removed.
+ auto selectableInstance = selectable.getData<SSlideInstanceWrapper>().m_Instance;
+ inspectableBase = new Qt3DSDMInspectable(selectableInstance);
+ } break;
+
+ case SelectedValueTypes::MultipleInstances:
+ case SelectedValueTypes::Instance: {
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ // Note: Inspector doesn't support multiple selection
+ qt3dsdm::TInstanceHandleList selectedsInstances = selectable.GetSelectedInstances();
+ if (selectedsInstances.size() == 1) {
+ Qt3DSDMInstanceHandle selectedInstance = selectedsInstances[0];
+ if (doc->GetDocumentReader().IsInstance(selectedInstance)) {
+ auto *bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ qt3dsdm::Qt3DSDMSlideHandle activeSlide = doc->GetActiveSlide();
+
+ // Scene or Component (when being edited)
+ if (selectedInstance == bridge->GetOwningComponentInstance(activeSlide)) {
+ Qt3DSDMInstanceHandle activeSlideInstance = doc->GetStudioSystem()
+ ->GetSlideSystem()->GetSlideInstance(activeSlide);
+ inspectableBase = new Qt3DSDMInspectable(selectedInstance,
+ activeSlideInstance);
+ } else if (bridge->IsMaterialBaseInstance(selectedInstance)) {
+ inspectableBase = new Qt3DSDMMaterialInspectable(selectedInstance);
+ } else {
+ inspectableBase = new Qt3DSDMInspectable(selectedInstance);
+ }
+ }
+ }
+ } break;
+
+ case SelectedValueTypes::Guide: {
+ qt3dsdm::Qt3DSDMGuideHandle guide = selectable.getData<qt3dsdm::Qt3DSDMGuideHandle>();
+ inspectableBase = new GuideInspectable(guide);
+ } break;
+
+ default:
+ break; // Ignore slide insertion and unknown selectable types
+ };
+ }
+
+ return inspectableBase;
+}
+
+void InspectorControlView::setInspectable(CInspectableBase *inInspectable)
+{
+ if (m_inspectableBase != inInspectable) {
+ m_activeBrowser.clear();
+ m_inspectableBase = inInspectable;
+ m_inspectorControlModel->setInspectable(inInspectable);
+
+ Q_EMIT titleChanged();
+
+ m_variantsGroupModel->refresh();
+ }
+}
+
+void InspectorControlView::showContextMenu(int x, int y, int handle, int instance)
+{
+ m_contextMenuInstance = instance;
+ m_contextMenuHandle = handle;
+
+ QMenu theContextMenu;
+
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+
+ if (canOpenInInspector(instance, handle)) {
+ auto action = theContextMenu.addAction(tr("Open in Inspector"));
+ connect(action, &QAction::triggered, this, &InspectorControlView::openInInspector);
+ }
+
+ if (canLinkProperty(instance, handle)) {
+ bool isLinked = doc->GetDocumentReader().IsPropertyLinked(instance, handle);
+ auto action = theContextMenu.addAction(isLinked ? tr("Unlink Property from Master Slide")
+ : tr("Link Property from Master Slide"));
+ connect(action, &QAction::triggered, this, &InspectorControlView::toggleMasterLink);
+ } else {
+ auto action = theContextMenu.addAction(tr("Unable to link from Master Slide"));
+ action->setEnabled(false);
+ }
+
+ theContextMenu.exec(mapToGlobal({x, y}));
+ m_contextMenuInstance = 0;
+ m_contextMenuHandle = 0;
+}
+
+// context menu for the variants tags
+void InspectorControlView::showTagContextMenu(int x, int y, const QString &group,
+ const QString &tag)
+{
+ QMenu theContextMenu;
+
+ auto actionRename = theContextMenu.addAction(QObject::tr("Rename Tag"));
+ connect(actionRename, &QAction::triggered, this, [&]() {
+ VariantTagDialog dlg(VariantTagDialog::RenameTag, group, tag);
+ if (dlg.exec() == QDialog::Accepted) {
+ g_StudioApp.GetCore()->getProjectFile().renameVariantTag(group, dlg.getNames().first,
+ dlg.getNames().second);
+ m_variantsGroupModel->refresh();
+
+ // refresh slide view so the tooltip show the renamed tag immediately, no need to
+ // refresh the timeline because each row gets the tags directly from the property which
+ // is always up to date.
+ g_StudioApp.m_pMainWnd->getSlideView()->refreshVariants();
+ }
+ });
+
+ auto actionDelete = theContextMenu.addAction(QObject::tr("Delete Tag"));
+ connect(actionDelete, &QAction::triggered, this, [&]() {
+ g_StudioApp.GetCore()->getProjectFile().deleteVariantTag(group, tag);
+ g_StudioApp.m_pMainWnd->getTimelineWidget()->refreshVariants();
+ g_StudioApp.m_pMainWnd->getSlideView()->refreshVariants();
+ m_variantsGroupModel->refresh();
+ if (g_StudioApp.GetCore()->getProjectFile().variantsDef()[group].m_tags.size() == 0)
+ g_StudioApp.m_pMainWnd->updateActionFilterEnableState();
+ });
+
+ theContextMenu.exec(mapToGlobal({x, y}));
+}
+
+// context menu for the variants groups
+void InspectorControlView::showGroupContextMenu(int x, int y, const QString &group)
+{
+ QMenu theContextMenu;
+
+ ProjectFile &projectFile = g_StudioApp.GetCore()->getProjectFile();
+
+ auto actionRename = theContextMenu.addAction(QObject::tr("Rename Group"));
+ connect(actionRename, &QAction::triggered, this, [&]() {
+ VariantTagDialog dlg(VariantTagDialog::RenameGroup, {}, group);
+ if (dlg.exec() == QDialog::Accepted) {
+ projectFile.renameVariantGroup(dlg.getNames().first, dlg.getNames().second);
+ g_StudioApp.m_pMainWnd->getTimelineWidget()->refreshVariants();
+ m_variantsGroupModel->refresh();
+
+ // refresh slide view so the tooltip show the renamed group immediately, no need to
+ // refresh the timeline because each row gets the tags directly from the property which
+ // is always up to date.
+ g_StudioApp.m_pMainWnd->getSlideView()->refreshVariants();
+ }
+ });
+
+ auto actionColor = theContextMenu.addAction(QObject::tr("Change Group Color"));
+ connect(actionColor, &QAction::triggered, this, [&]() {
+ const auto variantsDef = projectFile.variantsDef();
+ QColor newColor = this->showColorDialog(variantsDef[group].m_color);
+ projectFile.changeVariantGroupColor(group, newColor.name());
+ // no need to refresh variants in the timeline widget as it references the group color in
+ // the project file m_variants, and a redraw is triggered upon color selection dialog close.
+ g_StudioApp.m_pMainWnd->getSlideView()->refreshVariants();
+ m_variantsGroupModel->refresh();
+ });
+
+ auto actionDelete = theContextMenu.addAction(QObject::tr("Delete Group"));
+ connect(actionDelete, &QAction::triggered, this, [&]() {
+ projectFile.deleteVariantGroup(group);
+ g_StudioApp.m_pMainWnd->getTimelineWidget()->refreshVariants();
+ g_StudioApp.m_pMainWnd->getSlideView()->refreshVariants();
+ g_StudioApp.m_pMainWnd->updateActionFilterEnableState();
+ m_variantsGroupModel->refresh();
+ });
+
+ theContextMenu.exec(mapToGlobal({x, y}));
+}
+
+void InspectorControlView::toggleMasterLink()
+{
+ Q3DStudio::ScopedDocumentEditor editor(*g_StudioApp.GetCore()->GetDoc(),
+ QObject::tr("Link Property"), __FILE__, __LINE__);
+ bool wasLinked = editor->IsPropertyLinked(m_contextMenuInstance, m_contextMenuHandle);
+
+ if (wasLinked)
+ editor->UnlinkProperty(m_contextMenuInstance, m_contextMenuHandle);
+ else
+ editor->LinkProperty(m_contextMenuInstance, m_contextMenuHandle);
+}
+
+void InspectorControlView::setPropertyValueFromFilename(long instance, int handle,
+ const QString &name)
+{
+ if (m_inspectorControlModel) {
+ QString value;
+ if (name != ChooserModelBase::noneString()) {
+ // Relativize the path to the presentation
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const QDir documentDir(doc->GetDocumentDirectory());
+ QString relativeName = documentDir.relativeFilePath(name);
+ value = relativeName;
+ }
+ m_inspectorControlModel->setPropertyValue(instance, handle, value);
+ }
+}
+
+QObject *InspectorControlView::showImageChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_imageChooserView) {
+ m_imageChooserView = new ImageChooserView(this);
+ connect(m_imageChooserView, &ImageChooserView::imageSelected, this,
+ [this] (int handle, int instance, const QString &imageName) {
+ // To avoid duplicate undo points when setting image property we can't rely
+ // on regular property duplication checks, as images are not directly stored as
+ // their paths. Also, there is no check for duplication on renderables.
+ if (m_imageChooserView->currentDataModelPath() != imageName) {
+ QString renderableId = g_StudioApp.getRenderableId(imageName);
+ if (renderableId.isEmpty()) {
+ setPropertyValueFromFilename(instance, handle, imageName);
+ } else {
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(),
+ QObject::tr("Set Property"))
+ ->setInstanceImagePropertyValue(
+ instance, handle, Q3DStudio::CString::fromQString(renderableId));
+ if (m_inspectorControlModel)
+ m_inspectorControlModel->saveIfMaterial(instance);
+ }
+ }
+ });
+ }
+
+ m_imageChooserView->setHandle(handle);
+ m_imageChooserView->setInstance(instance);
+
+ CDialogs::showWidgetBrowser(this, m_imageChooserView, point);
+ m_activeBrowser.setData(m_imageChooserView, handle, instance);
+
+ return m_imageChooserView;
+}
+
+QObject *InspectorControlView::showFilesChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_fileChooserView) {
+ m_fileChooserView = new FileChooserView(this);
+ connect(m_fileChooserView, &FileChooserView::fileSelected, this,
+ [this] (int handle, int instance, const QString &fileName) {
+ setPropertyValueFromFilename(instance, handle, fileName);
+ });
+ }
+
+ m_fileChooserView->setHandle(handle);
+ m_fileChooserView->setInstance(instance);
+
+ CDialogs::showWidgetBrowser(this, m_fileChooserView, point);
+ m_activeBrowser.setData(m_fileChooserView, handle, instance);
+
+ return m_fileChooserView;
+}
+
+QObject *InspectorControlView::showMeshChooser(int handle, int instance, const QPoint &point)
+{
+ m_meshChooserView->setHandle(handle);
+ m_meshChooserView->setInstance(instance);
+
+ m_activeBrowser.setData(m_meshChooserView, handle, instance);
+ int numPrimitives = BasicObjectsModel::BasicMeshesModel().count();
+ bool combo = numPrimitives == m_meshChooserView->numMeshes(); // make a combobox size popup
+ int comboH = qMin(m_meshChooserView->numMeshes(), 15) // max popup height: 15 items
+ * CStudioPreferences::controlBaseHeight();
+
+ CDialogs::showWidgetBrowser(this, m_meshChooserView, point,
+ CDialogs::WidgetBrowserAlign::ComboBox,
+ combo ? QSize(CStudioPreferences::valueWidth(), comboH) : QSize());
+
+ return m_meshChooserView;
+}
+
+QObject *InspectorControlView::showTextureChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_textureChooserView) {
+ m_textureChooserView = new TextureChooserView(this);
+ connect(m_textureChooserView, &TextureChooserView::textureSelected, this,
+ [this] (int handle, int instance, const QString &fileName) {
+ if (m_textureChooserView->currentDataModelPath() != fileName) {
+ QString renderableId = g_StudioApp.getRenderableId(fileName);
+ if (renderableId.isEmpty())
+ setPropertyValueFromFilename(instance, handle, fileName);
+ else
+ m_inspectorControlModel->setPropertyValue(instance, handle, renderableId);
+ }
+ });
+ }
+
+ m_textureChooserView->setHandle(handle);
+ m_textureChooserView->setInstance(instance);
+
+ CDialogs::showWidgetBrowser(this, m_textureChooserView, point);
+ m_activeBrowser.setData(m_textureChooserView, handle, instance);
+
+ return m_textureChooserView;
+}
+
+QObject *InspectorControlView::showObjectReference(int handle, int instance, const QPoint &point)
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ // different base handle than current active root instance means that we have entered/exited
+ // component after the reference model had been created, and we need to recreate it
+ if (!m_objectReferenceModel
+ || (m_objectReferenceModel->baseHandle() != doc->GetActiveRootInstance())) {
+ if (m_objectReferenceModel)
+ delete m_objectReferenceModel;
+ m_objectReferenceModel = new ObjectListModel(g_StudioApp.GetCore(),
+ doc->GetActiveRootInstance(), this, true);
+ }
+ if (!m_objectReferenceView)
+ m_objectReferenceView = new ObjectBrowserView(this);
+ m_objectReferenceView->setModel(m_objectReferenceModel);
+
+ if (doc->GetStudioSystem()->GetClientDataModelBridge()
+ ->GetObjectType(instance) == OBJTYPE_ALIAS) {
+ QVector<EStudioObjectType> exclude;
+ exclude << OBJTYPE_ALIAS << OBJTYPE_BEHAVIOR << OBJTYPE_CUSTOMMATERIAL
+ << OBJTYPE_EFFECT << OBJTYPE_GUIDE << OBJTYPE_IMAGE << OBJTYPE_LAYER
+ << OBJTYPE_MATERIAL << OBJTYPE_REFERENCEDMATERIAL << OBJTYPE_SCENE;
+ m_objectReferenceModel->excludeObjectTypes(exclude);
+ } else {
+ m_objectReferenceModel->excludeObjectTypes(QVector<EStudioObjectType>());
+ }
+
+ disconnect(m_objectReferenceView, nullptr, nullptr, nullptr);
+
+ IObjectReferenceHelper *objRefHelper = doc->GetDataModelObjectReferenceHelper();
+ if (objRefHelper) {
+ qt3dsdm::SValue value = m_inspectorControlModel->currentPropertyValue(instance, handle);
+ qt3dsdm::Qt3DSDMInstanceHandle refInstance = objRefHelper->Resolve(value, instance);
+ m_objectReferenceView->selectAndExpand(refInstance, instance);
+ }
+
+ CDialogs::showWidgetBrowser(this, m_objectReferenceView, point);
+ m_activeBrowser.setData(m_objectReferenceView, handle, instance);
+
+ connect(m_objectReferenceView, &ObjectBrowserView::selectionChanged,
+ this, [this, doc, handle, instance] {
+ auto selectedItem = m_objectReferenceView->selectedHandle();
+ qt3dsdm::SObjectRefType objRef = doc->GetDataModelObjectReferenceHelper()->GetAssetRefValue(
+ selectedItem, instance,
+ (CRelativePathTools::EPathType)(m_objectReferenceView->pathType()));
+ qt3dsdm::SValue value = m_inspectorControlModel->currentPropertyValue(instance, handle);
+ if (!(value.getData<qt3dsdm::SObjectRefType>() == objRef)) {
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(instance, handle, objRef);
+ }
+ });
+
+ return m_objectReferenceView;
+}
+
+QObject *InspectorControlView::showMaterialReference(int handle, int instance, const QPoint &point)
+{
+ // create the list widget
+ if (!m_matRefListWidget)
+ m_matRefListWidget = new MaterialRefView(this);
+
+ disconnect(m_matRefListWidget, &QListWidget::itemClicked, nullptr, nullptr);
+ disconnect(m_matRefListWidget, &QListWidget::itemDoubleClicked, nullptr, nullptr);
+
+ const QSize popupSize = m_matRefListWidget->refreshMaterials(instance, handle);
+ CDialogs::showWidgetBrowser(this, m_matRefListWidget, point,
+ CDialogs::WidgetBrowserAlign::ComboBox, popupSize);
+ m_activeBrowser.setData(m_matRefListWidget, handle, instance);
+
+ connect(m_matRefListWidget, &QListWidget::itemClicked, this,
+ [instance, handle](QListWidgetItem *item) {
+ auto selectedInstance = item->data(Qt::UserRole).toInt();
+ if (selectedInstance > 0) {
+ qt3dsdm::SValue value;
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+ propertySystem->GetInstancePropertyValue(instance, handle, value);
+ auto refInstance = doc->GetDataModelObjectReferenceHelper()->Resolve(value, instance);
+ if (selectedInstance != refInstance) {
+ auto objRef = doc->GetDataModelObjectReferenceHelper()->GetAssetRefValue(
+ selectedInstance, instance, CRelativePathTools::EPATHTYPE_GUID);
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(instance, handle, objRef);
+ }
+ }
+ });
+ connect(m_matRefListWidget, &QListWidget::itemDoubleClicked, this, [this]() {
+ m_matRefListWidget->hide();
+ });
+
+ return m_matRefListWidget;
+}
+
+void InspectorControlView::showDataInputChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_dataInputChooserView) {
+ const QVector<EDataType> acceptedTypes;
+ m_dataInputChooserView = new DataInputSelectView(acceptedTypes, this);
+ connect(m_dataInputChooserView, &DataInputSelectView::dataInputChanged, this,
+ [this](int handle, int instance, const QString &controllerName) {
+ bool controlled =
+ controllerName == m_dataInputChooserView->getNoneString() ? false : true;
+ m_inspectorControlModel
+ ->setPropertyControllerInstance(
+ instance, handle,
+ Q3DStudio::CString::fromQString(controllerName), controlled);
+ m_inspectorControlModel->setPropertyControlled(instance, handle);
+ });
+ }
+ const auto propertySystem =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetPropertySystem();
+ const qt3dsdm::DataModelDataType::Value dataType
+ = propertySystem->GetDataType(handle);
+ // only add datainputs with matching type for this property
+ QVector<QPair<QString, int>> dataInputList;
+
+ for (auto &it : qAsConst(g_StudioApp.m_dataInputDialogItems))
+ dataInputList.append({it->name, it->type});
+
+ m_dataInputChooserView->setMatchingTypes(CDataInputDlg::getAcceptedTypes(dataType));
+ m_dataInputChooserView->
+ setData(dataInputList,
+ m_inspectorControlModel->currentControllerValue(instance, handle),
+ handle, instance);
+ CDialogs::showWidgetBrowser(this, m_dataInputChooserView, point,
+ CDialogs::WidgetBrowserAlign::ToolButton);
+ m_activeBrowser.setData(m_dataInputChooserView, handle, instance);
+}
+
+QColor InspectorControlView::showColorDialog(const QColor &color, int instance, int handle)
+{
+ bool showAlpha = false;
+ if (instance && handle) {
+ auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()
+ ->GetClientDataModelBridge();
+ showAlpha = bridge->getBGColorProperty(instance).GetHandleValue() == handle
+ || bridge->getTextColorProperty(instance).GetHandleValue() == handle;
+ }
+
+ m_currentColor = color;
+ CDialogs *dialogs = g_StudioApp.GetDialogs();
+ connect(dialogs, &CDialogs::onColorChanged,
+ this, &InspectorControlView::dialogCurrentColorChanged);
+ QColor currentColor = dialogs->displayColorDialog(color, showAlpha);
+ disconnect(dialogs, &CDialogs::onColorChanged,
+ this, &InspectorControlView::dialogCurrentColorChanged);
+ return currentColor;
+}
+
+bool InspectorControlView::toolTipsEnabled()
+{
+ return CStudioPreferences::ShouldShowTooltips();
+}
+
+// Converts a path that is relative to the current presentation to be relative to
+// the current project root
+QString InspectorControlView::convertPathToProjectRoot(const QString &presentationPath)
+{
+ QDir projDir(g_StudioApp.GetCore()->getProjectFile().getProjectPath());
+ QFileInfo presentationFile(g_StudioApp.GetCore()->GetDoc()->GetDocumentPath());
+ QDir presentationDir(presentationFile.absolutePath());
+ QString absPath = presentationDir.absoluteFilePath(presentationPath);
+
+ return projDir.relativeFilePath(absPath);
+}
+
+void InspectorControlView::OnBeginDataModelNotifications()
+{
+}
+
+void InspectorControlView::OnEndDataModelNotifications()
+{
+ CInspectableBase *inspectable = m_inspectorControlModel->inspectable();
+ if (inspectable && !inspectable->isValid())
+ OnSelectionSet(Q3DStudio::SSelectedValue());
+ m_inspectorControlModel->refresh();
+
+ if (m_activeBrowser.isActive()) {
+ // Check if the instance/handle pair still has an active UI control. If not, close browser.
+ if (!m_inspectorControlModel->hasInstanceProperty(
+ m_activeBrowser.m_instance, m_activeBrowser.m_handle)) {
+ m_activeBrowser.clear();
+ } else {
+ // Update browser selection
+ if (m_activeBrowser.m_browser == m_imageChooserView) {
+ m_imageChooserView->updateSelection();
+ } else if (m_activeBrowser.m_browser == m_fileChooserView) {
+ m_fileChooserView->updateSelection();
+ } else if (m_activeBrowser.m_browser == m_meshChooserView) {
+ m_meshChooserView->updateSelection();
+ } else if (m_activeBrowser.m_browser == m_textureChooserView) {
+ m_textureChooserView->updateSelection();
+ } else if (m_activeBrowser.m_browser == m_objectReferenceView) {
+ IObjectReferenceHelper *objRefHelper
+ = g_StudioApp.GetCore()->GetDoc()->GetDataModelObjectReferenceHelper();
+ if (objRefHelper) {
+ qt3dsdm::SValue value = m_inspectorControlModel->currentPropertyValue(
+ m_activeBrowser.m_instance, m_activeBrowser.m_handle);
+ qt3dsdm::Qt3DSDMInstanceHandle refInstance
+ = objRefHelper->Resolve(value, m_activeBrowser.m_instance);
+ m_objectReferenceView->selectAndExpand(refInstance, m_activeBrowser.m_instance);
+ }
+ } else if (m_activeBrowser.m_browser == m_matRefListWidget) {
+ m_matRefListWidget->updateSelection();
+ } else if (m_activeBrowser.m_browser == m_dataInputChooserView) {
+ m_dataInputChooserView->setCurrentController(
+ m_inspectorControlModel->currentControllerValue(
+ m_dataInputChooserView->instance(),
+ m_dataInputChooserView->handle()));
+ }
+ }
+ }
+}
+
+void InspectorControlView::OnImmediateRefreshInstanceSingle(qt3dsdm::Qt3DSDMInstanceHandle inInstance)
+{
+ m_inspectorControlModel->refresh();
+}
+
+void InspectorControlView::OnImmediateRefreshInstanceMultiple(qt3dsdm::Qt3DSDMInstanceHandle *inInstance, long inInstanceCount)
+{
+ m_inspectorControlModel->refresh();
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.h
new file mode 100644
index 00000000..75d07d73
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.h
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INSPECTORCONTROLVIEW_H
+#define INSPECTORCONTROLVIEW_H
+
+#include <QtQuickWidgets/qquickwidget.h>
+#include <QtCore/qpointer.h>
+#include "DispatchListeners.h"
+#include "Dispatch.h"
+#include "Qt3DSFileTools.h"
+#include "TabOrderHandler.h"
+#include "MouseHelper.h"
+#include "QmlUtils.h"
+#include "DataInputSelectView.h"
+
+class InspectorControlModel;
+class VariantsGroupModel;
+class CInspectableBase;
+class ImageChooserView;
+class DataInputSelectView;
+class ImageChooserModel;
+class MeshChooserView;
+class ObjectBrowserView;
+class ObjectListModel;
+class FileChooserView;
+class TextureChooserView;
+class MaterialRefView;
+
+QT_FORWARD_DECLARE_CLASS(QAbstractItemModel)
+
+class InspectorControlView : public QQuickWidget,
+ public CPresentationChangeListener,
+ public IDataModelListener,
+ public TabNavigable
+{
+ Q_OBJECT
+ Q_PROPERTY(QString titleText READ titleText NOTIFY titleChanged FINAL)
+ Q_PROPERTY(QString titleIcon READ titleIcon NOTIFY titleChanged FINAL)
+
+public:
+ explicit InspectorControlView(const QSize &preferredSize, QWidget *parent = nullptr);
+ ~InspectorControlView() override;
+
+ void OnSelectionSet(Q3DStudio::SSelectedValue inValue);
+ QAbstractItemModel *inspectorControlModel() const;
+
+ QString titleText() const;
+ QString titleIcon() const;
+ VariantsGroupModel *variantsModel() const { return m_variantsGroupModel; }
+
+ Q_INVOKABLE QColor titleColor(int instance = 0, int handle = 0) const;
+ Q_INVOKABLE QColor showColorDialog(const QColor &color, int instance = 0, int handle = 0);
+ Q_INVOKABLE void showContextMenu(int x, int y, int handle, int instance);
+ Q_INVOKABLE void showTagContextMenu(int x, int y, const QString &group, const QString &tag);
+ Q_INVOKABLE void showDataInputChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE void showGroupContextMenu(int x, int y, const QString &group);
+ Q_INVOKABLE QObject *showImageChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showFilesChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showMeshChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showObjectReference(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showMaterialReference(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showTextureChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE bool toolTipsEnabled();
+ Q_INVOKABLE bool isRefMaterial(int instance) const;
+ Q_INVOKABLE bool isEditable(int handle) const;
+ Q_INVOKABLE QString convertPathToProjectRoot(const QString &presentationPath);
+ Q_INVOKABLE QString noneString() const;
+
+ // IDataModelListener
+ void OnBeginDataModelNotifications() override;
+ void OnEndDataModelNotifications() override;
+ void OnImmediateRefreshInstanceSingle(qt3dsdm::Qt3DSDMInstanceHandle inInstance) override;
+ void OnImmediateRefreshInstanceMultiple(qt3dsdm::Qt3DSDMInstanceHandle *inInstance,
+ long inInstanceCount) override;
+
+Q_SIGNALS:
+ void titleChanged();
+ void controlsChanged();
+ void imageSelected(const QString &name);
+ void dialogCurrentColorChanged(const QColor &newColor);
+
+public Q_SLOTS:
+ void toggleMasterLink();
+
+protected:
+ QSize sizeHint() const override;
+ void mousePressEvent(QMouseEvent *event) override;
+
+private:
+ void setInspectable(CInspectableBase *inInspectable);
+ void initialize();
+ void onFilesChanged(const Q3DStudio::TFileModificationList &inFileModificationList);
+ void OnNewPresentation() override;
+ void OnClosingPresentation() override;
+ void filterMaterials(std::vector<Q3DStudio::CFilePath> &materials);
+ void filterMatDatas(std::vector<Q3DStudio::CFilePath> &matDatas);
+ void setPropertyValueFromFilename(long instance, int handle, const QString &name);
+ CInspectableBase *createInspectableFromSelectable(Q3DStudio::SSelectedValue selectable);
+ bool canLinkProperty(int instance, int handle) const;
+ bool canOpenInInspector(int instance, int handle) const;
+ void openInInspector();
+ void onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance,
+ qt3dsdm::Qt3DSDMPropertyHandle inProperty);
+ void onChildAdded(int inChild);
+ void onChildRemoved();
+
+ std::vector<std::shared_ptr<qt3dsdm::ISignalConnection>> m_connections;
+ QColor m_backgroundColor;
+ VariantsGroupModel *m_variantsGroupModel = nullptr;
+ InspectorControlModel *m_inspectorControlModel = nullptr;
+ CInspectableBase *m_inspectableBase = nullptr;
+ QPointer<ImageChooserView> m_imageChooserView;
+ QPointer<MeshChooserView> m_meshChooserView;
+ QPointer<FileChooserView> m_fileChooserView;
+ QPointer<TextureChooserView> m_textureChooserView;
+ QPointer<ObjectBrowserView> m_objectReferenceView;
+ QPointer<MaterialRefView> m_matRefListWidget;
+ QPointer<ObjectListModel> m_objectReferenceModel;
+ QPointer<DataInputSelectView> m_dataInputChooserView;
+ std::vector<Q3DStudio::CFilePath> m_fileList;
+ MouseHelper m_mouseHelper;
+ QmlUtils m_qmlUtils;
+
+ int m_contextMenuInstance = 0;
+ int m_contextMenuHandle = 0;
+
+ QSize m_preferredSize;
+ QColor m_currentColor;
+
+ class ActiveBrowserData
+ {
+ public:
+ void setData(QWidget *browser, int handle, int instance)
+ {
+ m_browser = browser;
+ m_handle = handle;
+ m_instance = instance;
+ }
+ void clear()
+ {
+ if (isActive())
+ m_browser->close();
+ m_browser.clear();
+ m_handle = -1;
+ m_instance = -1;
+ }
+ bool isActive() const
+ {
+ return !m_browser.isNull() && m_browser->isVisible();
+ }
+
+ QPointer<QWidget> m_browser = nullptr;
+ int m_handle = -1;
+ int m_instance = -1;
+ };
+
+ ActiveBrowserData m_activeBrowser;
+};
+
+#endif // INSPECTORCONTROLVIEW_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.qml
new file mode 100644
index 00000000..c291de7b
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.qml
@@ -0,0 +1,1289 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.8
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.2
+import QtQuick.Controls.Styles 1.4
+import QtQuick.Extras 1.4
+
+import Qt3DStudio 1.0
+import "../controls"
+import "../Action"
+
+Rectangle {
+ id: root
+ color: _backgroundColor
+
+ Connections {
+ target: _inspectorModel
+ onModelAboutToBeReset: {
+ _tabOrderHandler.clear();
+ inspectorToolbar.model = null;
+ if (_inspectorModel.isDefaultMaterial())
+ inspectorToolbar.model = defaultMaterialToolbarModel;
+ else if (_inspectorModel.isMaterial())
+ inspectorToolbar.model = materialToolbarModel;
+ inspectorToolbar.visible = inspectorToolbar.model !== null;
+ }
+ }
+
+ MouseArea {
+ anchors.fill: controlArea
+ onPressed: {
+ mouse.accepted = false
+ focusEater.forceActiveFocus();
+ }
+ }
+
+ ColumnLayout {
+ id: controlArea
+ anchors.fill: parent
+ anchors.topMargin: 4
+ spacing: 8
+
+ Item {
+ id: focusEater
+ // Used to eat keyboard focus when user clicks outside any property control
+ }
+
+ ListModel {
+ id: materialToolbarModel
+
+ ListElement {
+ image: "add.png"
+ name: qsTr("New")
+ inUse: true
+ }
+
+ ListElement {
+ image: "add.png"
+ name: qsTr("Duplicate")
+ inUse: true
+ }
+
+ property var actions: [
+ function(){ _inspectorModel.addMaterial(); },
+ function(){ _inspectorModel.duplicateMaterial(); }
+ ]
+ }
+
+ ListModel {
+ id: defaultMaterialToolbarModel
+
+ ListElement {
+ image: "add.png"
+ name: qsTr("New")
+ inUse: true
+ }
+
+ ListElement {
+ image: "add-disabled.png"
+ name: qsTr("Duplicate")
+ inUse: false
+ }
+
+ property var actions: [
+ function(){ _inspectorModel.addMaterial(); }
+ ]
+ }
+
+ ListView {
+ id: inspectorToolbar
+ model: null
+ visible: false
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: 32
+ orientation: ListView.Horizontal
+
+ spacing: 4
+
+ delegate: ToolButton {
+ id: control
+ enabled: inUse
+
+ onClicked: {
+ inspectorToolbar.model.actions[index]();
+ }
+
+ background: Rectangle {
+ color: control.pressed ? _selectionColor : (hovered ? _studioColor1 : "transparent")
+ border.color: _studioColor1
+ }
+
+ contentItem: RowLayout {
+ Image {
+ source: _resDir + image
+ }
+ StyledLabel {
+ text: name
+ Layout.preferredWidth: -1
+ color: control.enabled ? _textColor : _disabledColor
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ height: _controlBaseHeight + 8
+ Layout.leftMargin: 4
+
+ Image {
+ id: headerImage
+ source: _parentView.titleIcon !== "" ? _resDir + _parentView.titleIcon : ""
+ }
+
+ StyledLabel {
+ text: _parentView.titleText
+ color: _parentView.titleColor()
+ }
+ }
+
+ ListView {
+ id: groupElements
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.bottomMargin: 10
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {
+ visible: size < 1.0
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ z: -10
+ onPressed: {
+ mouse.accepted = false
+ focusEater.forceActiveFocus();
+ }
+ }
+
+ model: _inspectorModel
+ delegate: Rectangle {
+ id: delegateItem
+
+ property int indexOfThisDelegate: index
+
+ width: parent.width
+ height: items.height
+ color: "transparent";
+ ListView.delayRemove: true
+
+ readonly property var values: model.values
+
+ Column {
+ id: items
+
+ x: 10
+ width: parent.width - x
+ spacing: 4
+
+ Rectangle { // group header
+ x: -10
+ width: delegateItem.width
+ height: 25
+ color: _inspectorGroupHeaderColor
+
+ StyledLabel {
+ x: 30
+ text: model.title
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Image {
+ id: collapseButton
+ x: 10
+ anchors.verticalCenter: parent.verticalCenter
+ source: {
+ _resDir + (groupItems.visible ? "arrow_down.png" : "arrow.png")
+ }
+ }
+
+ MouseArea {
+ id: collapseButtonMouseArea
+ anchors.fill: parent
+ onClicked: {
+ if (mouse.button === Qt.LeftButton) {
+ groupItems.visible = !groupItems.visible;
+ _inspectorModel.updateGroupCollapseState(indexOfThisDelegate,
+ !groupItems.visible)
+ }
+ }
+ }
+ }
+
+ Column { // properties in a group
+ spacing: 4
+ id: groupItems
+
+ visible: !_inspectorModel.isGroupCollapsed(indexOfThisDelegate)
+
+ Repeater {
+ model: delegateItem.values
+
+ onItemAdded: {
+ if (index === 0)
+ _tabOrderHandler.clearGroup(indexOfThisDelegate);
+ if (item.loadedItem.tabItem1 !== undefined) {
+ _tabOrderHandler.addItem(indexOfThisDelegate,
+ item.loadedItem.tabItem1)
+ if (item.loadedItem.tabItem2 !== undefined) {
+ _tabOrderHandler.addItem(
+ indexOfThisDelegate,
+ item.loadedItem.tabItem2)
+ if (item.loadedItem.tabItem3 !== undefined) {
+ _tabOrderHandler.addItem(
+ indexOfThisDelegate,
+ item.loadedItem.tabItem3)
+ }
+ }
+ }
+ }
+
+ RowLayout { // a property row
+ id: groupDelegateItem
+ spacing: 0
+ enabled: _parentView.isEditable(modelData.handle)
+
+ property alias loadedItem: loader.item
+
+ function showContextMenu(coords) {
+ _parentView.showContextMenu(
+ coords.x, coords.y,
+ model.modelData.handle,
+ model.modelData.instance);
+ // Refresh text; title color is wrong after this
+ propertyRow.color = _parentView.titleColor(
+ modelData.instance, modelData.handle);
+ }
+
+ ColumnLayout { // Property row and datainput control
+ Layout.alignment: Qt.AlignTop
+ visible: modelData.title !== "variants"
+ spacing: 0
+ RowLayout { // Property row
+ Layout.alignment: Qt.AlignLeft
+ Rectangle { // Property animation control button
+ width: 16
+ height: 16
+ color: animateButtonMouseArea.containsMouse
+ ? _studioColor1 : _backgroundColor
+
+ Image {
+ id: animatedPropertyButton
+ visible: model.modelData.animatable
+
+ property bool animated: model.modelData.animated
+
+ anchors.fill: parent
+ fillMode: Image.Pad
+
+ source: {
+ _resDir + (animated
+ ? "Inspector-AnimateToggle-Active.png"
+ : "Inspector-AnimateToggle-Normal.png")
+ }
+
+ MouseArea {
+ id: animateButtonMouseArea
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton | Qt.LeftButton
+ hoverEnabled: true
+ onClicked: {
+ if (mouse.button === Qt.LeftButton) {
+ _inspectorModel.setPropertyAnimated(
+ model.modelData.instance,
+ model.modelData.handle,
+ !model.modelData.animated)
+ } else {
+ const coords = mapToItem(root,
+ mouse.x,
+ mouse.y);
+ groupDelegateItem.showContextMenu(coords);
+ }
+ }
+ }
+ StyledTooltip {
+ text: qsTr("Enable animation")
+ enabled: animateButtonMouseArea.containsMouse
+ }
+ }
+ }
+
+ Rectangle { // Datainput control button
+ width: 16
+ height: 16
+ color: dataInputButtonMouseArea.containsMouse
+ ? _studioColor1 : _backgroundColor
+
+ Image {
+ id: ctrldPropButton
+
+ property bool controlled: model.modelData.controlled
+ visible: model.modelData.controllable
+ anchors.fill: parent
+ fillMode: Image.Pad
+
+ source: {
+ _resDir + (controlled
+ ? "Objects-DataInput-Active.png"
+ : "Objects-DataInput-Inactive.png")
+ }
+
+ MouseArea {
+ id: dataInputButtonMouseArea
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ hoverEnabled: true
+ onClicked: {
+ _parentView.showDataInputChooser(
+ model.modelData.handle,
+ model.modelData.instance,
+ mapToGlobal(
+ ctrldPropButton.x
+ + ctrldPropButton.width,
+ ctrldPropButton.y
+ + ctrldPropButton.height));
+
+ }
+ }
+
+ StyledTooltip {
+ text: model.modelData.controlled
+ ? qsTr("Data Input controller:\n")
+ + model.modelData.controller
+ : qsTr("Set Data Input controller")
+ enabled: dataInputButtonMouseArea.containsMouse
+ }
+ }
+ }
+
+ StyledLabel { // Property label
+ id: propertyRow
+
+ readonly property var modelData: model.modelData
+ text: model.modelData.title
+ // Color picked from DataInput icon
+ color: model.modelData.controlled?
+ _dataInputColor
+ : _parentView.titleColor(modelData.instance,
+ modelData.handle)
+
+ Layout.alignment: Qt.AlignTop
+
+ MouseArea {
+ id: propertyLabelMouseArea
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton
+ hoverEnabled: true
+ onClicked: {
+ const coords = mapToItem(root, mouse.x, mouse.y);
+ groupDelegateItem.showContextMenu(coords);
+ }
+ }
+ StyledTooltip {
+ id: valueToolTip
+ text: modelData.toolTip
+ enabled: propertyLabelMouseArea.containsMouse
+ }
+ }
+ }
+ }
+
+ Loader {
+ id: loader
+ readonly property var modelData: propertyRow.modelData
+ enabled: modelData.enabled
+ opacity: enabled ? 1 : .5
+ Layout.alignment: Qt.AlignTop
+ sourceComponent: {
+ if (modelData.title === "variants")
+ return variantTagsComponent;
+
+ const dataType = modelData.dataType;
+ switch (dataType) {
+ case DataModelDataType.Long:
+ if (modelData.propertyType ===
+ AdditionalMetaDataType.ShadowMapResolution) {
+ return shadowResolutionComponent;
+ }
+ if (modelData.propertyType === AdditionalMetaDataType.Range)
+ return intSliderComponent;
+ console.warn("KDAB_TODO: implement handler for type \"Long\", property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.Long4:
+ if (modelData.propertyType === AdditionalMetaDataType.Image)
+ return imageChooser;
+ console.warn("KDAB_TODO: implement handler for type \"long4\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.ObjectRef:
+ if (modelData.propertyType === AdditionalMetaDataType.ObjectRef) {
+ return _parentView.isRefMaterial(modelData.instance)
+ ? materialReference
+ : objectReference;
+ }
+ console.warn("KDAB_TODO: implement handler for type: \"objectref\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.StringOrInt:
+ //TODO: Maybe do some further check if the right combo is used
+ if (modelData.propertyType === AdditionalMetaDataType.StringList)
+ return slideSelectionDropDown;
+ console.warn("KDAB_TODO: (String) implement handler for type \"stringOrInt\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.String:
+ if (modelData.propertyType === AdditionalMetaDataType.Import)
+ return fileChooser;
+ if (modelData.propertyType === AdditionalMetaDataType.StringList)
+ return comboDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.Renderable)
+ return renderableDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.Mesh)
+ return meshChooser;
+ if (modelData.propertyType === AdditionalMetaDataType.MultiLine)
+ return multiLine;
+ if (modelData.propertyType === AdditionalMetaDataType.Font)
+ return fontDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.Texture)
+ return textureChooser;
+ if (modelData.propertyType === AdditionalMetaDataType.String)
+ return editLine;
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return null;
+ console.warn("KDAB_TODO: (String) implement handler for type \"string\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.Bool:
+ return checkBox;
+ case DataModelDataType.Float:
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return valueComponent;
+ if (modelData.propertyType === AdditionalMetaDataType.Range)
+ return sliderComponent;
+ if (modelData.propertyType === AdditionalMetaDataType.FontSize)
+ return fontSizeComponent;
+ console.warn("KDAB_TODO: implement handler for type\"float\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.Float2:
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return xyPropertyComponent;
+ console.warn("TODO: implement handler for type:\"float2\" property:",
+ modelData.propertyType, "text ",
+ model.modelData.title);
+ return null;
+ case DataModelDataType.Float3:
+ if (modelData.propertyType === AdditionalMetaDataType.Rotation)
+ return xyzPropertyComponent;
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return xyzPropertyComponent;
+ console.warn("KDAB_TODO: implement handler for type:\"float3\" property:",
+ modelData.propertyType, "text ",
+ model.modelData.title);
+ return null;
+ case DataModelDataType.Float4:
+ if (modelData.propertyType === AdditionalMetaDataType.Color)
+ return colorBox;
+ return null;
+ case DataModelDataType.StringRef:
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return materialTypeDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.Renderable)
+ return shaderDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.ObjectRef)
+ return matDataDropDown;
+ console.warn("KDAB_TODO: implement handler for type:\"StringRef\" text ",
+ model.modelData.title);
+ return null;
+ default:
+ console.warn("KDAB_TODO: implement handler for type",
+ dataType, "property",
+ modelData.propertyType, "text ",
+ model.modelData.title);
+ }
+ return null;
+ }
+ }
+ }
+ }
+ }
+
+ Column {
+ visible: model.info.length > 0
+ StyledLabel {
+ width: groupElements.width
+ horizontalAlignment: Text.AlignHCenter
+ text: model.info;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: editLine
+
+ StyledTextField {
+ id: textArea
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant value: parent.modelData.value
+ property Item tabItem1: this
+ width: _valueWidth
+ height: _controlBaseHeight
+ horizontalAlignment: TextInput.AlignLeft
+ verticalAlignment: TextInput.AlignVCenter
+
+ // Don't just bind text to value, since changing text explicitly in onEditingFinished
+ // would break binding
+ onValueChanged: text = value
+
+ onTextChanged: _inspectorModel.setPropertyValue(instance, handle, text, false)
+
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(instance, handle, text, true);
+ // Ensure we update the text to current value also in cases where making name
+ // unique results in no change to model value
+ text = value;
+ }
+ }
+ }
+
+ Component {
+ id: multiLine
+
+ HandlerBaseMultilineText {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ width: _valueWidth
+ height: _controlBaseHeight * 3
+ value: parent.modelData.value
+
+ onTextChanged: _inspectorModel.setPropertyValue(instance, handle, value, false)
+ onEditingFinished: _inspectorModel.setPropertyValue(instance, handle, value, true)
+ }
+ }
+
+ Component {
+ id: meshChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _parentView.showMeshChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: imageChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: {
+ var renderableId = _inspectorModel.renderableId(parent.modelData.value);
+ renderableId === "" ? parent.modelData.value : renderableId;
+ }
+ onShowBrowser: {
+ activeBrowser = _parentView.showImageChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: fileChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: {
+ parent.modelData.value === "" ? _parentView.noneString()
+ : _parentView.convertPathToProjectRoot(
+ parent.modelData.value)
+ }
+ onShowBrowser: {
+ activeBrowser = _parentView.showFilesChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: textureChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _parentView.showTextureChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: xyzPropertyComponent
+
+ RowLayout {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ property alias tabItem1: xyzHandler.tabItem1
+ property alias tabItem2: xyzHandler.tabItem2
+ property alias tabItem3: xyzHandler.tabItem3
+ spacing: 0
+
+ onValuesChanged: {
+ // FloatTextField can set its text internally, thus breaking the binding, so
+ // let's set the text value explicitly each time value changes
+ xyzHandler.valueX = Number(values[0]).toFixed(xyzHandler.numberOfDecimal);
+ xyzHandler.valueY = Number(values[1]).toFixed(xyzHandler.numberOfDecimal);
+ xyzHandler.valueZ = Number(values[2]).toFixed(xyzHandler.numberOfDecimal);
+ }
+
+ HandlerPropertyBaseXYZ {
+ id: xyzHandler
+ valueX: Number(values[0]).toFixed(numberOfDecimal)
+ valueY: Number(values[1]).toFixed(numberOfDecimal)
+ valueZ: Number(values[2]).toFixed(numberOfDecimal)
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle,
+ Qt.vector3d(valueX, valueY, valueZ), true);
+ }
+ onPreviewValueChanged: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle,
+ Qt.vector3d(valueX, valueY, valueZ), false);
+ }
+ }
+ }
+ }
+
+ Component {
+ id: xyPropertyComponent
+
+ RowLayout {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ property alias tabItem1: xyHandler.tabItem1
+ property alias tabItem2: xyHandler.tabItem2
+ spacing: 0
+
+ onValuesChanged: {
+ // FloatTextField can set its text internally, thus breaking the binding, so
+ // let's set the text value explicitly each time value changes
+ xyHandler.valueX = Number(values[0]).toFixed(xyHandler.numberOfDecimal);
+ xyHandler.valueY = Number(values[1]).toFixed(xyHandler.numberOfDecimal);
+ }
+
+ HandlerPropertyBaseXY {
+ id: xyHandler
+ valueX: Number(values[0]).toFixed(numberOfDecimal)
+ valueY: Number(values[1]).toFixed(numberOfDecimal)
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle,
+ Qt.vector2d(valueX, valueY), true);
+ }
+ onPreviewValueChanged: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle,
+ Qt.vector2d(valueX, valueY), false);
+ }
+ }
+ }
+ }
+
+ Component {
+ id: valueComponent
+
+ RowLayout {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property real value: Number(parent.modelData.value).toFixed(floatField.decimalValue)
+ property Item tabItem1: floatField
+
+ onValueChanged: {
+ // FloatTextField can set its text internally, thus breaking the binding, so
+ // let's set the text value explicitly each time value changes
+ floatField.text = Number(value).toFixed(floatField.decimalValue);
+ }
+
+ spacing: 0
+
+ FloatTextField {
+ id: floatField
+ text: Number(parent.value).toFixed(decimalValue)
+ implicitHeight: _controlBaseHeight
+ implicitWidth: _valueWidth / 3
+
+ onPreviewValueChanged: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle,
+ Number(text), false);
+ }
+
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle,
+ Number(text), true);
+ }
+ }
+ }
+ }
+
+ Component {
+ id: sliderComponent
+
+ HandlerPropertyBaseSlider {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+
+ value: parent.modelData.value
+ sliderMin: values[0]
+ sliderMax: values[1]
+ sliderDecimals: values[2]
+
+ onCommitValue: _inspectorModel.setPropertyValue(instance, handle, desiredValue, true)
+ onPreviewValue: _inspectorModel.setPropertyValue(instance, handle, desiredValue, false)
+ }
+ }
+
+ Component {
+ id: comboDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex)
+ if (value !== newValue && currentIndex !== -1)
+ _inspectorModel.setPropertyValue(instance, handle, newValue)
+ }
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+
+ Component {
+ id: fontDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+ property bool blockIndexChange: false
+
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex)
+ if (!blockIndexChange && value !== newValue && currentIndex !== -1)
+ _inspectorModel.setPropertyValue(instance, handle, newValue)
+ }
+ onValueChanged: {
+ var newNewIndex = find(value);
+ if (!blockIndexChange || newNewIndex > 0)
+ currentIndex = newNewIndex;
+ blockIndexChange = false;
+ }
+ onValuesChanged : {
+ // Changing the values list will reset the currentIndex to zero, so block setting
+ // the actual font. We'll get the proper index right after.
+ if (currentIndex > 0)
+ blockIndexChange = true;
+ }
+ }
+ }
+
+ Component {
+ id: slideSelectionDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ var newIndex = find(value)
+ if (newIndex === -1)
+ newIndex = find(value + "|separator")
+ currentIndex = newIndex
+ }
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex).replace("|separator", "")
+ if (value !== newValue && currentIndex !== -1) {
+ _inspectorModel.setSlideSelection(instance, handle,
+ currentIndex, values)
+ }
+ }
+ onValueChanged: {
+ var newIndex = find(value)
+ if (newIndex === -1)
+ newIndex = find(value + "|separator")
+ currentIndex = newIndex
+ }
+ }
+ }
+
+ Component {
+ id: materialTypeDropDown
+
+ MaterialDropDown {
+ callback: _inspectorModel.setMaterialTypeValue
+ }
+ }
+
+ Component {
+ id: shaderDropDown
+
+ MaterialDropDown {
+ callback: _inspectorModel.setShaderValue
+ }
+ }
+
+ Component {
+ id: matDataDropDown
+
+ MaterialDropDown {
+ callback: _inspectorModel.setMatDataValue
+ }
+ }
+
+ Component {
+ id: renderableDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+ model: values
+
+ showArrow: enabled
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ // Disable for non-layer
+ enabled = _inspectorModel.isLayer(instance);
+ currentIndex = find(value);
+ }
+
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex)
+ if (value !== newValue && currentIndex !== -1)
+ _inspectorModel.setRenderableValue(instance, handle, newValue)
+ }
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+
+ Component {
+ id: checkBox
+
+ Item {
+ id: checkboxItem
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property bool checked: parent.modelData.value
+
+ width: 16
+ height: _controlBaseHeight
+ Image {
+ anchors.fill: parent
+ fillMode: Image.Pad
+ source: (_resDir + (checked ? "checkbox-checked.png" : "checkbox-unchecked.png"))
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: _inspectorModel.setPropertyValue(checkboxItem.instance,
+ checkboxItem.handle,
+ !checkboxItem.checked)
+ }
+ }
+ }
+ }
+
+ Component {
+ id: colorBox
+
+ HandlerGenericBaseColor {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+
+ color: parent.modelData.value
+ onColorSelected: _inspectorModel.setPropertyValue(instance, handle, selectedColor);
+ onPreviewColorSelected: _inspectorModel.setPropertyValue(instance, handle,
+ selectedColor, false);
+ }
+ }
+
+ Component {
+ id: objectReference
+
+ HandlerGenericChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _parentView.showObjectReference(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: materialReference
+
+ HandlerGenericChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _parentView.showMaterialReference(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: intSliderComponent
+
+ HandlerPropertyBaseSlider {
+ intSlider: true;
+ property int intValue: Math.round(desiredValue)
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ sliderMin: values[0]
+ sliderMax: values[1]
+
+ onCommitValue: _inspectorModel.setPropertyValue(instance, handle, intValue, true)
+ onPreviewValue: _inspectorModel.setPropertyValue(instance, handle, intValue, false)
+ }
+ }
+
+ Component {
+ id: fontSizeComponent
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property real value: parent.modelData.value
+
+ editable: true
+ property bool ready: false
+
+ validator: IntValidator {
+ bottom: 1
+ }
+
+ model: ["8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", "28",
+ "36", "48", "72", "96", "120", "160", "200"]
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ editText = value;
+ ready = true;
+ }
+
+ onValueChanged: {
+ if (ready && !isNaN(value))
+ editText = value;
+ }
+
+ onEditTextChanged: {
+ if (ready) {
+ var newvalue = parseInt(editText);
+ _inspectorModel.setPropertyValue(instance, handle, newvalue, false);
+ }
+ }
+
+ onActiveFocusChanged: {
+ if (!activeFocus) {
+ var newvalue = parseInt(editText);
+ _inspectorModel.setPropertyValue(instance, handle, newvalue);
+ }
+ }
+
+ }
+ }
+
+ Component {
+ id: shadowResolutionComponent
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var value: parent.modelData.value
+ property int newValue
+
+ model: ["8", "9", "10", "11"]
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+
+ onCurrentIndexChanged: {
+ newValue = parseInt(textAt(currentIndex))
+ if (value !== newValue && currentIndex !== -1)
+ _inspectorModel.setPropertyValue(instance, handle, newValue)
+ }
+
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+
+ Component {
+ id: variantTagsComponent
+
+ Column {
+ width: root.width - 10
+ spacing: 10
+
+ Row {
+ anchors.right: parent.right
+ anchors.rightMargin: 5
+ spacing: 5
+
+ ToolButton {
+ id: importButton
+ text: qsTr("Import...")
+ font.pixelSize: _fontSize
+ width: 70
+ height: 20
+
+ onClicked: {
+ _variantsGroupModel.importVariants()
+ }
+ }
+
+ ToolButton {
+ id: exportButton
+ text: qsTr("Export...")
+ font.pixelSize: _fontSize
+ width: 70
+ height: 20
+ enabled: !_variantsGroupModel.variantsEmpty
+
+ onClicked: {
+ _variantsGroupModel.exportVariants()
+ }
+ }
+ }
+
+ Text {
+ text: qsTr("There are no variant tags yet. Click [+ Group] to add a new tags group and start adding tags.")
+ font.pixelSize: _fontSize
+ color: _textColor
+ visible: _variantsGroupModel.variantsEmpty
+ width: parent.width
+ wrapMode: Text.WordWrap
+ rightPadding: 5
+ }
+
+ Repeater {
+ id: tagsRepeater
+ model: _variantsGroupModel
+ property int maxGroupLabelWidth;
+
+ onItemAdded: {
+ // make all group labels have equal width as the widest one
+ if (index == 0)
+ maxGroupLabelWidth = 20; // min group label width
+
+ if (item.groupLabelWidth > maxGroupLabelWidth) {
+ maxGroupLabelWidth = item.groupLabelWidth;
+
+ if (maxGroupLabelWidth > 150) // max group label width
+ maxGroupLabelWidth = 150;
+ }
+ }
+
+ Row {
+ id: variantTagsRow
+ spacing: 5
+
+ readonly property var tagsModel: model.tags
+ readonly property var groupModel: model
+ readonly property int groupLabelWidth: tLabel.implicitWidth + 10
+
+ Text {
+ id: tLabel
+ text: model.group
+ color: model.color
+ font.pixelSize: _fontSize
+ width: tagsRepeater.maxGroupLabelWidth;
+ elide: Text.ElideRight
+ anchors.top: parent.top
+ anchors.topMargin: 5
+
+ MouseArea {
+ anchors.fill: parent;
+ acceptedButtons: Qt.RightButton
+ onClicked: {
+ if (mouse.button === Qt.RightButton) {
+ const coords = mapToItem(root, mouse.x, mouse.y);
+ _parentView.showGroupContextMenu(coords.x, coords.y, model.group);
+ }
+ }
+ }
+ }
+
+ Flow {
+ width: root.width - 110
+ spacing: 5
+
+ Repeater {
+ model: tagsModel
+
+ Loader {
+ readonly property var tagsModel: model
+ readonly property var grpModel: groupModel
+ sourceComponent: tagComponent
+ }
+ }
+
+ ToolButton {
+ id: addTagButton
+ text: qsTr("+ Tag")
+ font.pixelSize: _fontSize
+ height: 25
+
+ onClicked: {
+ _variantsGroupModel.addNewTag(groupModel.group)
+ }
+
+ }
+ }
+ }
+ }
+
+ Item { width: 1; height: 5 } // vertical spacer
+
+ ToolButton {
+ id: addGroupButton
+ text: qsTr("+ Group")
+ font.pixelSize: _fontSize
+ width: 65
+ height: 25
+ onClicked: {
+ _variantsGroupModel.addNewGroup()
+ }
+ }
+
+ Item { width: 1; height: 5 } // vertical spacer
+ }
+ }
+
+ Component {
+ id: tagComponent
+
+ Rectangle {
+ property bool toggled: tagsModel ? tagsModel.selected : false
+ property color grpColor: grpModel ? grpModel.color : ""
+ property bool isBright: grpModel ? _utils.isBright(grpColor) : false
+
+ width: Math.max(tLabel.width + 10, 60)
+ height: 25
+ color: toggled ? grpColor : _backgroundColor
+ border.color: _studioColor4
+
+ Text {
+ id: tLabel
+ anchors.centerIn: parent
+ text: tagsModel ? tagsModel.tag : ""
+ font.pixelSize: _fontSize
+ color: toggled ? (isBright ? _studioColor1 : _textColor) : _studioColor4
+ }
+
+ MouseArea {
+ anchors.fill: parent;
+ acceptedButtons: Qt.RightButton | Qt.LeftButton
+ onClicked: {
+ if (mouse.button === Qt.LeftButton) {
+ toggled = !toggled;
+ _variantsGroupModel.setTagState(grpModel.group, tagsModel.tag, toggled);
+ } else if (mouse.button === Qt.RightButton) {
+ const coords = mapToItem(root, mouse.x, mouse.y);
+ _parentView.showTagContextMenu(coords.x, coords.y, grpModel.group,
+ tagsModel.tag);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.cpp
new file mode 100644
index 00000000..84967f4c
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.cpp
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "InspectorGroup.h"
+
+CInspectorGroup::CInspectorGroup(const QString &inName)
+{
+ m_name = inName;
+}
+
+CInspectorGroup::~CInspectorGroup()
+{
+}
+
+QString CInspectorGroup::GetName() const
+{
+ return m_name;
+}
+
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.h
new file mode 100644
index 00000000..18f56d79
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorGroup.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_INSPECTOR_GROUP_H
+#define INCLUDED_INSPECTOR_GROUP_H
+
+#include <QString>
+
+/**
+ * This is the base class for inspector groups.
+ *
+ * Derive from this class in order to create a new group for the inspector palette.
+ */
+class CInspectorGroup
+{
+public:
+ CInspectorGroup(const QString &inName = {});
+ virtual ~CInspectorGroup();
+
+ QString GetName() const;
+
+protected:
+ QString m_name;
+};
+
+#endif // INCLUDED_INSPECTOR_GROUP_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialDropDown.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialDropDown.qml
new file mode 100644
index 00000000..88de98c5
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialDropDown.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.0
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.2
+
+import Qt3DStudio 1.0
+import "../controls"
+import "../Action"
+
+StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+ property bool blockIndexChange: false
+ property var callback
+
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex)
+ if (!blockIndexChange && value !== newValue && currentIndex !== -1)
+ callback(instance, handle, newValue)
+ }
+ onValueChanged: {
+ var newNewIndex = find(value);
+ if (!blockIndexChange || newNewIndex > 0)
+ currentIndex = newNewIndex;
+ blockIndexChange = false;
+ }
+ onValuesChanged : {
+ // Changing the values list will reset the currentIndex to zero, so block setting
+ // the actual material. We'll get the proper index right after.
+ if (currentIndex > 0)
+ blockIndexChange = true;
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.cpp
new file mode 100644
index 00000000..b2d1a595
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "MaterialRefView.h"
+#include "StudioApp.h"
+#include "StudioPreferences.h"
+#include "Doc.h"
+#include "Core.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "IObjectReferenceHelper.h"
+#include "IDocumentEditor.h"
+
+#include <QtCore/qtimer.h>
+
+MaterialRefView::MaterialRefView(QWidget *parent)
+: QListWidget(parent)
+{
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QListWidget::Fixed);
+}
+
+/**
+ * Gather and display the currently used standard and custom material list
+ *
+ * @param instance The instance that owns the property handle
+ * @param handle The property handle this materials list is for
+ *
+ * @return necessary size for the dialog
+ */
+QSize MaterialRefView::refreshMaterials(int instance, int handle)
+{
+ clear(); // clear old material list
+
+ m_instance = instance;
+ m_handle = handle;
+ qt3dsdm::Qt3DSDMInstanceHandle refInstance = getRefInstance();
+
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+ auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ static const QPixmap pixMaterialNormal = QPixmap(":/images/Objects-Material-Normal.png");
+
+ QVector<qt3dsdm::Qt3DSDMInstanceHandle> mats;
+ doc->getSceneMaterials(doc->GetSceneInstance(), mats);
+
+ // add freshly collected material list
+ for (auto matInstance : qAsConst(mats)) {
+ qt3dsdm::SValue v;
+ propertySystem->GetInstancePropertyValue(matInstance,
+ bridge->GetObjectDefinitions().m_Named.m_NameProp, v);
+ QString matName = qt3dsdm::get<QString>(v);
+
+ // get the material's object name (parent)
+ qt3dsdm::Qt3DSDMInstanceHandle parentInstance = bridge->GetParentInstance(matInstance);
+ qt3dsdm::SValue vParent;
+ propertySystem->GetInstancePropertyValue(parentInstance,
+ bridge->GetObjectDefinitions().m_Named.m_NameProp, vParent);
+ QString objName = qt3dsdm::get<QString>(vParent);
+ matName.append(QLatin1String(" (") + objName + QLatin1String(")"));
+
+ QListWidgetItem *matItem = new QListWidgetItem(this);
+ matItem->setData(Qt::DisplayRole, matName);
+ matItem->setData(Qt::DecorationRole, pixMaterialNormal);
+ matItem->setData(Qt::UserRole, QVariant(matInstance));
+
+ if (matInstance == refInstance)
+ setCurrentItem(matItem);
+ }
+
+ if (count() == 0) {
+ // Show an unselectable dummy item
+ static const QPixmap pixWarning = QPixmap(":/images/warning.png");
+ QListWidgetItem *matItem = new QListWidgetItem(this);
+ matItem->setData(Qt::DisplayRole, tr("No animatable materials found"));
+ matItem->setData(Qt::DecorationRole, pixWarning);
+ matItem->setData(Qt::UserRole, -1);
+ setSelectionMode(QAbstractItemView::NoSelection);
+ } else {
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ }
+
+ QSize widgetSize(CStudioPreferences::valueWidth(),
+ qMin(10, count()) * CStudioPreferences::controlBaseHeight());
+
+ return widgetSize;
+}
+
+void MaterialRefView::updateSelection()
+{
+ int refInstance = getRefInstance();
+ for (int i = 0, itemCount = count(); i < itemCount; ++i) {
+ int matInstance = item(i)->data(Qt::UserRole).toInt();
+ if (matInstance == refInstance) {
+ setCurrentRow(i);
+ break;
+ }
+ }
+}
+
+bool MaterialRefView::isFocused() const
+{
+ return hasFocus();
+}
+
+int MaterialRefView::getRefInstance() const
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ qt3dsdm::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+ return doc->GetDataModelObjectReferenceHelper()->Resolve(value, m_instance);
+}
+
+void MaterialRefView::focusInEvent(QFocusEvent *event)
+{
+ QAbstractItemView::focusInEvent(event);
+ emit focusChanged();
+}
+
+void MaterialRefView::focusOutEvent(QFocusEvent *event)
+{
+ QAbstractItemView::focusOutEvent(event);
+ emit focusChanged();
+ QTimer::singleShot(0, this, &QAbstractItemView::close);
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.h
new file mode 100644
index 00000000..d4131cda
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MaterialRefView.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MATERIALREFVIEW_H
+#define MATERIALREFVIEW_H
+
+#include <QtWidgets/qlistwidget.h>
+#include "Qt3DSDMHandles.h"
+
+class MaterialRefView : public QListWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(bool focused READ isFocused NOTIFY focusChanged)
+
+public:
+ explicit MaterialRefView(QWidget *parent = nullptr);
+
+ QSize refreshMaterials(int instance, int handle);
+
+ void updateSelection();
+protected:
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+
+Q_SIGNALS:
+ void focusChanged();
+
+private:
+ bool isFocused() const;
+ int getRefInstance() const;
+
+ int m_instance = -1;
+ int m_handle = -1;
+};
+
+#endif // MATERIALREFVIEW_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooser.qml
new file mode 100644
index 00000000..fcaf498c
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooser.qml
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+ ListView {
+ id: listView
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 4
+
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _meshChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: {
+ _meshChooserView.setSelectedMeshName(filePath);
+ }
+ onDoubleClicked: {
+ _meshChooserView.setSelectedMeshName(filePath);
+ _meshChooserView.hide();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.cpp
new file mode 100644
index 00000000..f87dc89b
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "MeshChooserModel.h"
+#include "Core.h"
+#include "Doc.h"
+#include "IDocumentBufferCache.h"
+#include "StudioApp.h"
+#include "BasicObjectsModel.h"
+
+MeshChooserModel::MeshChooserModel(QObject *parent)
+ : ChooserModelBase(parent)
+{
+}
+
+MeshChooserModel::~MeshChooserModel()
+{
+
+}
+
+bool MeshChooserModel::isVisible(const QString &path) const
+{
+ return getIconType(path) == OBJTYPE_MODEL;
+}
+
+const QVector<ChooserModelBase::FixedItem> MeshChooserModel::getFixedItems() const
+{
+ static QVector<FixedItem> items;
+
+ if (items.isEmpty()) {
+ auto primitives = BasicObjectsModel::BasicMeshesModel();
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ for (int i = 0; i < primitives.size(); i++) {
+ auto item = primitives.at(i);
+ const wchar_t *itemName = doc->GetBufferCache().GetPrimitiveName(item.primitiveType());
+ if (itemName[0] == L'#')
+ items.append({ OBJTYPE_MODEL, item.icon(), QString::fromWCharArray(itemName + 1) });
+ }
+ }
+
+ return items;
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.h
new file mode 100644
index 00000000..5aea88a8
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserModel.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESHCHOOSERMODEL_H
+#define MESHCHOOSERMODEL_H
+
+#include "ChooserModelBase.h"
+
+class MeshChooserModel : public ChooserModelBase
+{
+ Q_OBJECT
+
+public:
+ explicit MeshChooserModel(QObject *parent = nullptr);
+ virtual ~MeshChooserModel();
+
+private:
+ bool isVisible(const QString &path) const override;
+ const QVector<FixedItem> getFixedItems() const override;
+};
+
+#endif // MESHCHOOSERMODEL_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.cpp
new file mode 100644
index 00000000..1aac4731
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "MeshChooserView.h"
+#include "MeshChooserModel.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "StudioPreferences.h"
+#include "IDocumentEditor.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "Qt3DSDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "IDocumentBufferCache.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+MeshChooserView::MeshChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new MeshChooserModel(this))
+{
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &MeshChooserView::initialize);
+}
+
+void MeshChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty(QStringLiteral("_resDir"),
+ StudioUtils::resourceImageUrl());
+ rootContext()->setContextProperty(QStringLiteral("_meshChooserView"), this);
+ rootContext()->setContextProperty(QStringLiteral("_meshChooserModel"), m_model);
+
+ engine()->addImportPath(StudioUtils::qmlImportPath());
+ setSource(QUrl(QStringLiteral("qrc:/Palettes/Inspector/MeshChooser.qml")));
+}
+
+int MeshChooserView::numMeshes() const
+{
+ return m_model->rowCount();
+}
+
+void MeshChooserView::setSelectedMeshName(const QString &name)
+{
+ bool resourceName = false;
+ QString meshName = QLatin1Char('#') + name;
+ const wchar_t **files = g_StudioApp.GetCore()->GetDoc()->GetBufferCache().GetPrimitiveNames();
+ for (const wchar_t **item = files; item && *item; ++item) {
+ QString primitive = QString::fromWCharArray(*item);
+ if (primitive == meshName) {
+ resourceName = true;
+ break;
+ }
+ }
+ if (!resourceName)
+ meshName = name;
+
+ Q_EMIT meshSelected(m_handle, m_instance, meshName);
+}
+
+void MeshChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+void MeshChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+void MeshChooserView::updateSelection()
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ qt3dsdm::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ QString currentFile;
+ const QString meshValue = qt3dsdm::get<QString>(value);
+ if (meshValue.startsWith(QLatin1Char('#')))
+ currentFile = meshValue.mid(1);
+ else
+ currentFile = QDir::cleanPath(QDir(doc->GetDocumentDirectory()).filePath(meshValue));
+
+ m_model->setCurrentFile(currentFile);
+}
+
+bool MeshChooserView::isFocused() const
+{
+ return hasFocus();
+}
+
+void MeshChooserView::focusInEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusInEvent(event);
+ emit focusChanged();
+}
+
+void MeshChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ emit focusChanged();
+ QTimer::singleShot(0, this, &QQuickWidget::close);
+}
+
+void MeshChooserView::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Escape)
+ QTimer::singleShot(0, this, &MeshChooserView::close);
+
+ QQuickWidget::keyPressEvent(event);
+}
+
+void MeshChooserView::showEvent(QShowEvent *event)
+{
+ updateSelection();
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.h
new file mode 100644
index 00000000..6d12cf14
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MeshChooserView.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESHCHOOSERVIEW_H
+#define MESHCHOOSERVIEW_H
+
+#include <QQuickWidget>
+
+namespace Q3DStudio {
+class CFilePath;
+class CString;
+}
+
+class MeshChooserModel;
+
+QT_FORWARD_DECLARE_CLASS(QAbstractItemModel)
+
+class MeshChooserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(bool focused READ isFocused NOTIFY focusChanged)
+
+public:
+ explicit MeshChooserView(QWidget *parent = nullptr);
+
+ Q_INVOKABLE void setSelectedMeshName(const QString &name);
+
+ void setHandle(int handle);
+ void setInstance(int instance);
+ int numMeshes() const;
+
+ void updateSelection();
+
+Q_SIGNALS:
+ void meshSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+
+Q_SIGNALS:
+ void focusChanged();
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ bool isFocused() const;
+
+ int m_handle = -1;
+ int m_instance = -1;
+ MeshChooserModel *m_model = nullptr;
+};
+
+#endif // MESHCHOOSERVIEW_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.cpp
new file mode 100644
index 00000000..24c67fbe
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "MouseHelper.h"
+#include "MainFrm.h"
+#include "StudioApp.h"
+#include <QtWidgets/qapplication.h>
+#include <QtWidgets/qmainwindow.h>
+#include <QtWidgets/qwidget.h>
+#include <QtGui/qcursor.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/qscreen.h>
+#include <QtCore/qtimer.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qbitmap.h>
+
+static void setBlankCursor()
+{
+ // Qt::BlankCursor gets corrupted in some situations, so use custom bitmap (QTBUG-61678)
+ static QBitmap *zeroBitmap = nullptr;
+ if (!zeroBitmap) {
+ zeroBitmap = new QBitmap(32, 32);
+ zeroBitmap->clear();
+ }
+ QGuiApplication::setOverrideCursor(QCursor(*zeroBitmap, *zeroBitmap));
+}
+
+MouseHelper::MouseHelper(QObject *parent)
+ : QObject(parent)
+ , m_dragState(StateNotDragging)
+ , m_maxDelta(50, 50)
+{
+ // All cursor position modifications are done asynchronously, so we don't get position
+ // changes in middle of mouse event handling.
+ m_cursorResetTimer.setInterval(0);
+ m_cursorResetTimer.setSingleShot(true);
+ connect(&m_cursorResetTimer, &QTimer::timeout, this, &MouseHelper::resetCursor);
+}
+
+void MouseHelper::startUnboundedDrag()
+{
+ m_dragState = StateDragging;
+ setBlankCursor();
+ m_startPos = QCursor::pos();
+
+ QWindow *window = g_StudioApp.m_pMainWnd->windowHandle();
+ if (window)
+ window->installEventFilter(this); // Always install event filter to main window
+
+ if (m_widget) {
+ // Use the center of the on-screen portion of the parent widget as reference point.
+ // This ensures cursor restores properly, as the cursor stays on the widget.
+ m_window = m_widget->window()->windowHandle();
+ const QRect screenGeometry = m_window->screen()->geometry();
+ const QPoint bottomRight = screenGeometry.bottomRight();
+ QSize widgetSize = m_widget->size();
+ QPoint widgetPos = m_widget->mapToGlobal(QPoint(0, 0));
+ if (widgetPos.x() < 0) {
+ widgetSize.setWidth(widgetSize.width() + widgetPos.x());
+ widgetPos.setX(0);
+ }
+ if (widgetPos.y() < 0) {
+ widgetSize.setHeight(widgetSize.height() + widgetPos.y());
+ widgetPos.setY(0);
+ }
+ if (widgetPos.x() + widgetSize.width() > bottomRight.x())
+ widgetSize.setWidth(bottomRight.x() - widgetPos.x());
+ if (widgetPos.y() + widgetSize.height() > bottomRight.y())
+ widgetSize.setHeight(bottomRight.y() - widgetPos.y());
+ m_maxDelta = QPoint(widgetSize.width() / 2, widgetSize.height() / 2);
+ m_referencePoint = widgetPos + m_maxDelta;
+ } else {
+ // Just assume the screen of the app is at least 400x400 if we don't have widget
+ m_referencePoint = QPoint(200, 200);
+ m_window = nullptr;
+ }
+ m_previousPoint = m_startPos;
+
+ m_cursorResetTimer.start();
+}
+
+void MouseHelper::endUnboundedDrag()
+{
+ QWindow *window = g_StudioApp.m_pMainWnd->windowHandle();
+ if (window)
+ window->removeEventFilter(this);
+ m_dragState = StateEndingDrag;
+ m_cursorResetTimer.start();
+}
+
+QPoint MouseHelper::delta()
+{
+ QPoint delta(0, 0);
+ if (m_dragState == StateDragging) {
+ QPoint currentPoint = QCursor::pos();
+ delta = currentPoint - m_previousPoint;
+ m_previousPoint = currentPoint;
+
+ // Limit delta to even out the maximum possible change rate regardless of widget position
+ if (delta.x() > m_maxDelta.x())
+ delta.setX(m_maxDelta.x());
+ else if (delta.x() < -m_maxDelta.x())
+ delta.setX(-m_maxDelta.x());
+
+ if (delta.y() > m_maxDelta.y())
+ delta.setY(m_maxDelta.y());
+ else if (delta.y() < -m_maxDelta.y())
+ delta.setY(-m_maxDelta.y());
+
+ if (!m_cursorResetTimer.isActive())
+ m_cursorResetTimer.start();
+ }
+ return delta;
+}
+
+void MouseHelper::setWidget(QWidget *widget)
+{
+ m_widget = widget;
+}
+
+void MouseHelper::resetCursor()
+{
+ switch (m_dragState) {
+ case StateDragging:
+ if (m_window)
+ QCursor::setPos(m_window->screen(), m_referencePoint);
+ else
+ QCursor::setPos(m_referencePoint);
+ m_previousPoint = m_referencePoint;
+ break;
+ case StateEndingDrag:
+ if (m_window)
+ QCursor::setPos(m_window->screen(), m_startPos);
+ else
+ QCursor::setPos(m_startPos);
+ m_dragState = StateFinalCursorReset;
+ m_cursorResetTimer.start();
+ break;
+ case StateFinalCursorReset:
+ // First change to default cursor to avoid any flicker of cursor
+ qApp->changeOverrideCursor(Qt::ArrowCursor);
+ qApp->restoreOverrideCursor();
+ m_dragState = StateNotDragging;
+ break;
+ case StateNotDragging:
+ default:
+ break;
+ }
+}
+
+bool MouseHelper::eventFilter(QObject *obj, QEvent *event)
+{
+ Q_UNUSED(obj)
+
+ // Eat all mouse button events that are not for left button and all key events
+ switch (event->type()) {
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease: {
+ QMouseEvent *me = static_cast<QMouseEvent *>(event);
+ if (me->button() == Qt::LeftButton)
+ return false;
+ else
+ return true;
+ }
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::ShortcutOverride:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.h
new file mode 100644
index 00000000..8f343bc4
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/MouseHelper.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef MOUSEHELPER_H
+#define MOUSEHELPER_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qtimer.h>
+
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QWindow)
+
+class MouseHelper : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit MouseHelper(QObject *parent = nullptr);
+ ~MouseHelper() {};
+
+ Q_INVOKABLE void startUnboundedDrag();
+ Q_INVOKABLE void endUnboundedDrag();
+ Q_INVOKABLE QPoint delta();
+
+ void setWidget(QWidget *widget);
+
+private Q_SLOTS:
+ void resetCursor();
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+
+private:
+ QPoint m_startPos;
+ QPoint m_referencePoint;
+ QPoint m_previousPoint;
+ QTimer m_cursorResetTimer;
+
+ enum DragState {
+ StateNotDragging,
+ StateDragging,
+ StateEndingDrag,
+ StateFinalCursorReset
+ };
+ DragState m_dragState;
+ QWidget *m_widget = nullptr; // Not owned
+ QWindow *m_window = nullptr; // Not owned
+ QPoint m_maxDelta;
+};
+
+#endif // MOUSEHELPER_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowser.qml
new file mode 100644
index 00000000..fc0a2e55
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowser.qml
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import Qt3DStudio 1.0
+import "../controls"
+
+Rectangle {
+ id: root
+
+ property alias selectedText: selectionText.text
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+
+ ListView {
+ id: browserList
+
+ Layout.margins: 10
+ Layout.columnSpan: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.minimumHeight: 80
+ Layout.preferredHeight: count * 20
+ Layout.preferredWidth: root.width
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _objectBrowserView.model
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+ currentIndex: _objectBrowserView.selection
+
+ delegate: Item {
+ id: delegateItem
+
+ x: model.depth * 20
+ width: parent.width
+ height: model.parentExpanded ? typeIcon.height + 10 : 0
+
+ visible: height > 0
+
+ Behavior on height {
+ NumberAnimation {
+ duration: 100
+ easing.type: Easing.OutQuad
+ }
+ }
+
+ Row {
+ id: row
+
+ height: typeIcon.height
+ spacing: 5
+
+ Image {
+ source: {
+ if (!model.hasChildren)
+ return "";
+ model.expanded ? _resDir + "arrow_down.png"
+ : _resDir + "arrow.png";
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: model.expanded = !model.expanded
+ }
+ }
+
+ Rectangle {
+ height: typeIcon.height
+ width: typeIcon.width + name.width + 10
+
+ color: model.index === browserList.currentIndex ? _selectionColor
+ : "transparent"
+
+ Row {
+ spacing: 10
+ Image {
+ id: typeIcon
+
+ source: model.icon
+ }
+
+ StyledLabel {
+ id: name
+ anchors.verticalCenter: typeIcon.verticalCenter
+ color: model.textColor
+ text: model.name
+ }
+ }
+
+ MouseArea {
+ id: delegateArea
+
+ anchors.fill: parent
+ onClicked: {
+ if (_objectBrowserView.selectable(model.index)) {
+ browserList.currentIndex = model.index;
+ // Set the selection here, as otherwise we can't set for
+ // example the same reference material to more than one target
+ // without selecting something else first
+ _objectBrowserView.selection = browserList.currentIndex;
+ }
+ }
+ onDoubleClicked: {
+ if (_objectBrowserView.selectable(model.index)) {
+ browserList.currentIndex = model.index;
+ // Set the selection here, as otherwise we can't set for
+ // example the same reference material to more than one target
+ // without selecting something else first
+ _objectBrowserView.selection = browserList.currentIndex;
+ _objectBrowserView.close();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Connections {
+ target: _objectBrowserView
+ onSelectionChanged: {
+ if (browserList.currentIndex !== _objectBrowserView.selection)
+ browserList.currentIndex = _objectBrowserView.selection;
+ }
+ }
+ }
+
+ StyledMenuSeparator {}
+
+ GridLayout {
+ columns: 2
+ Layout.margins: 10
+
+ StyledLabel {
+ text: qsTr("Type")
+ }
+
+ StyledComboBox {
+ id: pathCombo
+ model: [qsTr("Absolute Reference"), qsTr("Path Reference")]
+
+ onActivated: {
+ if (index === 0)
+ _objectBrowserView.pathType = ObjectBrowserView.Absolute;
+ else if (index === 1)
+ _objectBrowserView.pathType = ObjectBrowserView.Relative;
+ }
+ }
+
+ StyledLabel {
+ text: qsTr("Path")
+ }
+
+ StyledLabel {
+ id: selectionText
+ Layout.preferredWidth: _valueWidth
+ text: pathCombo.currentIndex === 0 ? _objectBrowserView.absPath(browserList.currentIndex)
+ : _objectBrowserView.relPath(browserList.currentIndex)
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.cpp
new file mode 100644
index 00000000..87238847
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.cpp
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "ObjectBrowserView.h"
+
+#include "ObjectListModel.h"
+#include "StudioPreferences.h"
+#include "StudioUtils.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+ObjectBrowserView::ObjectBrowserView(QWidget *parent)
+ : QQuickWidget(parent)
+{
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &ObjectBrowserView::initialize);
+}
+
+QAbstractItemModel *ObjectBrowserView::model() const
+{
+ return m_model;
+}
+
+void ObjectBrowserView::setModel(ObjectListModel *model)
+{
+ if (!m_model)
+ m_model = new FlatObjectListModel(model, this);
+ m_model->setSourceModel(model);
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+
+ // Remove "Scene.__Container" and "materials//Default" entries
+ QModelIndexList list = m_model->match(m_model->index(0, 0),
+ ObjectListModel::AbsolutePathRole,
+ QStringLiteral("Scene.")
+ + bridge->getMaterialContainerName(), 1,
+ Qt::MatchFlags(Qt::MatchWrap | Qt::MatchExactly
+ | Qt::MatchRecursive));
+ list.append(m_model->match(m_model->index(0, 0),
+ ObjectListModel::NameRole,
+ QStringLiteral("materials/") + bridge->getDefaultMaterialName(), 1,
+ Qt::MatchFlags(Qt::MatchWrap | Qt::MatchExactly
+ | Qt::MatchRecursive)));
+
+ for (int i = list.size(); i > 0; i--)
+ m_model->removeRow(list.at(i - 1).row());
+
+ m_ownerInstance = 0;
+ m_selection = -1;
+
+ Q_EMIT modelChanged();
+}
+
+QString ObjectBrowserView::absPath(int index) const
+{
+ return m_model->index(index, 0).data(ObjectListModel::AbsolutePathRole).toString();
+}
+
+QString ObjectBrowserView::relPath(int index) const
+{
+ return m_model->data(
+ m_model->index(index),
+ m_model->sourceModel()->indexForHandle(m_ownerInstance),
+ ObjectListModel::PathReferenceRole).toString();
+}
+
+bool ObjectBrowserView::selectable(int index) const
+{
+ auto handleId = m_model->index(index, 0).data(ObjectListModel::HandleRole).toInt();
+ auto handle = qt3dsdm::Qt3DSDMInstanceHandle(handleId);
+ return m_model->sourceModel()->selectable(handle);
+}
+
+void ObjectBrowserView::selectAndExpand(const qt3dsdm::Qt3DSDMInstanceHandle &handle,
+ const qt3dsdm::Qt3DSDMInstanceHandle &owner)
+{
+ m_ownerInstance = owner;
+ QModelIndex index = m_model->sourceIndexForHandle(handle);
+ if (!index.isValid())
+ return;
+ m_model->expandTo(QModelIndex(), index);
+ m_blockCommit = true;
+ setSelection(m_model->rowForSourceIndex(index));
+ m_blockCommit = false;
+}
+
+void ObjectBrowserView::setSelection(int index)
+{
+ if (m_selection != index) {
+ m_selection = index;
+ Q_EMIT selectionChanged();
+ }
+}
+
+void ObjectBrowserView::setPathType(ObjectBrowserView::PathType type)
+{
+ if (type != m_pathType) {
+ m_pathType = type;
+ Q_EMIT pathTypeChanged();
+ }
+}
+
+qt3dsdm::Qt3DSDMInstanceHandle ObjectBrowserView::selectedHandle() const
+{
+ auto handleId = m_model->index(m_selection, 0).data(ObjectListModel::HandleRole).toInt();
+ return qt3dsdm::Qt3DSDMInstanceHandle(handleId);
+}
+
+bool ObjectBrowserView::isFocused() const
+{
+ return hasFocus();
+}
+
+void ObjectBrowserView::focusInEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusInEvent(event);
+ emit focusChanged();
+}
+
+void ObjectBrowserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ emit focusChanged();
+ QTimer::singleShot(0, this, &QQuickWidget::close);
+}
+
+void ObjectBrowserView::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Escape)
+ QTimer::singleShot(0, this, &ObjectBrowserView::close);
+
+ QQuickWidget::keyPressEvent(event);
+}
+
+void ObjectBrowserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty(QStringLiteral("_objectBrowserView"), this);
+ rootContext()->setContextProperty(QStringLiteral("_resDir"), StudioUtils::resourceImageUrl());
+ qmlRegisterUncreatableType<ObjectBrowserView>(
+ "Qt3DStudio", 1, 0, "ObjectBrowserView",
+ QStringLiteral("Creation of ObjectBrowserView not allowed from QML"));
+ engine()->addImportPath(StudioUtils::qmlImportPath());
+ setSource(QUrl(QStringLiteral("qrc:/Palettes/Inspector/ObjectBrowser.qml")));
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.h
new file mode 100644
index 00000000..7fb7ec30
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectBrowserView.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef OBJECTBROWSERVIEW_H
+#define OBJECTBROWSERVIEW_H
+
+#include <QQuickWidget>
+
+#include "RelativePathTools.h"
+#include "Qt3DSDMHandles.h"
+
+#include <QColor>
+
+class ObjectListModel;
+class FlatObjectListModel;
+
+QT_FORWARD_DECLARE_CLASS(QAbstractItemModel)
+
+class ObjectBrowserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(bool focused READ isFocused NOTIFY focusChanged)
+ Q_PROPERTY(QAbstractItemModel *model READ model NOTIFY modelChanged FINAL)
+ Q_PROPERTY(int selection READ selection WRITE setSelection NOTIFY selectionChanged FINAL)
+ Q_PROPERTY(PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged FINAL)
+
+public:
+ ObjectBrowserView(QWidget *parent = nullptr);
+
+
+ enum PathType {
+ Absolute = CRelativePathTools::EPATHTYPE_GUID,
+ Relative = CRelativePathTools::EPATHTYPE_RELATIVE,
+ };
+ Q_ENUM(PathType)
+
+ QAbstractItemModel *model() const;
+ void setModel(ObjectListModel *model);
+
+ Q_INVOKABLE QString absPath(int index) const;
+ Q_INVOKABLE QString relPath(int index) const;
+ Q_INVOKABLE bool selectable(int index) const;
+
+ void selectAndExpand(const qt3dsdm::Qt3DSDMInstanceHandle &handle,
+ const qt3dsdm::Qt3DSDMInstanceHandle &owner);
+
+ int selection() const { return m_selection; }
+ void setSelection(int index);
+
+ PathType pathType() const {return m_pathType;}
+ void setPathType(PathType type);
+
+ qt3dsdm::Qt3DSDMInstanceHandle selectedHandle() const;
+
+ bool canCommit() const { return !m_blockCommit; }
+
+Q_SIGNALS:
+ void modelChanged();
+ void pathTypeChanged();
+ void selectionChanged();
+
+protected:
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+
+Q_SIGNALS:
+ void focusChanged();
+
+private:
+ void initialize();
+ bool isFocused() const;
+
+ FlatObjectListModel *m_model = nullptr;
+ QHash<int, ObjectListModel *> m_subModels;
+ QColor m_baseColor = QColor::fromRgb(75, 75, 75);
+ QColor m_selectColor;
+ int m_selection = -1;
+ PathType m_pathType = Absolute;
+ qt3dsdm::Qt3DSDMInstanceHandle m_ownerInstance = 0;
+ bool m_blockCommit = false;
+};
+
+#endif // OBJECTBROWSERVIEW_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.cpp
new file mode 100644
index 00000000..ad66274a
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.cpp
@@ -0,0 +1,534 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ObjectListModel.h"
+
+#include "ClientDataModelBridge.h"
+#include "Core.h"
+#include "Doc.h"
+#include "GraphUtils.h"
+#include "IObjectReferenceHelper.h"
+#include "StudioUtils.h"
+#include "SlideSystem.h"
+#include "StudioObjectTypes.h"
+#include "StudioPreferences.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+
+#include <QCoreApplication>
+#include <QColor>
+
+ObjectListModel::ObjectListModel(CCore *core,
+ const qt3dsdm::Qt3DSDMInstanceHandle &baseHandle, QObject *parent,
+ bool isAliasSelectList)
+ : QAbstractItemModel(parent)
+ , m_core(core)
+ , m_baseHandle(baseHandle)
+ , m_AliasSelectList(isAliasSelectList)
+{
+ auto doc = m_core->GetDoc();
+ m_objRefHelper = doc->GetDataModelObjectReferenceHelper();
+ if (!m_AliasSelectList)
+ m_slideHandle = m_objRefHelper->GetSlideList(m_baseHandle)[0];
+ else
+ m_slideHandle = m_objRefHelper->GetSlideList(m_baseHandle).back();
+}
+
+QHash<int, QByteArray> ObjectListModel::roleNames() const
+{
+ auto names = QAbstractItemModel::roleNames();
+ names.insert(NameRole, "name");
+ names.insert(HandleRole, "handle");
+ names.insert(IconRole, "icon");
+ names.insert(TextColorRole, "textColor");
+ names.insert(AbsolutePathRole, "absolutePath");
+ names.insert(PathReferenceRole, "referencePath");
+
+ return names;
+}
+
+int ObjectListModel::rowCount(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return 1;
+
+ const auto handle = handleForIndex(parent);
+ const auto children = childrenList(m_slideHandle, handle);
+ return int(children.size());
+}
+
+int ObjectListModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 1;
+}
+
+QVariant ObjectListModel::data(const QModelIndex &index, int role) const
+{
+ return data(index, QModelIndex(), role);
+}
+
+QVariant ObjectListModel::data(const QModelIndex &index,
+ const QModelIndex &startingIndex,
+ int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ return {};
+
+ auto handle = handleForIndex(index);
+
+ auto studioSystem = m_core->GetDoc()->GetStudioSystem();
+ auto propertySystem = studioSystem->GetPropertySystem();
+ qt3dsdm::SValue typeValue;
+ propertySystem->GetInstancePropertyValue(handle,
+ studioSystem->GetClientDataModelBridge()
+ ->GetTypeProperty(), typeValue);
+ qt3dsdm::DataModelDataType::Value valueType(qt3dsdm::GetValueType(typeValue));
+ if (valueType == qt3dsdm::DataModelDataType::None)
+ return {};
+
+ switch (role) {
+ case NameRole: {
+ return nameForHandle(handle);
+ }
+ case PathReferenceRole: {
+ Q3DStudio::CString data;
+ if (startingIndex.isValid()) {
+ data = m_objRefHelper->GetObjectReferenceString(
+ handleForIndex(startingIndex),
+ CRelativePathTools::EPATHTYPE_RELATIVE,
+ handle);
+ } else {
+ data = m_objRefHelper->GetObjectReferenceString(
+ m_baseHandle,
+ CRelativePathTools::EPATHTYPE_RELATIVE,
+ handle);
+ }
+ return data.toQString();
+ }
+ case AbsolutePathRole: {
+ Q3DStudio::CString data(m_objRefHelper->GetObjectReferenceString(
+ m_baseHandle, CRelativePathTools::EPATHTYPE_GUID, handle));
+ return data.toQString();
+ }
+ case HandleRole: {
+ return (int)handleForIndex(index);
+ }
+ case IconRole: {
+ auto info = m_objRefHelper->GetInfo(handle);
+ return StudioUtils::resourceImageUrl() + CStudioObjectTypes::GetNormalIconName(info.m_Type);
+ }
+ case TextColorRole: {
+ auto bridge = m_core->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ auto objType = bridge->GetObjectType(handle);
+ auto info = m_objRefHelper->GetInfo(handle);
+ if (m_excludeTypes.contains(objType))
+ return QVariant::fromValue(CStudioPreferences::disabledColor());
+ else if (info.m_Master)
+ return QVariant::fromValue(CStudioPreferences::masterColor());
+ else
+ return QVariant::fromValue(CStudioPreferences::textColor());
+ }
+ default:
+ return {};
+ }
+
+ return {};
+}
+
+QModelIndex ObjectListModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return createIndex(row, column, (quintptr)(m_baseHandle));
+
+ const auto handle = handleForIndex(parent);
+ const auto children = childrenList(m_slideHandle, handle);
+ if (row >= children.size())
+ return {};
+
+ auto childHandle = children[row];
+ return createIndex(row, column, (quintptr)(childHandle));
+}
+
+QModelIndex ObjectListModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return {};
+
+ const auto handle = handleForIndex(index);
+ qt3dsdm::Qt3DSDMInstanceHandle parentHandle = m_core->GetDoc()->GetAssetGraph()->GetParent(handle);
+ if (!parentHandle.Valid())
+ return {};
+
+ int row = 0;
+ qt3dsdm::Qt3DSDMInstanceHandle grandParentHandle = m_core->GetDoc()->GetAssetGraph()
+ ->GetParent(parentHandle);
+ if (grandParentHandle.Valid()) {
+ const auto children = childrenList(m_slideHandle, grandParentHandle);
+ const auto it = std::find(children.begin(), children.end(), parentHandle);
+ Q_ASSERT(it != children.end());
+ row = it - children.begin();
+ }
+
+ return createIndex(row, 0, (quintptr)(parentHandle));
+}
+
+qt3dsdm::Qt3DSDMInstanceHandle ObjectListModel::handleForIndex(const QModelIndex &index) const
+{
+ return static_cast<qt3dsdm::Qt3DSDMInstanceHandle>(index.internalId());
+}
+
+void ObjectListModel::excludeObjectTypes(const QVector<EStudioObjectType> &types)
+{
+ m_excludeTypes = types;
+}
+
+bool ObjectListModel::selectable(const qt3dsdm::Qt3DSDMInstanceHandle &handle) const
+{
+ auto bridge = m_core->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ auto objType = bridge->GetObjectType(handle);
+ // disallow aliasing the current active root
+ bool tryingToAliasParent = (m_core->GetDoc()->GetActiveRootInstance() == handle)
+ && m_AliasSelectList;
+ return (!m_excludeTypes.contains(objType) && !tryingToAliasParent);
+}
+
+qt3dsdm::TInstanceHandleList ObjectListModel::childrenList(
+ const qt3dsdm::Qt3DSDMSlideHandle &slideHandle,
+ const qt3dsdm::Qt3DSDMInstanceHandle &handle) const
+{
+ auto studioSystem = m_core->GetDoc()->GetStudioSystem();
+ auto slideSystem = studioSystem->GetSlideSystem();
+ auto currentMaster = slideSystem->GetMasterSlide(slideHandle);
+
+ qt3dsdm::TInstanceHandleList children;
+ m_objRefHelper->GetChildInstanceList(handle, children, slideHandle, m_baseHandle, true);
+ // allow action trigger/target from all objects
+ if (m_AliasSelectList) {
+ children.erase(
+ std::remove_if(children.begin(), children.end(),
+ [&slideHandle, slideSystem, &currentMaster](const qt3dsdm::Qt3DSDMInstanceHandle &h) {
+ const auto childSlide = slideSystem->GetAssociatedSlide(h);
+ if (!childSlide.Valid())
+ return true;
+ const auto childMaster = slideSystem->GetMasterSlide(childSlide);
+ if (childMaster == currentMaster) {
+ return childSlide != childMaster && childSlide != slideHandle;
+ } else {
+ return childSlide != childMaster;
+ }
+ }), children.end());
+ }
+ return children;
+}
+
+QString ObjectListModel::nameForHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle) const
+{
+ const auto data = m_objRefHelper->GetInfo(handle);
+ return data.m_Name.toQString();
+}
+
+QModelIndex ObjectListModel::indexForHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle,
+ const QModelIndex &startIndex) const
+{
+ if (handle == m_baseHandle)
+ return index(0, 0, {});
+
+ for (int i = 0; i < rowCount(startIndex); i++) {
+ auto idx = index(i, 0, startIndex);
+ if (static_cast<qt3dsdm::Qt3DSDMInstanceHandle>(idx.internalId()) == handle)
+ return idx;
+ if (rowCount(idx) > 0) {
+ QModelIndex foundIndex = indexForHandle(handle, idx);
+ if (foundIndex.isValid())
+ return foundIndex;
+ }
+ }
+ return {};
+}
+
+
+FlatObjectListModel::FlatObjectListModel(ObjectListModel *sourceModel, QObject *parent)
+ : QAbstractListModel(parent)
+
+{
+ Q_ASSERT(sourceModel);
+ setSourceModel(sourceModel);
+}
+
+QVector<FlatObjectListModel::SourceInfo> FlatObjectListModel::collectSourceIndexes(
+ const QModelIndex &sourceIndex, int depth) const
+{
+ QVector<SourceInfo> sourceInfo;
+
+ for (int i = 0; i < m_sourceModel->rowCount(sourceIndex); i++) {
+ auto idx = m_sourceModel->index(i, 0, sourceIndex);
+ SourceInfo info;
+ info.depth = depth;
+ info.index = idx;
+ sourceInfo.append(info);
+ if (m_sourceModel->rowCount(idx) > 0) {
+ sourceInfo += collectSourceIndexes(idx, depth + 1);
+ }
+ }
+
+ return sourceInfo;
+}
+
+QHash<int, QByteArray> FlatObjectListModel::roleNames() const
+{
+ auto roles = m_sourceModel->roleNames();
+ roles.insert(DepthRole, "depth");
+ roles.insert(ExpandedRole, "expanded");
+ roles.insert(ParentExpandedRole, "parentExpanded");
+ roles.insert(HasChildrenRole, "hasChildren");
+ return roles;
+}
+
+QModelIndex FlatObjectListModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ int row = proxyIndex.row();
+ if (row < 0 || row >= m_sourceInfo.count())
+ return {};
+ return m_sourceInfo[row].index;
+}
+
+QModelIndex FlatObjectListModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ return index(rowForSourceIndex(sourceIndex));
+}
+
+QVariant FlatObjectListModel::data(const QModelIndex &index, int role) const
+{
+ return data(index, QModelIndex(), role);
+}
+
+QVariant FlatObjectListModel::data(const QModelIndex &index,
+ const QModelIndex &startingIndex,
+ int role) const
+{
+ const auto row = index.row();
+ if (row < 0 || row >= m_sourceInfo.count())
+ return {};
+
+ switch (role) {
+ case DepthRole: {
+ auto info = m_sourceInfo[row];
+ return info.depth;
+ }
+ case ExpandedRole: {
+ auto info = m_sourceInfo[row];
+ return info.expanded;
+ }
+ case ParentExpandedRole: {
+ auto info = m_sourceInfo[row];
+ if (info.depth == 0)
+ return true;
+
+ int depth = info.depth;
+ for (int i = row - 1; i >= 0; i--) {
+ const auto prevInfo = m_sourceInfo[i];
+ if (prevInfo.depth < depth) {
+ if (!prevInfo.expanded) {
+ return false;
+ } else {
+ depth = prevInfo.depth;
+ }
+ }
+ }
+ return true;
+ }
+ case HasChildrenRole: {
+ if (row == m_sourceInfo.count() - 1)
+ return false;
+ auto info = m_sourceInfo[row];
+ auto nextInfo = m_sourceInfo[row + 1];
+ return (nextInfo.depth > info.depth);
+ }
+ }
+
+ QModelIndex sourceIndex = mapToSource(index);
+ if (startingIndex.isValid())
+ return m_sourceModel->data(sourceIndex, startingIndex, role);
+ else
+ return m_sourceModel->data(sourceIndex, QModelIndex(), role);
+
+}
+
+bool FlatObjectListModel::setData(const QModelIndex &index, const QVariant &data, int role)
+{
+ const auto row = index.row();
+ if (row < 0 || row >= m_sourceInfo.count())
+ return {};
+
+ switch (role) {
+ case ExpandedRole: {
+ auto info = &m_sourceInfo[index.row()];
+ info->expanded = data.toBool();
+ Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0), {});
+ return true;
+ }
+ }
+
+ QModelIndex sourceIndex = mapToSource(index);
+ return m_sourceModel->setData(sourceIndex, data, role);
+}
+
+int FlatObjectListModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_sourceInfo.count();
+}
+
+bool FlatObjectListModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ beginRemoveRows(parent, row, row + count - 1);
+ m_sourceInfo.remove(row, count);
+ endRemoveRows();
+ return true;
+}
+
+void FlatObjectListModel::setSourceModel(ObjectListModel *sourceModel)
+{
+ beginResetModel();
+ sourceModel->disconnect(this);
+ connect(sourceModel, &QAbstractListModel::dataChanged, this,
+ [this](const QModelIndex &start, const QModelIndex &end, const QVector<int> &roles) {
+ emit dataChanged(mapFromSource(start), mapFromSource(end), roles);
+
+ });
+ connect(sourceModel, &QAbstractListModel::rowsInserted, this,
+ [this](const QModelIndex &parent, int start, int end) {
+ const int parentRow = rowForSourceIndex(parent);
+ const int depth = m_sourceInfo[parentRow].depth + 1;
+ const int startRow = rowForSourceIndex(parent, start);
+ Q_ASSERT(startRow != -1);
+ beginInsertRows({}, startRow, startRow + end - start);
+ for (int row = end; row >= start; --row) {
+ SourceInfo info;
+ info.depth = depth;
+ info.index = m_sourceModel->index(row, 0, parent);
+ m_sourceInfo.insert(startRow, info);
+ }
+ endInsertRows();
+ });
+ connect(sourceModel, &QAbstractListModel::rowsRemoved, this,
+ [this](const QModelIndex &parent, int start, int end) {
+ const int startRow = rowForSourceIndex(parent, start);
+ const int endRow = rowForSourceIndex(parent, end);
+ Q_ASSERT(startRow != -1 && endRow != -1);
+ beginRemoveRows({}, startRow, endRow);
+ m_sourceInfo.remove(startRow, endRow - startRow + 1);
+ endRemoveRows();
+ });
+ connect(sourceModel, &QAbstractListModel::modelReset, this,
+ [this]() {
+ beginResetModel();
+ m_sourceInfo = collectSourceIndexes({}, 0);
+ endResetModel();
+ });
+
+ connect(sourceModel, &ObjectListModel::roleUpdated, this, [this](int role) {
+ emit dataChanged(index(0,0), index(rowCount() - 1, 0), {role});
+ });
+
+ connect(sourceModel, &ObjectListModel::rolesUpdated, this,
+ [this](const QVector<int> &roles = QVector<int> ()) {
+ emit dataChanged(index(0,0), index(rowCount() - 1, 0), roles);
+ });
+
+
+ m_sourceModel = sourceModel;
+ m_sourceInfo = collectSourceIndexes({}, 0);
+ endResetModel();
+}
+
+// startIndex and searchIndex are source indexes
+bool FlatObjectListModel::expandTo(const QModelIndex &startIndex, const QModelIndex &searchIndex)
+{
+ // Found the index we are looking for. We don't want to expand it, so just return true.
+ if (startIndex == searchIndex)
+ return true;
+
+ // Look for the search index in children
+ const int rowCount = m_sourceModel->rowCount(startIndex);
+ for (int i = 0; i < rowCount; i++) {
+ auto idx = m_sourceModel->index(i, 0, startIndex);
+ if (idx == searchIndex) {
+ // Expand startIndex as that is the parent
+ setData(index(rowForSourceIndex(startIndex)), QVariant(true), ExpandedRole);
+ return true;
+ }
+ if (m_sourceModel->rowCount(idx) > 0) {
+ bool found = expandTo(idx, searchIndex);
+ if (found) {
+ // Found by some descendant. Keep expanding parents
+ setData(index(rowForSourceIndex(startIndex)), QVariant(true), ExpandedRole);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+int FlatObjectListModel::rowForSourceIndex(const QModelIndex &sourceIndex) const
+{
+ for (int i = 0; i < m_sourceInfo.size(); i++) {
+ if (m_sourceInfo[i].index == sourceIndex)
+ return i;
+ }
+ return -1;
+}
+
+int FlatObjectListModel::rowForSourceIndex(const QModelIndex& parentIndex, int row) const
+{
+ const int parentRow = rowForSourceIndex(parentIndex);
+ if (parentRow == -1)
+ return -1;
+ const int childDepth = m_sourceInfo[parentRow].depth + 1;
+ int i = parentRow + 1;
+ while (i < m_sourceInfo.size()) {
+ const auto& info = m_sourceInfo[i];
+ if (info.depth < childDepth)
+ break;
+ if (info.depth == childDepth) {
+ if (row == 0)
+ break;
+ --row;
+ }
+ ++i;
+ }
+ return i;
+}
+
+QModelIndex FlatObjectListModel::sourceIndexForHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle)
+{
+ return m_sourceModel->indexForHandle(handle);
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.h
new file mode 100644
index 00000000..4013f15c
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/ObjectListModel.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OBJECTLISTMODEL_H
+#define OBJECTLISTMODEL_H
+
+#include <QAbstractItemModel>
+#include <QAbstractListModel>
+
+#include "Qt3DSDMHandles.h"
+#include "StudioObjectTypes.h"
+
+class IObjectReferenceHelper;
+class CCore;
+
+class ObjectListModel : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ ObjectListModel(CCore *core, const qt3dsdm::Qt3DSDMInstanceHandle &baseHandle,
+ QObject *parent = nullptr,
+ bool isAliasSelectList = false);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QVariant data(const QModelIndex &index,
+ const QModelIndex &startingIndex = {},
+ int role = Qt::DisplayRole) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &index) const override;
+
+ enum Roles {
+ NameRole = Qt::DisplayRole,
+ AbsolutePathRole = Qt::UserRole + 1,
+ PathReferenceRole,
+ IconRole,
+ TextColorRole,
+ HandleRole,
+ LastRole = HandleRole
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+
+ qt3dsdm::Qt3DSDMInstanceHandle baseHandle() const {return m_baseHandle;}
+
+ QModelIndex indexForHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle,
+ const QModelIndex &startIndex = {}) const;
+
+ bool selectable(const qt3dsdm::Qt3DSDMInstanceHandle &handle) const;
+
+ void excludeObjectTypes(const QVector<EStudioObjectType> &types);
+
+Q_SIGNALS:
+ void roleUpdated(int role);
+ void rolesUpdated(const QVector<int> &roles = QVector<int> ());
+
+protected:
+ qt3dsdm::Qt3DSDMInstanceHandle handleForIndex(const QModelIndex &index) const;
+
+ virtual qt3dsdm::TInstanceHandleList childrenList(const qt3dsdm::Qt3DSDMSlideHandle &slideHandle,
+ const qt3dsdm::Qt3DSDMInstanceHandle &handle) const;
+
+ QString nameForHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle) const;
+
+ CCore *m_core;
+ qt3dsdm::Qt3DSDMSlideHandle m_slideHandle;
+ qt3dsdm::Qt3DSDMInstanceHandle m_baseHandle;
+ IObjectReferenceHelper *m_objRefHelper;
+ QVector<EStudioObjectType> m_excludeTypes;
+ bool m_AliasSelectList;
+};
+
+class FlatObjectListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ // TODO Make FlatObjectList take a shared pointer
+ FlatObjectListModel(ObjectListModel *sourceModel, QObject *parent = nullptr);
+
+ enum Roles {
+ DepthRole = ObjectListModel::LastRole + 1,
+ ExpandedRole,
+ ParentExpandedRole,
+ HasChildrenRole
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QVariant data(const QModelIndex &index,
+ const QModelIndex &startingIndex = {},
+ int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
+
+ void setSourceModel(ObjectListModel *sourceModel);
+ ObjectListModel *sourceModel() const { return m_sourceModel; }
+ bool expandTo(const QModelIndex &rootIndex, const QModelIndex &searchIndex);
+ int rowForSourceIndex(const QModelIndex &sourceIndex) const;
+ QModelIndex sourceIndexForHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle);
+
+private:
+ int rowForSourceIndex(const QModelIndex &parentIndex, int row) const;
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+ QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
+
+ struct SourceInfo {
+ bool expanded = false;
+ int depth = 0;
+ QPersistentModelIndex index;
+ };
+
+ QVector<SourceInfo> collectSourceIndexes(const QModelIndex &sourceIndex, int depth) const;
+
+ QVector<SourceInfo> m_sourceInfo;
+ ObjectListModel *m_sourceModel = nullptr;
+};
+
+
+#endif // OBJECTLISTMODEL_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.cpp
new file mode 100644
index 00000000..a82df79c
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.cpp
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSDMInspectable.h"
+#include "Qt3DSDMInspectorGroup.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Doc.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "Qt3DSDMSlides.h"
+#include "IDocumentReader.h"
+
+using namespace qt3dsdm;
+
+Qt3DSDMInspectable::Qt3DSDMInspectable(qt3dsdm::Qt3DSDMInstanceHandle instance,
+ qt3dsdm::Qt3DSDMInstanceHandle activeSlideInstance)
+ : m_instance(instance)
+ , m_activeSlideInstance(activeSlideInstance)
+{
+ QT3DS_ASSERT(getDoc()->GetDocumentReader().IsInstance(m_instance));
+
+ if (m_activeSlideInstance) {
+ // only active root scene or components set m_activeSlideInstance
+ auto *bridge = getDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ QT3DS_ASSERT(bridge->IsSceneInstance(instance)
+ || bridge->IsComponentInstance(instance));
+ }
+}
+
+// Returns the name of this inspectable
+Q3DStudio::CString Qt3DSDMInspectable::getName()
+{
+ auto *bridge = getDoc()->GetStudioSystem()->GetClientDataModelBridge();
+
+ if (!m_activeSlideInstance)
+ return bridge->GetName(m_instance, true);
+
+ Q3DStudio::CString theName = bridge->GetName(m_instance, true);
+ theName += " (";
+ theName += bridge->GetName(m_activeSlideInstance, true);
+ theName += ")";
+
+ return theName;
+}
+
+// Returns the number of groups in this inspectable
+long Qt3DSDMInspectable::getGroupCount() const
+{
+ IMetaData &theMetaData = *getDoc()->GetStudioSystem()->GetActionMetaData();
+ // In addition to a background group, Scene has a basic properties group (hidden in
+ // inspector) because it is derived from Asset. Until this is fixed properly, we force the
+ // Scene groups count to 1 (else an empty group will appear in the inspector).
+ long count = getObjectType() == OBJTYPE_SCENE ? 1
+ : long(theMetaData.GetGroupCountForInstance(m_instance));
+
+ if (m_activeSlideInstance)
+ count += long(theMetaData.GetGroupCountForInstance(m_activeSlideInstance));
+
+ return count;
+}
+
+// Return the property group for display
+CInspectorGroup *Qt3DSDMInspectable::getGroup(long inIndex)
+{
+ Qt3DSDMInspectorGroup *group = new Qt3DSDMInspectorGroup(GetGroupName(inIndex));
+
+ TMetaDataPropertyHandleList properties = GetGroupProperties(inIndex);
+
+ for (auto &prop : properties)
+ group->CreateRow(getDoc(), prop);
+
+ return group;
+}
+
+// Return the property handles for display, given the group index
+TMetaDataPropertyHandleList Qt3DSDMInspectable::GetGroupProperties(long inIndex)
+{
+ long activeGroupIdx = activeGroupIndex(inIndex);
+ TMetaDataPropertyHandleList retval;
+ IMetaData &theMetaData = *getDoc()->GetStudioSystem()->GetActionMetaData();
+ theMetaData.GetMetaDataProperties(GetGroupInstance(inIndex), retval);
+ qt3dsdm::IPropertySystem &thePropertySystem(*getDoc()->GetStudioSystem()->GetPropertySystem());
+ // get name of the current group for filtering
+ Option<qt3dsdm::TCharStr> theGroupFilterName =
+ theMetaData.GetGroupFilterNameForInstance(GetGroupInstance(inIndex), activeGroupIdx);
+ long theGroupCount = getGroupCount();
+
+ // end is explicitly required
+ for (size_t idx = 0; idx < retval.size(); ++idx) {
+ if (theMetaData.GetMetaDataPropertyInfo(retval[idx])->m_IsHidden) {
+ retval.erase(retval.begin() + idx);
+ --idx;
+ } else if (theGroupCount > 1 && theGroupFilterName.hasValue()
+ && theMetaData.GetMetaDataPropertyInfo(retval[idx])->m_GroupName
+ != theGroupFilterName) {
+ retval.erase(retval.begin() + idx);
+ --idx;
+ } else {
+ qt3ds::foundation::NVConstDataRef<SPropertyFilterInfo> theFilters(
+ theMetaData.GetMetaDataPropertyFilters(retval[idx]));
+ if (theFilters.size()) {
+ Option<bool> keepProperty;
+ // The tests are done in an ambiguous way. Really, show if equal should take
+ // multiple conditions
+ // as should hide if equal. They do not, so we need to rigorously define exactly
+ // how those two interact.
+ for (QT3DSU32 propIdx = 0, propEnd = theFilters.size(); propIdx < propEnd;
+ ++propIdx) {
+ const SPropertyFilterInfo &theFilter(theFilters[propIdx]);
+
+ QT3DS_ASSERT(theFilter.m_FilterType == PropertyFilterTypes::ShowIfEqual
+ || theFilter.m_FilterType == PropertyFilterTypes::HideIfEqual);
+
+ SValue theValue;
+ thePropertySystem.GetInstancePropertyValue(
+ GetGroupInstance(inIndex), theFilter.m_FilterProperty, theValue);
+ bool resultIfTrue = theFilter.m_FilterType == PropertyFilterTypes::ShowIfEqual;
+ if (Equals(theValue.toOldSkool(), theFilter.m_Value.toOldSkool())) {
+ keepProperty = resultIfTrue;
+ break;
+ } else {
+ keepProperty = !resultIfTrue;
+ }
+ }
+ if (keepProperty.hasValue() && *keepProperty == false) {
+ retval.erase(retval.begin() + idx);
+ --idx;
+ }
+ }
+ }
+ }
+ return retval;
+}
+
+// Return the Group Name, given the group index
+QString Qt3DSDMInspectable::GetGroupName(long groupIndex)
+{
+ std::vector<TCharStr> theGroupNames;
+ IMetaData &theMetaData = *getDoc()->GetStudioSystem()->GetActionMetaData();
+ theMetaData.GetGroupNamesForInstance(GetGroupInstance(groupIndex), theGroupNames);
+
+ long activeGroupIdx = activeGroupIndex(groupIndex);
+ if (activeGroupIdx < theGroupNames.size())
+ return Q3DStudio::CString(theGroupNames[activeGroupIdx].wide_str()).toQString();
+
+ return QObject::tr("Basic Properties");
+}
+
+// Return the Inspectable Instance Handle for the Group, given the group index
+Qt3DSDMInstanceHandle Qt3DSDMInspectable::GetGroupInstance(long inGroupIndex)
+{
+ // if active root, return the slide instance at first index
+ if (m_activeSlideInstance && inGroupIndex == 0)
+ return m_activeSlideInstance;
+
+ return m_instance;
+}
+
+EStudioObjectType Qt3DSDMInspectable::getObjectType() const
+{
+ return getDoc()->GetStudioSystem()->GetClientDataModelBridge()->GetObjectType(m_instance);
+}
+
+bool Qt3DSDMInspectable::isValid() const
+{
+ if (m_activeSlideInstance) {
+ return getDoc()->GetStudioSystem()->IsInstance(m_instance)
+ && getDoc()->GetStudioSystem()->IsInstance(m_activeSlideInstance);
+ }
+ return getDoc()->GetStudioSystem()->IsInstance(m_instance);
+}
+
+bool Qt3DSDMInspectable::isMaster() const
+{
+ ISlideSystem *slideSystem = getDoc()->GetStudioSystem()->GetSlideSystem();
+ qt3dsdm::Qt3DSDMSlideHandle theSlideHandle = slideSystem->GetAssociatedSlide(m_instance);
+ if (theSlideHandle.Valid())
+ return slideSystem->IsMasterSlide(theSlideHandle);
+ // Slide handle may not be valid if we are selecting the Scene or if we are inside Component and
+ // we select the Component root.
+ return false;
+}
+
+// Returns the group index taking into consideration that for active roots, first index is the slide
+// group so need to decrement all index bigger than 1, by 1. For scene we decrement 1 more because
+// the first group (Basic properties) is not in use.
+long Qt3DSDMInspectable::activeGroupIndex(long groupIndex) const
+{
+ if (m_activeSlideInstance && groupIndex > 0 && getObjectType() != OBJTYPE_SCENE)
+ return groupIndex - 1;
+
+ return groupIndex;
+}
+
+CDoc *Qt3DSDMInspectable::getDoc() const
+{
+ return g_StudioApp.GetCore()->GetDoc();
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.h
new file mode 100644
index 00000000..0da2916d
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectable.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_QT3DSDM_INSPECTABLE_H
+#define INCLUDED_QT3DSDM_INSPECTABLE_H
+
+#include "InspectableBase.h"
+#include "Qt3DSDMHandles.h"
+
+class CDoc;
+
+// For inspecting data model instances
+class Qt3DSDMInspectable : public CInspectableBase
+{
+public:
+ Qt3DSDMInspectable(qt3dsdm::Qt3DSDMInstanceHandle inInstance,
+ qt3dsdm::Qt3DSDMInstanceHandle activeSlideInstance = 0);
+
+ Q3DStudio::CString getName() override;
+ long getGroupCount() const override;
+ CInspectorGroup *getGroup(long) override;
+ EStudioObjectType getObjectType() const override;
+ bool isValid() const override;
+ bool isMaster() const override;
+ qt3dsdm::Qt3DSDMInstanceHandle getInstance() const override { return m_instance; }
+ virtual qt3dsdm::TMetaDataPropertyHandleList GetGroupProperties(long inGroupIndex);
+ virtual qt3dsdm::Qt3DSDMInstanceHandle GetGroupInstance(long inGroupIndex);
+
+protected:
+ qt3dsdm::Qt3DSDMInstanceHandle m_instance;
+ qt3dsdm::Qt3DSDMInstanceHandle m_activeSlideInstance;
+
+ virtual QString GetGroupName(long inGroupIndex);
+ CDoc *getDoc() const;
+ long activeGroupIndex(long groupIndex) const;
+};
+
+#endif
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.cpp
new file mode 100644
index 00000000..f62812ac
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSDMInspectorGroup.h"
+#include "Qt3DSDMInspectorRow.h"
+#include "Qt3DSDMInspectable.h"
+#include "Qt3DSDMMetaData.h"
+
+Qt3DSDMInspectorGroup::Qt3DSDMInspectorGroup(const QString &inName)
+ : CInspectorGroup(inName)
+{
+}
+
+Qt3DSDMInspectorGroup::~Qt3DSDMInspectorGroup()
+{
+ for (auto it = m_inspectorRows.begin(); it != m_inspectorRows.end(); ++it)
+ delete (*it);
+}
+
+// Create a new InspectorRowBase.
+void Qt3DSDMInspectorGroup::CreateRow(CDoc *inDoc,
+ qt3dsdm::Qt3DSDMMetaDataPropertyHandle inProperty)
+{
+ Q3DStudio::Qt3DSDMInspectorRow *theRow = new Q3DStudio::Qt3DSDMInspectorRow(inDoc, inProperty);
+ m_inspectorRows.push_back(theRow); // this Qt3DSDMInspectorRow is now owned by this class
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.h
new file mode 100644
index 00000000..c2bbc9fc
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorGroup.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_QT3DSDM_INSPECTORGROUP_H
+#define INCLUDED_QT3DSDM_INSPECTORGROUP_H
+
+#include "InspectorGroup.h"
+#include "Qt3DSDMHandles.h"
+
+class Qt3DSDMInspectable;
+class CDoc;
+
+namespace Q3DStudio {
+class Qt3DSDMInspectorRow;
+};
+
+class Qt3DSDMInspectorGroup : public CInspectorGroup
+{
+public:
+ Qt3DSDMInspectorGroup(const QString &inName);
+ ~Qt3DSDMInspectorGroup();
+
+ void CreateRow(CDoc *inDoc, qt3dsdm::Qt3DSDMMetaDataPropertyHandle inProperty);
+
+ const std::vector<Q3DStudio::Qt3DSDMInspectorRow *> &GetRows() const { return m_inspectorRows; }
+
+protected:
+ std::vector<Q3DStudio::Qt3DSDMInspectorRow *> m_inspectorRows;
+};
+
+#endif
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.cpp
new file mode 100644
index 00000000..3f629df4
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSDMInspectorRow.h"
+#include "Qt3DSDMMetaData.h"
+#include "Doc.h"
+#include "Qt3DSDMStudioSystem.h"
+
+using namespace qt3dsdm;
+
+namespace Q3DStudio {
+
+Qt3DSDMInspectorRow::Qt3DSDMInspectorRow(CDoc *inDoc, Qt3DSDMMetaDataPropertyHandle inProperty)
+ : m_MetaProperty(inProperty)
+{
+ IMetaData *theMetaData = inDoc->GetStudioSystem()->GetActionMetaData();
+ SMetaDataPropertyInfo theInfo(theMetaData->GetMetaDataPropertyInfo(inProperty));
+ m_MetaDataPropertyInfo = theInfo;
+}
+
+Qt3DSDMInspectorRow::~Qt3DSDMInspectorRow()
+{
+}
+
+} // namespace Q3DStudio
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.h
new file mode 100644
index 00000000..6c8156c1
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMInspectorRow.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+
+#include "Qt3DSDMHandles.h"
+#include "Qt3DSDMMetaDataTypes.h"
+
+class CDoc;
+
+namespace Q3DStudio {
+
+// This is a binding between a DataModelInspectable and an InspectorRow
+class Qt3DSDMInspectorRow
+{
+public:
+ explicit Qt3DSDMInspectorRow(CDoc *inDoc, qt3dsdm::Qt3DSDMMetaDataPropertyHandle inProperty);
+ virtual ~Qt3DSDMInspectorRow();
+
+ qt3dsdm::Qt3DSDMMetaDataPropertyHandle GetMetaDataProperty() const { return m_MetaProperty; }
+
+ const qt3dsdm::SMetaDataPropertyInfo &GetMetaDataPropertyInfo() const
+ {
+ return m_MetaDataPropertyInfo;
+ }
+
+protected:
+ qt3dsdm::Qt3DSDMMetaDataPropertyHandle m_MetaProperty;
+ qt3dsdm::SMetaDataPropertyInfo m_MetaDataPropertyInfo;
+};
+
+} // namespace Q3DStudio
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.cpp
new file mode 100644
index 00000000..69d86623
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSDMMaterialInspectable.h"
+
+using namespace qt3dsdm;
+
+Qt3DSDMMaterialInspectorGroup::Qt3DSDMMaterialInspectorGroup(const QString &inName)
+ : Qt3DSDMInspectorGroup(inName)
+ , m_isMaterialGroup(inName == QLatin1String("Material"))
+{
+}
+
+CInspectorGroup *Qt3DSDMMaterialInspectable::getGroup(long inIndex)
+{
+ Qt3DSDMInspectorGroup *group = new Qt3DSDMMaterialInspectorGroup(GetGroupName(inIndex));
+
+ TMetaDataPropertyHandleList properties = GetGroupProperties(inIndex);
+
+ for (auto &prop : properties)
+ group->CreateRow(getDoc(), prop);
+
+ return group;
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.h
new file mode 100644
index 00000000..0367bb7a
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/Qt3DSDMMaterialInspectable.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_QT3DSDM_MATERIAL_INSPECTABLE_H
+#define INCLUDED_QT3DSDM_MATERIAL_INSPECTABLE_H
+
+#include "Qt3DSDMInspectable.h"
+#include "Qt3DSDMInspectorGroup.h"
+
+class Qt3DSDMMaterialInspectorGroup : public Qt3DSDMInspectorGroup
+{
+public:
+ Qt3DSDMMaterialInspectorGroup(const QString &inName);
+
+ bool isMaterialGroup() const { return m_isMaterialGroup; }
+
+private:
+ bool m_isMaterialGroup = false;
+};
+
+class Qt3DSDMMaterialInspectable : public Qt3DSDMInspectable
+{
+public:
+ Qt3DSDMMaterialInspectable(qt3dsdm::Qt3DSDMInstanceHandle inInstance)
+ : Qt3DSDMInspectable(inInstance)
+ {
+ }
+
+ CInspectorGroup *getGroup(long) override;
+};
+
+#endif
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.cpp
new file mode 100644
index 00000000..1f4b8632
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "TabOrderHandler.h"
+
+TabOrderHandler::TabOrderHandler(QObject *parent)
+ : QObject(parent)
+{
+
+}
+
+TabOrderHandler::~TabOrderHandler()
+{
+
+}
+
+void TabOrderHandler::addItem(int group, QQuickItem *item)
+{
+ m_itemMap[group].append(item);
+}
+
+void TabOrderHandler::clear()
+{
+ m_itemMap.clear();
+}
+
+void TabOrderHandler::clearGroup(int group)
+{
+ m_itemMap[group].clear();
+}
+
+void TabOrderHandler::tabNavigate(bool tabForward)
+{
+ // Find the currently focused control
+ for (int i = 0; i < m_itemMap.size(); i++) {
+ const QList<QQuickItem *> items = m_itemMap[i];
+ for (int j = 0; j < items.size(); j++) {
+ if (items[j]->hasActiveFocus()) {
+ if (tabForward)
+ nextItem(i, j)->forceActiveFocus(Qt::TabFocusReason);
+ else
+ previousItem(i, j)->forceActiveFocus(Qt::BacktabFocusReason);
+ return;
+ }
+ }
+ }
+ // Activate the first item if could not find currently focused item
+ for (int i = 0; i < m_itemMap.size(); i++) {
+ if (m_itemMap[i].size() > 0)
+ m_itemMap[i][0]->forceActiveFocus(Qt::TabFocusReason);
+ }
+}
+
+QQuickItem *TabOrderHandler::nextItem(int group, int index)
+{
+ if (m_itemMap[group].size() > index + 1) {
+ // Try next item in group
+ index++;
+ } else {
+ // Get item in next available group
+ int nextGroup = group + 1;
+ while (nextGroup != group) {
+ if (nextGroup >= m_itemMap.size())
+ nextGroup = 0;
+ if (m_itemMap[nextGroup].size() == 0)
+ nextGroup++;
+ else
+ group = nextGroup;
+ }
+ index = 0;
+ }
+ return m_itemMap[group][index];
+}
+
+QQuickItem *TabOrderHandler::previousItem(int group, int index)
+{
+ if (index - 1 >= 0) {
+ // Try previous item in group
+ index--;
+ } else {
+ // Get last item in previous available group
+ int nextGroup = group - 1;
+ while (nextGroup != group) {
+ if (nextGroup < 0)
+ nextGroup = m_itemMap.size() - 1;
+ if (m_itemMap[nextGroup].size() == 0)
+ nextGroup--;
+ else
+ group = nextGroup;
+ }
+ index = m_itemMap[group].size() - 1;
+ }
+ return m_itemMap[group][index];
+}
+
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.h
new file mode 100644
index 00000000..10c1d400
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/TabOrderHandler.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef TABORDERHANDLER_H
+#define TABORDERHANDLER_H
+
+#include <QtCore/qobject.h>
+#include <QtQuick/qquickitem.h>
+
+class TabOrderHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit TabOrderHandler(QObject *parent = nullptr);
+ ~TabOrderHandler();
+
+ Q_INVOKABLE void addItem(int group, QQuickItem *item);
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void clearGroup(int group);
+
+ void tabNavigate(bool tabForward);
+
+private:
+ QQuickItem *nextItem(int group, int index);
+ QQuickItem *previousItem(int group, int index);
+
+ QHash<int, QList<QQuickItem *> > m_itemMap;
+};
+
+class TabNavigable {
+public:
+ TabNavigable() : m_tabOrderHandler(new TabOrderHandler) {}
+ virtual ~TabNavigable() { delete m_tabOrderHandler; }
+ TabOrderHandler *tabOrderHandler() const { return m_tabOrderHandler; }
+
+private:
+ TabOrderHandler *m_tabOrderHandler;
+};
+
+#endif // TABORDERHANDLER_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooser.qml b/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooser.qml
new file mode 100644
index 00000000..2a406257
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooser.qml
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+ ListView {
+ id: listView
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: 4
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _textureChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: {
+ _textureChooserView.textureSelected(_textureChooserView.handle,
+ _textureChooserView.instance, filePath);
+ }
+ onDoubleClicked: {
+ _textureChooserView.textureSelected(_textureChooserView.handle,
+ _textureChooserView.instance, filePath);
+ _textureChooserView.hide();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.cpp
new file mode 100644
index 00000000..8804e675
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "TextureChooserView.h"
+#include "ImageChooserModel.h"
+#include "StudioPreferences.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "IDocumentEditor.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "Qt3DSDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "StudioPreferences.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+TextureChooserView::TextureChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new ImageChooserModel(false, this))
+{
+ setWindowTitle(tr("Texture"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &TextureChooserView::initialize);
+}
+
+void TextureChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty(QStringLiteral("_resDir"), StudioUtils::resourceImageUrl());
+ rootContext()->setContextProperty(QStringLiteral("_textureChooserView"), this);
+ rootContext()->setContextProperty(QStringLiteral("_textureChooserModel"), m_model);
+ engine()->addImportPath(StudioUtils::qmlImportPath());
+ setSource(QUrl(QStringLiteral("qrc:/Palettes/Inspector/TextureChooser.qml")));
+}
+
+void TextureChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+int TextureChooserView::handle() const
+{
+ return m_handle;
+}
+
+void TextureChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+int TextureChooserView::instance() const
+{
+ return m_instance;
+}
+
+QString TextureChooserView::currentDataModelPath() const
+{
+ QString cleanPath;
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ qt3dsdm::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ const QString path = qt3dsdm::get<QString>(value);
+
+ // An empty value can sometimes be represented by a relative path either to project root or the
+ // presentation file, such as"./" or "../", so let's just consider all directory paths as empty
+ if (path.isEmpty() || QFileInfo(path).isDir()) {
+ cleanPath = ChooserModelBase::noneString();
+ } else {
+ // If path is renderable id, we need to resolve the actual path
+ const QString renderablePath = g_StudioApp.getRenderableAbsolutePath(path);
+
+ if (renderablePath.isEmpty())
+ cleanPath = path;
+ else
+ cleanPath = renderablePath;
+
+ cleanPath = QDir::cleanPath(QDir(doc->GetDocumentDirectory()).filePath(cleanPath));
+ }
+ return cleanPath;
+}
+
+void TextureChooserView::updateSelection()
+{
+ m_model->setCurrentFile(currentDataModelPath());
+}
+
+void TextureChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ QTimer::singleShot(0, this, &TextureChooserView::close);
+}
+
+void TextureChooserView::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Escape)
+ QTimer::singleShot(0, this, &TextureChooserView::close);
+
+ QQuickWidget::keyPressEvent(event);
+}
+
+void TextureChooserView::showEvent(QShowEvent *event)
+{
+ updateSelection();
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.h
new file mode 100644
index 00000000..156b2465
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/TextureChooserView.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TEXTURECHOOSERVIEW_H
+#define TEXTURECHOOSERVIEW_H
+
+#include <QQuickWidget>
+
+class ImageChooserModel;
+
+class TextureChooserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int instance READ instance)
+ Q_PROPERTY(int handle READ handle)
+
+public:
+ explicit TextureChooserView(QWidget *parent = nullptr);
+
+ void setHandle(int handle);
+ int handle() const;
+
+ void setInstance(int instance);
+ int instance() const;
+ QString currentDataModelPath() const;
+
+ void updateSelection();
+
+Q_SIGNALS:
+ void textureSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ int m_handle = -1;
+ int m_instance = -1;
+ ImageChooserModel *m_model = nullptr;
+};
+
+#endif // TEXTURECHOOSERVIEW_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.cpp
new file mode 100644
index 00000000..05a8fa64
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "VariantTagDialog.h"
+#include "ui_VariantTagDialog.h"
+#include "Dialogs.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "ProjectFile.h"
+
+#include <QtGui/qvalidator.h>
+
+VariantTagDialog::VariantTagDialog(DialogType type, const QString &group, const QString &name,
+ QWidget *parent)
+ : QDialog(parent)
+ , m_type(type)
+ , m_group(group)
+ , m_ui(new Ui::VariantTagDialog)
+{
+ m_ui->setupUi(this);
+
+ m_names.first = name;
+
+ QRegExpValidator *rgx = new QRegExpValidator(QRegExp("[\\w\\s]+"), this);
+ m_ui->lineEditTagName->setValidator(rgx);
+
+ if (type == AddGroup) {
+ setWindowTitle(tr("Add new Group"));
+ m_ui->label->setText(tr("Group name"));
+ } else if (type == RenameGroup) {
+ setWindowTitle(tr("Rename Group"));
+ m_ui->label->setText(tr("Group name"));
+ m_ui->lineEditTagName->setText(name);
+ m_ui->lineEditTagName->selectAll();
+ } else if (type == RenameTag) {
+ m_ui->lineEditTagName->setText(name);
+ m_ui->lineEditTagName->selectAll();
+ }
+
+ window()->setFixedSize(size());
+}
+
+void VariantTagDialog::accept()
+{
+ QString name = m_ui->lineEditTagName->text();
+
+ if (name.isEmpty()) {
+ displayWarning(EmptyWarning);
+ } else if (name == m_names.first) { // no change
+ QDialog::reject();
+ } else if (((m_type == AddGroup || m_type == RenameGroup)
+ && !g_StudioApp.GetCore()->getProjectFile().isVariantGroupUnique(name))
+ || (!g_StudioApp.GetCore()->getProjectFile().isVariantTagUnique(m_group, name))) {
+ displayWarning(UniqueWarning);
+ } else {
+ m_names.second = name;
+ QDialog::accept();
+ }
+}
+
+std::pair<QString, QString> VariantTagDialog::getNames() const
+{
+ return m_names;
+}
+
+void VariantTagDialog::displayWarning(WarningType warningType)
+{
+ QString warning;
+ if (warningType == EmptyWarning) {
+ if (m_type == AddGroup || m_type == RenameGroup)
+ warning = tr("The group name must not be empty.");
+ else
+ warning = tr("The tag name must not be empty.");
+ } else if (warningType == UniqueWarning) {
+ if (m_type == AddGroup || m_type == RenameGroup)
+ warning = tr("The group name must be unique.");
+ else
+ warning = tr("The tag name must be unique within the tag group.");
+ }
+
+ g_StudioApp.GetDialogs()->DisplayMessageBox(tr("Warning"), warning,
+ Qt3DSMessageBox::ICON_WARNING, false);
+}
+
+VariantTagDialog::~VariantTagDialog()
+{
+ delete m_ui;
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.h
new file mode 100644
index 00000000..b5e3989f
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VARIANTTAGDIALOG_H
+#define VARIANTTAGDIALOG_H
+
+#include <QtWidgets/qdialog.h>
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class VariantTagDialog;
+}
+QT_END_NAMESPACE
+
+class VariantTagDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ enum DialogType { AddTag, RenameTag, AddGroup, RenameGroup };
+
+ explicit VariantTagDialog(DialogType type, const QString &group = {}, const QString &name = {},
+ QWidget *parent = nullptr);
+ ~VariantTagDialog() override;
+
+public Q_SLOTS:
+ void accept() override;
+ std::pair<QString, QString> getNames() const;
+
+private:
+ enum WarningType {
+ EmptyWarning,
+ UniqueWarning
+ };
+
+ void displayWarning(WarningType warningType);
+
+ DialogType m_type;
+ QString m_group;
+ Ui::VariantTagDialog *m_ui;
+ std::pair<QString, QString> m_names; // holds the tags values before and after rename
+};
+
+#endif // VARIANTTAGDIALOG_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.ui b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.ui
new file mode 100644
index 00000000..2c0b2863
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantTagDialog.ui
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VariantTagDialog</class>
+ <widget class="QDialog" name="VariantTagDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>300</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Add new Tag</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QWidget" name="labelEditLayout" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Tag name</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="lineEditTagName"/>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>VariantTagDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>VariantTagDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.cpp
new file mode 100644
index 00000000..066f912a
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "VariantsGroupModel.h"
+#include "VariantsTagModel.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "MainFrm.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentEditor.h"
+#include "VariantTagDialog.h"
+#include "StudioUtils.h"
+#include "Dialogs.h"
+
+#include <QtCore/qsavefile.h>
+
+VariantsGroupModel::VariantsGroupModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+
+}
+
+void VariantsGroupModel::refresh()
+{
+ int instance = g_StudioApp.GetCore()->GetDoc()->GetSelectedInstance();
+ auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+
+ if (instance == 0 || bridge->GetObjectType(instance) & ~OBJTYPE_IS_VARIANT) {
+ m_instance = 0;
+ m_property = 0;
+ return;
+ }
+
+ auto propertySystem = g_StudioApp.GetCore()->GetDoc()->GetPropertySystem();
+ m_instance = instance;
+ m_property = bridge->getVariantsProperty(instance);
+
+ qt3dsdm::SValue sValue;
+ if (propertySystem->GetInstancePropertyValue(m_instance, m_property, sValue)) {
+ beginResetModel();
+
+ // delete tag models
+ for (auto &g : qAsConst(m_data))
+ delete g.m_tagsModel;
+
+ m_data.clear();
+
+ QString propVal = qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue)->toQString();
+ QHash<QString, QStringList> propTags;
+ if (!propVal.isEmpty()) {
+ const QStringList propTagsList = propVal.split(QChar(','));
+ for (auto &propTag : propTagsList) {
+ const QStringList propTagPair = propTag.split(QChar(':'));
+ propTags[propTagPair[0]].append(propTagPair[1]);
+ }
+ }
+
+ // build the variants data model
+ const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef();
+ const auto keys = g_StudioApp.GetCore()->getProjectFile().variantsDefKeys();
+ for (auto &group : keys) {
+ TagGroupData g;
+ g.m_title = group;
+ g.m_color = variantsDef[group].m_color;
+
+ QVector<std::pair<QString, bool> > tags;
+ for (int i = 0; i < variantsDef[group].m_tags.length(); ++i) {
+ tags.append({variantsDef[group].m_tags[i],
+ propTags[group].contains(variantsDef[group].m_tags[i])});
+ }
+
+ g.m_tagsModel = new VariantsTagModel(tags);
+ m_data.push_back(g);
+ }
+
+ endResetModel();
+
+ bool isVariantsEmpty = rowCount() == 0;
+ if (m_variantsEmpty != isVariantsEmpty) {
+ m_variantsEmpty = isVariantsEmpty;
+ Q_EMIT varaintsEmptyChanged();
+ }
+ }
+}
+
+int VariantsGroupModel::rowCount(const QModelIndex &parent) const
+{
+ // For list models only the root node (an invalid parent) should return the list's size. For all
+ // other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
+ if (parent.isValid())
+ return 0;
+
+ return m_data.size();
+}
+
+QVariant VariantsGroupModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role == GroupTitleRole)
+ return m_data.at(index.row()).m_title;
+ else if (role == GroupColorRole)
+ return m_data.at(index.row()).m_color;
+ else if (role == TagRole)
+ return QVariant::fromValue(m_data.at(index.row()).m_tagsModel);
+
+ return QVariant();
+}
+
+void VariantsGroupModel::setTagState(const QString &group, const QString &tag, bool selected)
+{
+ QString val;
+ QString tagsStr;
+ bool skipFirst = false;
+ for (auto &g : qAsConst(m_data)) {
+ if (g.m_title == group)
+ g.m_tagsModel->updateTagState(tag, selected);
+
+ tagsStr = g.m_tagsModel->serialize(g.m_title);
+ if (!tagsStr.isEmpty()) {
+ if (skipFirst)
+ val.append(QChar(','));
+ val.append(tagsStr);
+ skipFirst = true;
+ }
+ }
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(m_instance, m_property, QVariant(val));
+}
+
+void VariantsGroupModel::addNewTag(const QString &group)
+{
+ VariantTagDialog dlg(VariantTagDialog::AddTag, group);
+
+ if (dlg.exec() == QDialog::Accepted) {
+ g_StudioApp.GetCore()->getProjectFile().addVariantTag(group, dlg.getNames().second);
+ refresh();
+
+ if (g_StudioApp.GetCore()->getProjectFile().variantsDef()[group].m_tags.size() == 1)
+ g_StudioApp.m_pMainWnd->updateActionFilterEnableState();
+ }
+}
+
+void VariantsGroupModel::addNewGroup()
+{
+ VariantTagDialog dlg(VariantTagDialog::AddGroup);
+
+ if (dlg.exec() == QDialog::Accepted) {
+ g_StudioApp.GetCore()->getProjectFile().addVariantGroup(dlg.getNames().second);
+ refresh();
+ }
+}
+
+void VariantsGroupModel::importVariants()
+{
+ QString importFilePath = g_StudioApp.GetDialogs()->getImportVariantsDlg();
+
+ if (!importFilePath.isEmpty()) {
+ g_StudioApp.GetCore()->getProjectFile().loadVariants(importFilePath);
+ refresh();
+ }
+}
+
+void VariantsGroupModel::exportVariants()
+{
+ QString exportFilePath = g_StudioApp.GetDialogs()->getExportVariantsDlg();
+
+ if (exportFilePath.isEmpty())
+ return;
+
+ QDomDocument domDoc;
+ domDoc.appendChild(domDoc.createProcessingInstruction(QStringLiteral("xml"),
+ QStringLiteral("version=\"1.0\""
+ " encoding=\"utf-8\"")));
+
+ const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef();
+ const auto keys = g_StudioApp.GetCore()->getProjectFile().variantsDefKeys();
+ QDomElement vElem = domDoc.createElement(QStringLiteral("variants"));
+ domDoc.appendChild(vElem);
+ for (auto &g : keys) {
+ const auto group = variantsDef[g];
+ QDomElement gElem = domDoc.createElement(QStringLiteral("variantgroup"));
+ gElem.setAttribute(QStringLiteral("id"), g);
+ gElem.setAttribute(QStringLiteral("color"), group.m_color);
+ vElem.appendChild(gElem);
+
+ for (auto &t : qAsConst(group.m_tags)) {
+ QDomElement tElem = domDoc.createElement(QStringLiteral("variant"));;
+ tElem.setAttribute(QStringLiteral("id"), t);
+ gElem.appendChild(tElem);
+ }
+ }
+
+ QSaveFile file(exportFilePath);
+ if (StudioUtils::openTextSave(file))
+ StudioUtils::commitDomDocumentSave(file, domDoc);
+}
+
+QHash<int, QByteArray> VariantsGroupModel::roleNames() const
+{
+ auto names = QAbstractListModel::roleNames();
+ names.insert(GroupTitleRole, "group");
+ names.insert(GroupColorRole, "color");
+ names.insert(TagRole, "tags");
+ return names;
+}
+
+Qt::ItemFlags VariantsGroupModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ return Qt::ItemIsEditable;
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.h
new file mode 100644
index 00000000..75a218f7
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsGroupModel.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VARIANTSGROUPMODEL_H
+#define VARIANTSGROUPMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+
+class VariantsTagModel;
+
+class VariantsGroupModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(bool variantsEmpty MEMBER m_variantsEmpty NOTIFY varaintsEmptyChanged)
+
+public:
+Q_SIGNALS:
+ void varaintsEmptyChanged();
+
+public:
+ explicit VariantsGroupModel(QObject *parent = nullptr);
+
+ enum Roles {
+ GroupTitleRole = Qt::UserRole + 1,
+ GroupColorRole,
+ TagRole
+ };
+
+ struct TagGroupData
+ {
+ QString m_title;
+ QString m_color;
+ VariantsTagModel *m_tagsModel = nullptr;
+ };
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = GroupTitleRole) const override;
+
+ Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+ void refresh();
+
+ Q_INVOKABLE void setTagState(const QString &group, const QString &tag, bool selected);
+ Q_INVOKABLE void addNewTag(const QString &group);
+ Q_INVOKABLE void addNewGroup();
+ Q_INVOKABLE void importVariants();
+ Q_INVOKABLE void exportVariants();
+
+
+protected:
+ QHash<int, QByteArray> roleNames() const override;
+
+private:
+ QVector<TagGroupData> m_data;
+ int m_instance = 0; // selected layer instance
+ int m_property = 0; // variant tags property handler
+ bool m_variantsEmpty = true; // no groups (nor tags)
+};
+
+#endif // VARIANTSGROUPMODEL_H
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.cpp
new file mode 100644
index 00000000..30f33635
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "VariantsTagModel.h"
+
+VariantsTagModel::VariantsTagModel(const QVector<std::pair<QString, bool> > &data, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_data(data)
+{
+
+}
+
+void VariantsTagModel::updateTagState(const QString &tag, bool selected)
+{
+ for (auto &t : m_data) {
+ if (t.first == tag) {
+ t.second = selected;
+ break;
+ }
+ }
+}
+
+// return the tags in a formatted string to be saved to the property
+QString VariantsTagModel::serialize(const QString &groupName) const
+{
+ QString ret;
+ bool skipFirst = false;
+ for (auto &t : qAsConst(m_data)) {
+ if (t.second) {
+ if (skipFirst)
+ ret.append(QLatin1Char(','));
+
+ ret.append(groupName + QLatin1Char(':') + t.first);
+
+ skipFirst = true;
+ }
+ }
+
+ return ret;
+}
+
+int VariantsTagModel::rowCount(const QModelIndex &parent) const
+{
+ // For list models only the root node (an invalid parent) should return the list's size. For all
+ // other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
+ if (parent.isValid())
+ return 0;
+
+ return m_data.size();
+}
+
+QVariant VariantsTagModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role == TagRole)
+ return m_data.at(index.row()).first;
+ else if (role == SelectedRole)
+ return m_data.at(index.row()).second;
+
+ return QVariant();
+}
+
+QHash<int, QByteArray> VariantsTagModel::roleNames() const
+{
+ auto names = QAbstractListModel::roleNames();
+ names.insert(TagRole, "tag");
+ names.insert(SelectedRole, "selected");
+ return names;
+}
+
+Qt::ItemFlags VariantsTagModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ return Qt::ItemIsEditable; // FIXME: Implement me!
+}
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.h b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.h
new file mode 100644
index 00000000..392de760
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/VariantsTagModel.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VARIANTSTAGMODEL_H
+#define VARIANTSTAGMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+
+class VariantsTagModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit VariantsTagModel(const QVector<std::pair<QString, bool> > &data,
+ QObject *parent = nullptr);
+
+ enum Roles {
+ TagRole = Qt::UserRole + 1,
+ SelectedRole,
+ };
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = TagRole) const override;
+
+ Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+ void updateTagState(const QString &tag, bool selected);
+ QString serialize(const QString &groupName) const;
+
+protected:
+ QHash<int, QByteArray> roleNames() const override;
+
+private:
+ QVector<std::pair<QString, bool> > m_data; // [{tagName, selectedState}, ...]
+};
+
+#endif // VARIANTSTAGMODEL_H