summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Studio/Palettes
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Studio/Palettes')
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp11
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h5
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp64
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h4
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml158
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.cpp106
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.h73
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.ui113
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.cpp181
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.h77
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.cpp108
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.h62
12 files changed, 956 insertions, 6 deletions
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp
index d4fdc306..1232939c 100644
--- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp
@@ -62,6 +62,7 @@
#include "foundation/Qt3DSLogging.h"
#include "Dialogs.h"
#include "Dispatch.h"
+#include "VariantsGroupModel.h"
static QStringList renderableItems()
{
@@ -107,8 +108,9 @@ static std::pair<bool, bool> getSlideCharacteristics(qt3dsdm::Qt3DSDMInstanceHan
return std::make_pair(hasNextSlide, hasPreviousSlide);
}
-InspectorControlModel::InspectorControlModel(QObject *parent)
- : QAbstractListModel(parent)
+InspectorControlModel::InspectorControlModel(VariantsGroupModel *variantsModel, QObject *parent)
+ : m_variantsModel(variantsModel)
+ , QAbstractListModel(parent)
, m_UpdatableEditor(*g_StudioApp.GetCore()->GetDoc())
{
m_modifiedProperty.first = 0;
@@ -1229,7 +1231,6 @@ void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) c
metaDataProvider->GetMetaDataProperty(instance, element->m_property));
}
-
bool skipEmits = false;
switch (element->m_dataType) {
case qt3dsdm::DataModelDataType::String: {
@@ -1239,6 +1240,10 @@ void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) c
if (index != -1)
stringValue = stringValue.mid(index + 1);
}
+
+ if (bridge->IsLayerInstance(instance))
+ m_variantsModel->refresh();
+
element->m_value = stringValue;
} // intentional fall-through for other String-derived datatypes
case qt3dsdm::DataModelDataType::StringOrInt:
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h
index e329dba1..21e6b3a7 100644
--- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h
@@ -42,6 +42,7 @@
class CInspectableBase;
class Qt3DSDMInspectable;
class SGuideInspectableImpl;
+class VariantsGroupModel;
namespace qt3dsdm {
class ISignalConnection;
@@ -107,7 +108,7 @@ class InspectorControlModel : public QAbstractListModel
{
Q_OBJECT
public:
- explicit InspectorControlModel(QObject *parent);
+ explicit InspectorControlModel(VariantsGroupModel *variantsModel, QObject *parent);
~InspectorControlModel() = default;
enum Roles {
@@ -247,6 +248,8 @@ private:
bool isGroupRebuildRequired(CInspectableBase *inspectable, int theIndex) const;
static int handleToGuidePropIndex(int handle) { return handle - 1; }
+
+ VariantsGroupModel *m_variantsModel = nullptr;
};
#endif // INSPECTORCONTROLMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp
index 69543a12..6252c0ea 100644
--- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp
@@ -58,6 +58,8 @@
#include "MaterialRefView.h"
#include "BasicObjectsModel.h"
#include "Qt3DSDMSlides.h"
+#include "VariantsGroupModel.h"
+#include "VariantTagDialog.h"
#include <QtCore/qtimer.h>
#include <QtQml/qqmlcontext.h>
@@ -69,7 +71,8 @@
InspectorControlView::InspectorControlView(const QSize &preferredSize, QWidget *parent)
: QQuickWidget(parent),
TabNavigable(),
- m_inspectorControlModel(new InspectorControlModel(this)),
+ m_variantsGroupModel(new VariantsGroupModel(this)),
+ m_inspectorControlModel(new InspectorControlModel(m_variantsGroupModel, this)),
m_meshChooserView(new MeshChooserView(this)),
m_instance(0),
m_handle(0),
@@ -241,6 +244,7 @@ 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);
@@ -412,6 +416,8 @@ void InspectorControlView::setInspectable(CInspectableBase *inInspectable)
m_inspectorControlModel->setInspectable(inInspectable);
Q_EMIT titleChanged();
+
+ m_variantsGroupModel->refresh();
}
}
@@ -452,6 +458,62 @@ void InspectorControlView::showContextMenu(int x, int y, int handle, int instanc
m_handle = 0;
}
+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);
+ }
+ });
+
+ auto actionDelete = theContextMenu.addAction(QObject::tr("Delete Tag"));
+ connect(actionDelete, &QAction::triggered, this, [&]() {
+ g_StudioApp.GetCore()->getProjectFile().deleteVariantTag(group, tag);
+ });
+
+ theContextMenu.exec(mapToGlobal({x, y}));
+}
+
+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);
+ }
+ });
+
+ auto actionColor = theContextMenu.addAction(QObject::tr("Change Group Color"));
+ connect(actionColor, &QAction::triggered, this, [&]() {
+ const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef();
+ for (auto &g : variantsDef) {
+ if (g.m_title == group) {
+ QColor newColor = this->showColorDialog(g.m_color);
+ projectFile.changeVariantGroupColor(group, newColor.name());
+ break;
+ }
+ }
+ });
+
+ auto actionDelete = theContextMenu.addAction(QObject::tr("Delete Group"));
+ connect(actionDelete, &QAction::triggered, this, [&]() {
+ projectFile.deleteVariantGroup(group);
+ });
+
+ theContextMenu.exec(mapToGlobal({x, y}));
+}
+
void InspectorControlView::toggleMasterLink()
{
Q3DStudio::ScopedDocumentEditor editor(*g_StudioApp.GetCore()->GetDoc(),
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h
index 98d67f41..14936b57 100644
--- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h
@@ -39,6 +39,7 @@
#include "DataInputSelectView.h"
class InspectorControlModel;
+class VariantsGroupModel;
class CInspectableBase;
class ImageChooserView;
class DataInputSelectView;
@@ -72,6 +73,8 @@ public:
QString titleIcon() const;
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 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);
@@ -126,6 +129,7 @@ private:
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;
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml
index b2e8b286..833bbd65 100644
--- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml
@@ -28,6 +28,8 @@
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"
@@ -286,6 +288,7 @@ Rectangle {
ColumnLayout { // Property row and datainput control
Layout.alignment: Qt.AlignTop
+ visible: modelData.title !== "variants"
spacing: 0
RowLayout { // Property row
Layout.alignment: Qt.AlignLeft
@@ -315,7 +318,7 @@ Rectangle {
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.LeftButton
hoverEnabled: true
- onClicked: {
+ onClicked: {
if (mouse.button === Qt.LeftButton) {
_inspectorModel.setPropertyAnimated(
model.modelData.instance,
@@ -423,6 +426,9 @@ Rectangle {
opacity: enabled ? 1 : .5
Layout.alignment: Qt.AlignTop
sourceComponent: {
+ if (modelData.title === "variants")
+ return variantTagsComponent;
+
const dataType = modelData.dataType;
switch (dataType) {
case DataModelDataType.Long:
@@ -1098,4 +1104,154 @@ Rectangle {
}
}
}
+
+ 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...")
+ width: 70
+ height: 20
+
+ onClicked: {
+ // TODO: implement
+ }
+ }
+
+ ToolButton {
+ id: exportButton
+ text: qsTr("Export...")
+ width: 70
+ height: 20
+
+ onClicked: {
+ // TODO: implement
+ }
+ }
+ }
+
+ Text {
+ text: qsTr("There are no variant tags yet. Click [+ Group] to add a new tag group and start adding tags.")
+ color: "#ffffff"
+ visible: _variantsGroupModel.rowCount() === 0
+ }
+
+ Repeater {
+ id: tagsReeater
+ model: _variantsGroupModel
+
+ Row {
+ id: variantTagsRow
+
+ readonly property var tagsModel: model.tags
+ readonly property var groupModel: model
+
+ Text {
+ text: model.group
+ color: model.color
+ width: 50
+ 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")
+ height: 25
+
+ onClicked: {
+ _variantsGroupModel.addNewTag(groupModel.group)
+ }
+
+ }
+ }
+ }
+ }
+
+ Item { width: 1; height: 5 } // vertical spacer
+
+ ToolButton {
+ id: addGroupButton
+ text: qsTr("+ Group")
+ width: 60
+ height: 25
+ onClicked: {
+ _variantsGroupModel.addNewGroup()
+ }
+ }
+
+ Item { width: 1; height: 5 } // vertical spacer
+ }
+ }
+
+ Component {
+ id: tagComponent
+
+ Rectangle {
+ property bool toggled: tagsModel.selected
+ property string grpColor: grpModel ? grpModel.color : ""
+
+ width: Math.max(tLabel.width + 10, 60)
+ height: 25
+ color: toggled ? grpColor : "#2e2f30"
+ border.color: "#959596"
+
+ Text {
+ id: tLabel
+ anchors.centerIn: parent
+ text: tagsModel.tag
+ color: toggled ? "#ffffff" : "#959596"
+ }
+
+ 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/Studio/Palettes/Inspector/VariantTagDialog.cpp b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.cpp
new file mode 100644
index 00000000..83e72e7b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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"
+
+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;
+
+ 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();
+ }
+}
+
+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/Studio/Palettes/Inspector/VariantTagDialog.h b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.h
new file mode 100644
index 00000000..b5e3989f
--- /dev/null
+++ b/src/Authoring/Studio/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/Studio/Palettes/Inspector/VariantTagDialog.ui b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.ui
new file mode 100644
index 00000000..aa5d24ef
--- /dev/null
+++ b/src/Authoring/Studio/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>241</width>
+ <height>89</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/Studio/Palettes/Inspector/VariantsGroupModel.cpp b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.cpp
new file mode 100644
index 00000000..3a2a2a45
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** 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 "QDebug" // TODO: remove
+#include "Qt3DSDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentEditor.h"
+#include "VariantTagDialog.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->IsLayerInstance(instance)) {
+ m_instance = 0;
+ m_property = 0;
+ return;
+ }
+
+ auto propertySystem = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetPropertySystem();
+ m_instance = instance;
+ m_property = propertySystem->GetAggregateInstancePropertyByName(instance, L"variants");
+
+ qt3dsdm::SValue sValue;
+ if (propertySystem->GetInstancePropertyValue(m_instance, m_property, sValue)) {
+ beginResetModel();
+ m_data.clear();
+
+ QString val = QString::fromWCharArray(
+ qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue)->GetData());
+ // TODO: remove qDebug when the variants work is fully done.
+ qDebug() << "\x1b[42m \x1b[1m" << __FUNCTION__
+ << ", val=" << val
+ << "\x1b[m";
+ QHash<QString, QStringList> propTags;
+ if (!val.isEmpty()) {
+ const QStringList propTagsList = val.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();
+ for (auto &group : variantsDef) {
+ TagGroupData g;
+ g.m_title = group.m_title;
+ g.m_color = group.m_color;
+
+ VariantsTagModel *m = new VariantsTagModel(this);
+ QVector<std::pair<QString, bool> > tags;
+ for (int i = 0; i < group.m_tags.length(); ++i)
+ tags.append({group.m_tags[i], propTags[group.m_title].contains(group.m_tags[i])});
+
+ m->init(tags);
+ g.m_tagsModel = m;
+
+ m_data.push_back(g);
+ }
+
+ endResetModel();
+ }
+}
+
+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;
+ }
+ }
+
+ auto sVal = std::make_shared<qt3dsdm::CDataStr>(Q3DStudio::CString::fromQString(val));
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(m_instance, m_property, sVal);
+}
+
+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);
+}
+
+void VariantsGroupModel::addNewGroup()
+{
+ VariantTagDialog dlg(VariantTagDialog::AddGroup);
+
+ if (dlg.exec() == QDialog::Accepted)
+ g_StudioApp.GetCore()->getProjectFile().addVariantGroup(dlg.getNames().second);
+}
+
+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/Studio/Palettes/Inspector/VariantsGroupModel.h b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.h
new file mode 100644
index 00000000..695e3fb0
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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
+
+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();
+
+
+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
+};
+
+#endif // VARIANTSGROUPMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.cpp b/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.cpp
new file mode 100644
index 00000000..910b4f09
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** 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(QObject *parent)
+ : QAbstractListModel(parent)
+{
+
+}
+
+void VariantsTagModel::init(const QVector<std::pair<QString, bool> > &data)
+{
+ 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/Studio/Palettes/Inspector/VariantsTagModel.h b/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.h
new file mode 100644
index 00000000..01bb4e73
--- /dev/null
+++ b/src/Authoring/Studio/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(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 init(const QVector<std::pair<QString, bool> > &data);
+ 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