Rework of the material handling system. This first part concntrates on a rework of the material cards. Rather than use a fixed list of possible properties, properties can be defined separately in their own files and mixed to provide a complete list of possible properties. Properties can be inherited. The cards then provide values for the properties. These can also be inherited allowing for small changes in cards as required. The new property definitions are more extensive than previously. 2 and 3 dimensional arrays of properties can be defined. Values are obtained by calling an API instead of reading from a dictionary. For compatibility, a Python dictionary of values can be obtained similar to how it was done previously, but this is considered a deprecated API and won't support the newer advanced features. The editor is completely reworked. It will be able to edit older format material cards, but can only save them in the new format. For testing during the development phase, a system preference can specifiy wether the old or new material editors are to be used. This option will be removed before release.
906 lines
30 KiB
C++
906 lines
30 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
#ifndef _PreComp_
|
|
#include <QColorDialog>
|
|
#include <QDesktopServices>
|
|
#include <QIODevice>
|
|
#include <QItemSelectionModel>
|
|
#include <QMessageBox>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
#include <QTextStream>
|
|
#include <QVariant>
|
|
#endif
|
|
|
|
#include <limits>
|
|
|
|
#include <App/Application.h>
|
|
#include <Base/Interpreter.h>
|
|
#include <Base/Quantity.h>
|
|
#include <Gui/Application.h>
|
|
#include <Gui/Command.h>
|
|
#include <Gui/InputField.h>
|
|
#include <Gui/PrefWidgets.h>
|
|
#include <Gui/SpinBox.h>
|
|
#include <Gui/WaitCursor.h>
|
|
|
|
#include <Mod/Material/App/Exceptions.h>
|
|
#include <Mod/Material/App/ModelManager.h>
|
|
|
|
#include "MaterialDelegate.h"
|
|
#include "MaterialSave.h"
|
|
#include "MaterialsEditor.h"
|
|
#include "ModelSelect.h"
|
|
#include "ui_MaterialsEditor.h"
|
|
|
|
|
|
using namespace MatGui;
|
|
|
|
/* TRANSLATOR MatGui::MaterialsEditor */
|
|
|
|
MaterialsEditor::MaterialsEditor(QWidget* parent)
|
|
: QDialog(parent)
|
|
, ui(new Ui_MaterialsEditor)
|
|
, _edited(false)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
getFavorites();
|
|
getRecents();
|
|
|
|
createMaterialTree();
|
|
createPhysicalTree();
|
|
createAppearanceTree();
|
|
createPreviews();
|
|
|
|
ui->buttonURL->setIcon(QIcon(QString::fromStdString(":/icons/internet-web-browser.svg")));
|
|
|
|
connect(ui->standardButtons->button(QDialogButtonBox::Ok),
|
|
&QPushButton::clicked,
|
|
this,
|
|
&MaterialsEditor::onOk);
|
|
connect(ui->standardButtons->button(QDialogButtonBox::Cancel),
|
|
&QPushButton::clicked,
|
|
this,
|
|
&MaterialsEditor::onCancel);
|
|
connect(ui->standardButtons->button(QDialogButtonBox::Save),
|
|
&QPushButton::clicked,
|
|
this,
|
|
&MaterialsEditor::onSave);
|
|
|
|
connect(ui->buttonURL, &QPushButton::clicked, this, &MaterialsEditor::onURL);
|
|
connect(ui->buttonPhysicalAdd, &QPushButton::clicked, this, &MaterialsEditor::onPhysicalAdd);
|
|
connect(ui->buttonAppearanceAdd,
|
|
&QPushButton::clicked,
|
|
this,
|
|
&MaterialsEditor::onAppearanceAdd);
|
|
connect(ui->buttonFavorite, &QPushButton::clicked, this, &MaterialsEditor::onFavourite);
|
|
|
|
QItemSelectionModel* selectionModel = ui->treeMaterials->selectionModel();
|
|
connect(selectionModel,
|
|
&QItemSelectionModel::selectionChanged,
|
|
this,
|
|
&MaterialsEditor::onSelectMaterial);
|
|
}
|
|
|
|
void MaterialsEditor::getFavorites()
|
|
{
|
|
_favorites.clear();
|
|
|
|
auto param = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Material/Favorites");
|
|
int count = param->GetInt("Favorites", 0);
|
|
for (int i = 0; i < count; i++) {
|
|
QString key = QString::fromLatin1("FAV%1").arg(i);
|
|
QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), ""));
|
|
_favorites.push_back(uuid);
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::saveFavorites()
|
|
{
|
|
auto param = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Material/Favorites");
|
|
|
|
// Clear out the existing favorites
|
|
int count = param->GetInt("Favorites", 0);
|
|
for (int i = 0; i < count; i++) {
|
|
QString key = QString::fromLatin1("FAV%1").arg(i);
|
|
param->RemoveASCII(key.toStdString().c_str());
|
|
}
|
|
|
|
// Add the current values
|
|
param->SetInt("Favorites", _favorites.size());
|
|
int j = 0;
|
|
for (auto favorite : _favorites) {
|
|
QString key = QString::fromLatin1("FAV%1").arg(j);
|
|
param->SetASCII(key.toStdString().c_str(), favorite.toStdString());
|
|
|
|
j++;
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::addFavorite(const QString& uuid)
|
|
{
|
|
// Ensure it is a material. New, unsaved materials will not be
|
|
try {
|
|
const Materials::Material& material = _materialManager.getMaterial(uuid);
|
|
Q_UNUSED(material)
|
|
}
|
|
catch (const Materials::MaterialNotFound&) {
|
|
return;
|
|
}
|
|
|
|
if (!isFavorite(uuid)) {
|
|
_favorites.push_back(uuid);
|
|
saveFavorites();
|
|
refreshMaterialTree();
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::removeFavorite(const QString& uuid)
|
|
{
|
|
if (isFavorite(uuid)) {
|
|
_favorites.remove(uuid);
|
|
saveFavorites();
|
|
refreshMaterialTree();
|
|
}
|
|
}
|
|
|
|
bool MaterialsEditor::isFavorite(const QString& uuid) const
|
|
{
|
|
for (auto it : _favorites) {
|
|
if (it == uuid) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void MaterialsEditor::getRecents()
|
|
{
|
|
_recents.clear();
|
|
|
|
auto param = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Material/Recent");
|
|
_recentMax = param->GetInt("RecentMax", 5);
|
|
int count = param->GetInt("Recent", 0);
|
|
for (int i = 0; i < count; i++) {
|
|
QString key = QString::fromLatin1("MRU%1").arg(i);
|
|
QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), ""));
|
|
_recents.push_back(uuid);
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::saveRecents()
|
|
{
|
|
auto param = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Material/Recent");
|
|
|
|
// Clear out the existing favorites
|
|
int count = param->GetInt("Recent", 0);
|
|
for (int i = 0; i < count; i++) {
|
|
QString key = QString::fromLatin1("MRU%1").arg(i);
|
|
param->RemoveASCII(key.toStdString().c_str());
|
|
}
|
|
|
|
// Add the current values
|
|
int size = _recents.size();
|
|
if (size > _recentMax) {
|
|
size = _recentMax;
|
|
}
|
|
param->SetInt("Recent", size);
|
|
int j = 0;
|
|
for (auto recent : _recents) {
|
|
QString key = QString::fromLatin1("MRU%1").arg(j);
|
|
param->SetASCII(key.toStdString().c_str(), recent.toStdString());
|
|
|
|
j++;
|
|
if (j >= size) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::addRecent(const QString& uuid)
|
|
{
|
|
// Ensure it is a material. New, unsaved materials will not be
|
|
try {
|
|
const Materials::Material& material = _materialManager.getMaterial(uuid);
|
|
Q_UNUSED(material)
|
|
}
|
|
catch (const Materials::MaterialNotFound&) {
|
|
return;
|
|
}
|
|
|
|
// Ensure no duplicates
|
|
if (isRecent(uuid)) {
|
|
_recents.remove(uuid);
|
|
}
|
|
|
|
_recents.push_front(uuid);
|
|
while (_recents.size() > static_cast<std::size_t>(_recentMax)) {
|
|
_recents.pop_back();
|
|
}
|
|
|
|
saveRecents();
|
|
}
|
|
|
|
bool MaterialsEditor::isRecent(const QString& uuid) const
|
|
{
|
|
for (auto it : _recents) {
|
|
if (it == uuid) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MaterialsEditor::propertyChange(const QString& property, const QString value)
|
|
{
|
|
Base::Console().Log("MaterialsEditor::propertyChange(%s) = '%s'\n",
|
|
property.toStdString().c_str(),
|
|
value.toStdString().c_str());
|
|
if (_material.hasPhysicalProperty(property)) {
|
|
_material.setPhysicalValue(property, value);
|
|
}
|
|
else if (_material.hasAppearanceProperty(property)) {
|
|
_material.setAppearanceValue(property, value);
|
|
updatePreview();
|
|
}
|
|
_edited = true;
|
|
}
|
|
|
|
void MaterialsEditor::onURL(bool checked)
|
|
{
|
|
Q_UNUSED(checked)
|
|
|
|
Base::Console().Log("URL\n");
|
|
QString url = ui->editSourceURL->text();
|
|
if (url.length() > 0) {
|
|
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::onPhysicalAdd(bool checked)
|
|
{
|
|
Q_UNUSED(checked)
|
|
|
|
ModelSelect dialog(this, Materials::ModelManager::ModelFilter_Physical);
|
|
dialog.setModal(true);
|
|
if (dialog.exec() == QDialog::Accepted) {
|
|
QString selected = dialog.selectedModel();
|
|
Base::Console().Log("Selected model '%s'\n", selected.toStdString().c_str());
|
|
_material.addPhysical(selected);
|
|
updateMaterial();
|
|
}
|
|
else {
|
|
Base::Console().Log("No model selected\n");
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::onAppearanceAdd(bool checked)
|
|
{
|
|
Q_UNUSED(checked)
|
|
|
|
ModelSelect dialog(this, Materials::ModelManager::ModelFilter_Appearance);
|
|
dialog.setModal(true);
|
|
if (dialog.exec() == QDialog::Accepted) {
|
|
QString selected = dialog.selectedModel();
|
|
Base::Console().Log("Selected model '%s'\n", selected.toStdString().c_str());
|
|
_material.addAppearance(selected);
|
|
updateMaterial();
|
|
}
|
|
else {
|
|
Base::Console().Log("No model selected\n");
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::onFavourite(bool checked)
|
|
{
|
|
Q_UNUSED(checked)
|
|
|
|
Base::Console().Log("Favorite\n");
|
|
auto selected = _material.getUUID();
|
|
if (isFavorite(selected)) {
|
|
removeFavorite(selected);
|
|
}
|
|
else {
|
|
addFavorite(selected);
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::onOk(bool checked)
|
|
{
|
|
Q_UNUSED(checked)
|
|
|
|
accept();
|
|
}
|
|
|
|
void MaterialsEditor::onCancel(bool checked)
|
|
{
|
|
Q_UNUSED(checked)
|
|
|
|
reject();
|
|
}
|
|
|
|
void MaterialsEditor::onSave(bool checked)
|
|
{
|
|
Q_UNUSED(checked)
|
|
|
|
saveMaterial();
|
|
}
|
|
|
|
void MaterialsEditor::saveMaterial()
|
|
{
|
|
MaterialSave dialog(&_material, this);
|
|
dialog.setModal(true);
|
|
if (dialog.exec() == QDialog::Accepted) {
|
|
_material.resetEditState();
|
|
refreshMaterialTree();
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::accept()
|
|
{
|
|
addRecent(_material.getUUID());
|
|
QDialog::accept();
|
|
}
|
|
|
|
void MaterialsEditor::reject()
|
|
{
|
|
QDialog::reject();
|
|
}
|
|
|
|
// QIcon MaterialsEditor::errorIcon(const QIcon &icon) const {
|
|
// auto pixmap = icon.pixmap();
|
|
// }
|
|
|
|
void MaterialsEditor::addMaterials(QStandardItem& parent,
|
|
const std::map<QString, Materials::MaterialTreeNode*>* modelTree,
|
|
const QIcon& folderIcon,
|
|
const QIcon& icon)
|
|
{
|
|
auto tree = ui->treeMaterials;
|
|
for (auto& mat : *modelTree) {
|
|
Materials::MaterialTreeNode* nodePtr = mat.second;
|
|
if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) {
|
|
const Materials::Material* material = nodePtr->getData();
|
|
QString uuid = material->getUUID();
|
|
// Base::Console().Log("Material path '%s'\n",
|
|
// material->getDirectory().toStdString().c_str());
|
|
|
|
// auto card = new QStandardItem(icon, material->getName());
|
|
auto card = new QStandardItem(icon, mat.first);
|
|
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
|
|
| Qt::ItemIsDropEnabled);
|
|
card->setData(QVariant(uuid), Qt::UserRole);
|
|
|
|
addExpanded(tree, &parent, card);
|
|
}
|
|
else {
|
|
auto node = new QStandardItem(folderIcon, mat.first);
|
|
addExpanded(tree, &parent, node);
|
|
node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
|
|
const std::map<QString, Materials::MaterialTreeNode*>* treeMap = nodePtr->getFolder();
|
|
addMaterials(*node, treeMap, folderIcon, icon);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child)
|
|
{
|
|
parent->appendRow(child);
|
|
tree->setExpanded(child->index(), true);
|
|
}
|
|
|
|
void MaterialsEditor::addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child)
|
|
{
|
|
parent->appendRow(child);
|
|
tree->setExpanded(child->index(), true);
|
|
}
|
|
|
|
void MaterialsEditor::createPhysicalTree()
|
|
{
|
|
auto tree = ui->treePhysicalProperties;
|
|
auto model = new QStandardItemModel();
|
|
tree->setModel(model);
|
|
|
|
QStringList headers;
|
|
headers.append(QString::fromStdString("Property"));
|
|
headers.append(QString::fromStdString("Value"));
|
|
headers.append(QString::fromStdString("Type"));
|
|
model->setHorizontalHeaderLabels(headers);
|
|
|
|
tree->setColumnWidth(0, 250);
|
|
tree->setColumnWidth(1, 250);
|
|
tree->setColumnHidden(2, true);
|
|
|
|
tree->setHeaderHidden(false);
|
|
tree->setUniformRowHeights(true);
|
|
MaterialDelegate* delegate = new MaterialDelegate(this);
|
|
tree->setItemDelegateForColumn(1, delegate);
|
|
|
|
connect(delegate, &MaterialDelegate::propertyChange, this, &MaterialsEditor::propertyChange);
|
|
}
|
|
|
|
void MaterialsEditor::createPreviews()
|
|
{
|
|
_rendered = new QSvgWidget(QString::fromStdString(":/icons/preview-rendered.svg"));
|
|
_rendered->setMaximumWidth(64);
|
|
_rendered->setMinimumHeight(64);
|
|
ui->layoutAppearance->addWidget(_rendered);
|
|
|
|
_vectored = new QSvgWidget(QString::fromStdString(":/icons/preview-vector.svg"));
|
|
_vectored->setMaximumWidth(64);
|
|
_vectored->setMinimumHeight(64);
|
|
ui->layoutAppearance->addWidget(_vectored);
|
|
|
|
updatePreview();
|
|
}
|
|
|
|
void MaterialsEditor::createAppearanceTree()
|
|
{
|
|
auto tree = ui->treeAppearance;
|
|
auto model = new QStandardItemModel();
|
|
tree->setModel(model);
|
|
|
|
QStringList headers;
|
|
headers.append(QString::fromStdString("Property"));
|
|
headers.append(QString::fromStdString("Value"));
|
|
headers.append(QString::fromStdString("Type"));
|
|
model->setHorizontalHeaderLabels(headers);
|
|
|
|
tree->setColumnWidth(0, 250);
|
|
tree->setColumnWidth(1, 250);
|
|
tree->setColumnHidden(2, true);
|
|
|
|
tree->setHeaderHidden(false);
|
|
tree->setUniformRowHeights(false);
|
|
MaterialDelegate* delegate = new MaterialDelegate(this);
|
|
tree->setItemDelegateForColumn(1, delegate);
|
|
|
|
connect(delegate, &MaterialDelegate::propertyChange, this, &MaterialsEditor::propertyChange);
|
|
}
|
|
|
|
void MaterialsEditor::addRecents(QStandardItem* parent)
|
|
{
|
|
auto tree = ui->treeMaterials;
|
|
for (auto& uuid : _recents) {
|
|
try {
|
|
const Materials::Material& material = getMaterialManager().getMaterial(uuid);
|
|
|
|
QIcon icon = QIcon(material.getLibrary().getIconPath());
|
|
auto card = new QStandardItem(icon, material.getName());
|
|
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
|
|
| Qt::ItemIsDropEnabled);
|
|
card->setData(QVariant(uuid), Qt::UserRole);
|
|
|
|
addExpanded(tree, parent, card);
|
|
}
|
|
catch (const Materials::MaterialNotFound&) {
|
|
}
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::addFavorites(QStandardItem* parent)
|
|
{
|
|
auto tree = ui->treeMaterials;
|
|
for (auto& uuid : _favorites) {
|
|
try {
|
|
const Materials::Material& material = getMaterialManager().getMaterial(uuid);
|
|
|
|
QIcon icon = QIcon(material.getLibrary().getIconPath());
|
|
auto card = new QStandardItem(icon, material.getName());
|
|
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
|
|
| Qt::ItemIsDropEnabled);
|
|
card->setData(QVariant(uuid), Qt::UserRole);
|
|
|
|
addExpanded(tree, parent, card);
|
|
}
|
|
catch (const Materials::MaterialNotFound&) {
|
|
}
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::fillMaterialTree()
|
|
{
|
|
auto tree = ui->treeMaterials;
|
|
auto model = static_cast<QStandardItemModel*>(tree->model());
|
|
|
|
auto lib = new QStandardItem(QString::fromStdString("Favorites"));
|
|
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
|
|
addExpanded(tree, model, lib);
|
|
addFavorites(lib);
|
|
|
|
lib = new QStandardItem(QString::fromStdString("Recent"));
|
|
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
|
|
addExpanded(tree, model, lib);
|
|
addRecents(lib);
|
|
|
|
auto libraries = Materials::MaterialManager::getMaterialLibraries();
|
|
for (const auto& library : *libraries) {
|
|
lib = new QStandardItem(library->getName());
|
|
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
|
|
addExpanded(tree, model, lib);
|
|
|
|
QIcon icon(library->getIconPath());
|
|
QIcon folderIcon(QString::fromStdString(":/icons/folder.svg"));
|
|
|
|
std::map<QString, Materials::MaterialTreeNode*>* modelTree =
|
|
_materialManager.getMaterialTree(*library);
|
|
addMaterials(*lib, modelTree, folderIcon, icon);
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::createMaterialTree()
|
|
{
|
|
// Materials::ModelManager &modelManager = getModelManager();
|
|
// Q_UNUSED(modelManager)
|
|
|
|
auto tree = ui->treeMaterials;
|
|
auto model = new QStandardItemModel();
|
|
tree->setModel(model);
|
|
|
|
tree->setHeaderHidden(true);
|
|
fillMaterialTree();
|
|
}
|
|
|
|
void MaterialsEditor::refreshMaterialTree()
|
|
{
|
|
auto tree = ui->treeMaterials;
|
|
auto model = static_cast<QStandardItemModel*>(tree->model());
|
|
model->clear();
|
|
|
|
fillMaterialTree();
|
|
}
|
|
|
|
void MaterialsEditor::updatePreview() const
|
|
{
|
|
QString diffuseColor;
|
|
QString highlightColor;
|
|
QString sectionColor;
|
|
|
|
if (_material.hasAppearanceProperty(QString::fromStdString("DiffuseColor"))) {
|
|
diffuseColor = _material.getAppearanceValueString(QString::fromStdString("DiffuseColor"));
|
|
}
|
|
else if (_material.hasAppearanceProperty(QString::fromStdString("ViewColor"))) {
|
|
diffuseColor = _material.getAppearanceValueString(QString::fromStdString("ViewColor"));
|
|
}
|
|
else if (_material.hasAppearanceProperty(QString::fromStdString("Color"))) {
|
|
diffuseColor = _material.getAppearanceValueString(QString::fromStdString("Color"));
|
|
}
|
|
|
|
if (_material.hasAppearanceProperty(QString::fromStdString("SpecularColor"))) {
|
|
highlightColor =
|
|
_material.getAppearanceValueString(QString::fromStdString("SpecularColor"));
|
|
}
|
|
|
|
if (_material.hasAppearanceProperty(QString::fromStdString("SectionColor"))) {
|
|
sectionColor = _material.getAppearanceValueString(QString::fromStdString("SectionColor"));
|
|
}
|
|
|
|
if ((diffuseColor.length() + highlightColor.length()) > 0) {
|
|
auto file = QFile(QString::fromStdString(":/icons/preview-rendered.svg"));
|
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
QString svg = QTextStream(&file).readAll();
|
|
file.close();
|
|
if (diffuseColor.length() > 0) {
|
|
svg =
|
|
svg.replace(QString::fromStdString("#d3d7cf"), getColorHash(diffuseColor, 255));
|
|
svg =
|
|
svg.replace(QString::fromStdString("#555753"), getColorHash(diffuseColor, 125));
|
|
}
|
|
if (highlightColor.length() > 0) {
|
|
svg = svg.replace(QString::fromStdString("#fffffe"),
|
|
getColorHash(highlightColor, 255));
|
|
}
|
|
_rendered->load(svg.toUtf8());
|
|
}
|
|
}
|
|
|
|
if ((diffuseColor.length() + sectionColor.length()) > 0) {
|
|
auto file = QFile(QString::fromStdString(":/icons/preview-vector.svg"));
|
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
QString svg = QTextStream(&file).readAll();
|
|
file.close();
|
|
if (diffuseColor.length() > 0) {
|
|
svg =
|
|
svg.replace(QString::fromStdString("#d3d7cf"), getColorHash(diffuseColor, 255));
|
|
svg =
|
|
svg.replace(QString::fromStdString("#555753"), getColorHash(diffuseColor, 125));
|
|
}
|
|
if (sectionColor.length() > 0) {
|
|
svg =
|
|
svg.replace(QString::fromStdString("#ffffff"), getColorHash(sectionColor, 255));
|
|
}
|
|
_vectored->load(svg.toUtf8());
|
|
}
|
|
}
|
|
}
|
|
|
|
QString MaterialsEditor::getColorHash(const QString& colorString, int colorRange) const
|
|
{
|
|
/*
|
|
returns a '#000000' string from a '(0.1,0.2,0.3)' string. Optionally the string
|
|
has a fourth value for alpha (transparency)
|
|
*/
|
|
std::stringstream stream(colorString.toStdString());
|
|
|
|
char c;
|
|
stream >> c;// read "("
|
|
double red;
|
|
stream >> red;
|
|
stream >> c;// ","
|
|
double green;
|
|
stream >> green;
|
|
stream >> c;// ","
|
|
double blue;
|
|
stream >> blue;
|
|
stream >> c;// ","
|
|
double alpha = 1.0;
|
|
if (c == ',') {
|
|
stream >> alpha;
|
|
}
|
|
|
|
QColor color(static_cast<int>(red * colorRange),
|
|
static_cast<int>(green * colorRange),
|
|
static_cast<int>(blue * colorRange),
|
|
static_cast<int>(alpha * colorRange));
|
|
return color.name();
|
|
}
|
|
|
|
void MaterialsEditor::updateMaterialAppearance()
|
|
{
|
|
QTreeView* tree = ui->treeAppearance;
|
|
QStandardItemModel* treeModel = static_cast<QStandardItemModel*>(tree->model());
|
|
treeModel->clear();
|
|
|
|
QStringList headers;
|
|
headers.append(QString::fromStdString("Property"));
|
|
headers.append(QString::fromStdString("Value"));
|
|
headers.append(QString::fromStdString("Type"));
|
|
treeModel->setHorizontalHeaderLabels(headers);
|
|
|
|
tree->setColumnWidth(0, 250);
|
|
tree->setColumnWidth(1, 250);
|
|
tree->setColumnHidden(2, true);
|
|
|
|
const std::vector<QString>* models = _material.getAppearanceModels();
|
|
if (models) {
|
|
for (auto it = models->begin(); it != models->end(); it++) {
|
|
QString uuid = *it;
|
|
try {
|
|
const Materials::Model& model = getModelManager().getModel(uuid);
|
|
QString name = model.getName();
|
|
|
|
auto modelRoot = new QStandardItem(name);
|
|
modelRoot->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
|
|
| Qt::ItemIsDropEnabled);
|
|
addExpanded(tree, treeModel, modelRoot);
|
|
for (auto itp = model.begin(); itp != model.end(); itp++) {
|
|
QList<QStandardItem*> items;
|
|
|
|
QString key = itp->first;
|
|
auto propertyItem = new QStandardItem(key);
|
|
propertyItem->setToolTip(itp->second.getDescription());
|
|
items.append(propertyItem);
|
|
|
|
auto valueItem = new QStandardItem(_material.getAppearanceValueString(key));
|
|
valueItem->setToolTip(itp->second.getDescription());
|
|
QVariant variant;
|
|
variant.setValue(&_material);
|
|
valueItem->setData(variant);
|
|
items.append(valueItem);
|
|
|
|
auto typeItem = new QStandardItem(itp->second.getPropertyType());
|
|
items.append(typeItem);
|
|
|
|
auto unitsItem = new QStandardItem(itp->second.getUnits());
|
|
items.append(unitsItem);
|
|
|
|
modelRoot->appendRow(items);
|
|
tree->setExpanded(modelRoot->index(), true);
|
|
}
|
|
}
|
|
catch (Materials::ModelNotFound const&) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::updateMaterialProperties()
|
|
{
|
|
QTreeView* tree = ui->treePhysicalProperties;
|
|
QStandardItemModel* treeModel = static_cast<QStandardItemModel*>(tree->model());
|
|
treeModel->clear();
|
|
|
|
QStringList headers;
|
|
headers.append(QString::fromStdString("Property"));
|
|
headers.append(QString::fromStdString("Value"));
|
|
headers.append(QString::fromStdString("Type"));
|
|
headers.append(QString::fromStdString("Units"));
|
|
treeModel->setHorizontalHeaderLabels(headers);
|
|
|
|
tree->setColumnWidth(0, 250);
|
|
tree->setColumnWidth(1, 250);
|
|
tree->setColumnHidden(2, true);
|
|
tree->setColumnHidden(3, true);
|
|
|
|
const std::vector<QString>* models = _material.getPhysicalModels();
|
|
if (models) {
|
|
for (auto it = models->begin(); it != models->end(); it++) {
|
|
QString uuid = *it;
|
|
try {
|
|
const Materials::Model& model = getModelManager().getModel(uuid);
|
|
QString name = model.getName();
|
|
|
|
auto modelRoot = new QStandardItem(name);
|
|
modelRoot->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
|
|
| Qt::ItemIsDropEnabled);
|
|
addExpanded(tree, treeModel, modelRoot);
|
|
for (auto itp = model.begin(); itp != model.end(); itp++) {
|
|
QList<QStandardItem*> items;
|
|
|
|
QString key = itp->first;
|
|
Materials::ModelProperty modelProperty =
|
|
static_cast<Materials::ModelProperty>(itp->second);
|
|
auto propertyItem = new QStandardItem(key);
|
|
propertyItem->setToolTip(modelProperty.getDescription());
|
|
items.append(propertyItem);
|
|
|
|
auto valueItem = new QStandardItem(_material.getPhysicalValueString(key));
|
|
valueItem->setToolTip(modelProperty.getDescription());
|
|
QVariant variant;
|
|
variant.setValue(&_material);
|
|
valueItem->setData(variant);
|
|
items.append(valueItem);
|
|
|
|
auto typeItem = new QStandardItem(modelProperty.getPropertyType());
|
|
items.append(typeItem);
|
|
|
|
auto unitsItem = new QStandardItem(modelProperty.getUnits());
|
|
items.append(unitsItem);
|
|
|
|
// addExpanded(tree, modelRoot, propertyItem);
|
|
modelRoot->appendRow(items);
|
|
tree->setExpanded(modelRoot->index(), true);
|
|
}
|
|
}
|
|
catch (Materials::ModelNotFound const&) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MaterialsEditor::updateMaterial()
|
|
{
|
|
// Update the general information
|
|
ui->editName->setText(_material.getName());
|
|
ui->editAuthorLicense->setText(_material.getAuthorAndLicense());
|
|
// ui->editParent->setText(_material.getName());
|
|
ui->editSourceURL->setText(_material.getURL());
|
|
ui->editSourceReference->setText(_material.getReference());
|
|
// ui->editTags->setText(_material.getName());
|
|
ui->editDescription->setText(_material.getDescription());
|
|
|
|
updateMaterialProperties();
|
|
updateMaterialAppearance();
|
|
|
|
updatePreview();
|
|
}
|
|
|
|
void MaterialsEditor::onSelectMaterial(const QItemSelection& selected,
|
|
const QItemSelection& deselected)
|
|
{
|
|
Q_UNUSED(deselected);
|
|
|
|
// Get the UUID before changing the underlying data model
|
|
QString uuid;
|
|
QStandardItemModel* model = static_cast<QStandardItemModel*>(ui->treeMaterials->model());
|
|
QModelIndexList indexes = selected.indexes();
|
|
for (auto it = indexes.begin(); it != indexes.end(); it++) {
|
|
QStandardItem* item = model->itemFromIndex(*it);
|
|
|
|
Base::Console().Log("%s\n", item->text().toStdString().c_str());
|
|
if (item) {
|
|
uuid = item->data(Qt::UserRole).toString();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (uuid.isEmpty() || uuid == _material.getUUID()) {
|
|
Base::Console().Log("*** Unchanged material '%s'\n", uuid.toStdString().c_str());
|
|
return;
|
|
}
|
|
|
|
// Ensure data is saved (or discarded) before changing materials
|
|
if (_material.getEditState() != Materials::Material::ModelEdit_None) {
|
|
// Prompt the user to save or discard changes
|
|
Base::Console().Log("*** Material edited!!!\n");
|
|
int res = confirmSave(this);
|
|
if (res == QMessageBox::Cancel) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get the selected material
|
|
try {
|
|
_material = getMaterialManager().getMaterial(uuid);
|
|
}
|
|
catch (Materials::ModelNotFound const&) {
|
|
Base::Console().Log("*** Unable to load material '%s'\n", uuid.toStdString().c_str());
|
|
Materials::Material empty;
|
|
_material = empty;
|
|
}
|
|
|
|
updateMaterial();
|
|
_material.resetEditState();
|
|
}
|
|
|
|
int MaterialsEditor::confirmSave(QWidget* parent)
|
|
{
|
|
QMessageBox box(parent ? parent : this);
|
|
box.setIcon(QMessageBox::Question);
|
|
box.setWindowTitle(QObject::tr("Unsaved Material"));
|
|
box.setText(QObject::tr("Do you want to save your changes to the material before closing?"));
|
|
|
|
box.setInformativeText(QObject::tr("If you don't save, your changes will be lost."));
|
|
box.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save);
|
|
box.setDefaultButton(QMessageBox::Save);
|
|
box.setEscapeButton(QMessageBox::Cancel);
|
|
|
|
// add shortcuts
|
|
QAbstractButton* saveBtn = box.button(QMessageBox::Save);
|
|
if (saveBtn->shortcut().isEmpty()) {
|
|
QString text = saveBtn->text();
|
|
text.prepend(QLatin1Char('&'));
|
|
saveBtn->setShortcut(QKeySequence::mnemonic(text));
|
|
}
|
|
|
|
QAbstractButton* discardBtn = box.button(QMessageBox::Discard);
|
|
if (discardBtn->shortcut().isEmpty()) {
|
|
QString text = discardBtn->text();
|
|
text.prepend(QLatin1Char('&'));
|
|
discardBtn->setShortcut(QKeySequence::mnemonic(text));
|
|
}
|
|
|
|
int res = QMessageBox::Cancel;
|
|
box.adjustSize();// Silence warnings from Qt on Windows
|
|
switch (box.exec()) {
|
|
case QMessageBox::Save:
|
|
saveMaterial();
|
|
res = QMessageBox::Save;
|
|
break;
|
|
case QMessageBox::Discard:
|
|
res = QMessageBox::Discard;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
#include "moc_MaterialsEditor.cpp"
|