summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp')
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp964
1 files changed, 964 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp
new file mode 100644
index 00000000..f991f467
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlView.cpp
@@ -0,0 +1,964 @@
+/****************************************************************************
+**
+** 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 "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);
+
+ m_shaderStatusUpdateTimer.setSingleShot(true);
+ m_shaderStatusUpdateTimer.setInterval(3000);
+
+ connect(&m_shaderStatusUpdateTimer, &QTimer::timeout, [&](){
+ m_inspectorControlModel->refresh();
+ });
+
+ 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 *effectExtensions[] = {
+ L"effect", 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
+ m_shaderStatusUpdateTimer.start();
+ } 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();
+ } else if (isInList(effectExtensions, record.m_File.GetExtension())) {
+ m_shaderStatusUpdateTimer.start();
+ }
+ }
+ }
+ 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
+{
+ return getBridge()->IsReferencedMaterialInstance(instance);
+}
+
+QString InspectorControlView::noneString() const
+{
+ return ChooserModelBase::noneString();
+}
+
+bool InspectorControlView::canLinkProperty(int instance, int handle) const
+{
+ if (!instance || !handle)
+ return false;
+
+ if (getBridge()->isInsideMaterialContainer(instance))
+ return false;
+
+ if (handle == getBridge()->GetSceneAsset().m_Eyeball.m_Property) // eyeball is unlinkable
+ return false;
+
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+
+ // Disallow linking of properties that refer to images as unlinking them is not trivial at all.
+ qt3dsdm::AdditionalMetaDataType::Value thePropertyMetaData =
+ doc->GetStudioSystem()->GetPropertySystem()->GetAdditionalMetaDataType(instance, handle);
+ if (thePropertyMetaData == qt3dsdm::AdditionalMetaDataType::Image)
+ 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();
+
+ qt3dsdm::SValue value;
+ doc->GetPropertySystem()->GetInstancePropertyValue(m_contextMenuInstance, m_contextMenuHandle,
+ value);
+ qt3dsdm::SLong4 guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+ const auto instance = getBridge()->GetInstanceByGUID(guid);
+ doc->SelectDataModelObject(instance);
+}
+
+void InspectorControlView::onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance,
+ qt3dsdm::Qt3DSDMPropertyHandle inProperty)
+{
+ m_inspectorControlModel->notifyPropertyChanged(inInstance, inProperty);
+
+ // 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 == getBridge()->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();
+
+ if (getBridge()->IsCustomMaterialInstance(inChild)) {
+ QVector<qt3dsdm::Qt3DSDMInstanceHandle> refMats;
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ doc->getSceneReferencedMaterials(doc->GetSceneInstance(), refMats);
+ for (auto &refMat : qAsConst(refMats)) {
+ if (int(getBridge()->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()) {
+ delete inspectable;
+ 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)) {
+ qt3dsdm::Qt3DSDMSlideHandle activeSlide = doc->GetActiveSlide();
+
+ // Scene or Component (when being edited)
+ if (selectedInstance == getBridge()->GetOwningComponentInstance(activeSlide)) {
+ Qt3DSDMInstanceHandle activeSlideInstance = doc->GetStudioSystem()
+ ->GetSlideSystem()->GetSlideInstance(activeSlide);
+ inspectableBase = new Qt3DSDMInspectable(selectedInstance,
+ activeSlideInstance);
+ } 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();
+
+ CInspectableBase *oldInspectableBase = m_inspectableBase;
+
+ m_inspectableBase = inInspectable;
+ m_inspectorControlModel->setInspectable(inInspectable);
+
+ // Delete old inspectable base only after setting the new inspectable is completed,
+ // as model may still need it to commit pending transaction
+ delete oldInspectableBase;
+
+ 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;
+
+
+ if (canOpenInInspector(instance, handle)) {
+ auto action = theContextMenu.addAction(tr("Open in Inspector"));
+ connect(action, &QAction::triggered, this, &InspectorControlView::openInInspector);
+ }
+
+ if (canLinkProperty(instance, handle)) {
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ 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);
+ }
+ }
+ });
+ }
+
+ // For basic materials edit IBL Override of the referenced material. This applies only for basic
+ // materials as IBL Override is disabled for referenced (and default basic) materials.
+ if (handle == getBridge()->GetObjectDefinitions().m_MaterialBase.m_IblProbe.m_Property) {
+ int refInstance = getBridge()->getMaterialReference(instance);
+ if (refInstance)
+ instance = refInstance;
+ }
+
+ 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) {
+ showAlpha = getBridge()->getBGColorProperty(instance).GetHandleValue() == handle
+ || getBridge()->getTextColorProperty(instance).GetHandleValue() == handle
+ || getBridge()->IsCustomMaterialInstance(instance);
+ }
+
+ 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() const
+{
+ return CStudioPreferences::isTooltipsOn();
+}
+
+// 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()));
+ }
+ }
+ }
+}
+
+CClientDataModelBridge *InspectorControlView::getBridge() const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+}
+
+void InspectorControlView::OnImmediateRefreshInstanceSingle(qt3dsdm::Qt3DSDMInstanceHandle inInstance)
+{
+ m_inspectorControlModel->refresh();
+}
+
+void InspectorControlView::OnImmediateRefreshInstanceMultiple(qt3dsdm::Qt3DSDMInstanceHandle *inInstance, long inInstanceCount)
+{
+ m_inspectorControlModel->refresh();
+}