From 51be8e7b4e14174296854565d26500245d2b9b6f Mon Sep 17 00:00:00 2001 From: David Carter Date: Mon, 13 May 2024 21:21:40 -0400 Subject: [PATCH] Material: Appearance and Material inspectors Dialogs to view the Appearance and Material properties of an object These inspectors are intended to be used when debugging Appearance and Material issues in a model. The Appearance inspector displays the appearance properties of an object. This will be more useful once PR 13792 is merged which migrates parts to use ShapeAppearance instead of DiffuseColor. This shows each of the appearance properties per face for the object. The Material inspector shows the material, models, and properties assigned to a model. It displays useful debugging information such as the UUID and file paths associated with eacch of the items. This is useful when finding and resolving model conflicts. The material inspector now gives the option of copying the information to the clipboard. --- src/Mod/Material/App/Materials.cpp | 9 +- src/Mod/Material/App/Materials.h | 16 +- src/Mod/Material/App/Model.h | 4 - src/Mod/Material/Gui/CMakeLists.txt | 11 +- src/Mod/Material/Gui/Command.cpp | 60 ++- src/Mod/Material/Gui/DlgInspectAppearance.cpp | 273 +++++++++++ src/Mod/Material/Gui/DlgInspectAppearance.h | 112 +++++ src/Mod/Material/Gui/DlgInspectAppearance.ui | 122 +++++ src/Mod/Material/Gui/DlgInspectMaterial.cpp | 425 ++++++++++++++++++ src/Mod/Material/Gui/DlgInspectMaterial.h | 123 +++++ src/Mod/Material/Gui/DlgInspectMaterial.ui | 145 ++++++ src/Mod/Material/Gui/Workbench.cpp | 14 +- src/Mod/Part/Gui/Workbench.cpp | 2 + src/Mod/PartDesign/Gui/Workbench.cpp | 3 + .../Material/App/TestMaterialProperties.cpp | 17 +- 15 files changed, 1308 insertions(+), 28 deletions(-) create mode 100644 src/Mod/Material/Gui/DlgInspectAppearance.cpp create mode 100644 src/Mod/Material/Gui/DlgInspectAppearance.h create mode 100644 src/Mod/Material/Gui/DlgInspectAppearance.ui create mode 100644 src/Mod/Material/Gui/DlgInspectMaterial.cpp create mode 100644 src/Mod/Material/Gui/DlgInspectMaterial.h create mode 100644 src/Mod/Material/Gui/DlgInspectMaterial.ui diff --git a/src/Mod/Material/App/Materials.cpp b/src/Mod/Material/App/Materials.cpp index 5c199240ca..5192328211 100644 --- a/src/Mod/Material/App/Materials.cpp +++ b/src/Mod/Material/App/Materials.cpp @@ -50,14 +50,15 @@ MaterialProperty::MaterialProperty() _valuePtr = std::make_shared(MaterialValue::None); } -MaterialProperty::MaterialProperty(const ModelProperty& other) +MaterialProperty::MaterialProperty(const ModelProperty& other, QString modelUUID) : ModelProperty(other) + , _modelUUID(modelUUID) , _valuePtr(nullptr) { setType(getPropertyType()); auto columns = other.getColumns(); for (auto& it : columns) { - MaterialProperty prop(it); + MaterialProperty prop(it, modelUUID); addColumn(prop); } } @@ -657,7 +658,7 @@ void Material::addPhysical(const QString& uuid) ModelProperty property = static_cast(it.second); try { - _physical[propertyName] = std::make_shared(property); + _physical[propertyName] = std::make_shared(property, uuid); } catch (const UnknownValueType&) { Base::Console().Error("Property '%s' has unknown type '%s'. Ignoring\n", @@ -733,7 +734,7 @@ void Material::addAppearance(const QString& uuid) if (!hasAppearanceProperty(propertyName)) { ModelProperty property = static_cast(it.second); - _appearance[propertyName] = std::make_shared(property); + _appearance[propertyName] = std::make_shared(property, uuid); } } } diff --git a/src/Mod/Material/App/Materials.h b/src/Mod/Material/App/Materials.h index 33de6ae629..1b5630d2b1 100644 --- a/src/Mod/Material/App/Materials.h +++ b/src/Mod/Material/App/Materials.h @@ -52,7 +52,7 @@ class MaterialsExport MaterialProperty: public ModelProperty public: MaterialProperty(); MaterialProperty(const MaterialProperty& other); - explicit MaterialProperty(const ModelProperty& other); + explicit MaterialProperty(const ModelProperty& other, QString modelUUID); explicit MaterialProperty(const std::shared_ptr& other); ~MaterialProperty() override = default; @@ -61,7 +61,11 @@ public: return _valuePtr->getType(); } - const QString getModelUUID() const; + const QString getModelUUID() const + { + return _modelUUID; + } + QVariant getValue(); QVariant getValue() const; QList getList() @@ -347,10 +351,18 @@ public: { return _physical; } + const std::map>& getPhysicalProperties() const + { + return _physical; + } std::map>& getAppearanceProperties() { return _appearance; } + const std::map>& getAppearanceProperties() const + { + return _appearance; + } std::map& getLegacyProperties() { return _legacy; diff --git a/src/Mod/Material/App/Model.h b/src/Mod/Material/App/Model.h index 5fe5888de0..b04f412b86 100644 --- a/src/Mod/Material/App/Model.h +++ b/src/Mod/Material/App/Model.h @@ -201,10 +201,6 @@ public: { return QDir(_directory).absolutePath(); } - // const QString getRelativePath() const - // { - // return QDir(_directory).relativeFilePath(QDir(_directory).absolutePath()); - // } const QString getUUID() const { return _uuid; diff --git a/src/Mod/Material/Gui/CMakeLists.txt b/src/Mod/Material/Gui/CMakeLists.txt index 1c5f5b50d1..cbb949b8a9 100644 --- a/src/Mod/Material/Gui/CMakeLists.txt +++ b/src/Mod/Material/Gui/CMakeLists.txt @@ -48,6 +48,8 @@ set(MatGui_UIC_SRCS Array2D.ui Array3D.ui DlgDisplayProperties.ui + DlgInspectAppearance.ui + DlgInspectMaterial.ui DlgMaterial.ui DlgSettingsDefaultMaterial.ui DlgSettingsMaterial.ui @@ -82,9 +84,12 @@ SET(MatGui_SRCS DlgDisplayPropertiesImp.cpp DlgDisplayPropertiesImp.h DlgDisplayProperties.ui - DlgMaterialImp.cpp - DlgMaterialImp.h - DlgDisplayProperties.ui + DlgInspectAppearance.cpp + DlgInspectAppearance.h + DlgInspectAppearance.ui + DlgInspectMaterial.cpp + DlgInspectMaterial.h + DlgInspectMaterial.ui DlgMaterialImp.cpp DlgMaterialImp.h DlgMaterial.ui diff --git a/src/Mod/Material/Gui/Command.cpp b/src/Mod/Material/Gui/Command.cpp index b6d7fbbc66..d6cee068ef 100644 --- a/src/Mod/Material/Gui/Command.cpp +++ b/src/Mod/Material/Gui/Command.cpp @@ -30,6 +30,8 @@ #include #include "DlgDisplayPropertiesImp.h" +#include "DlgInspectAppearance.h" +#include "DlgInspectMaterial.h" #include "DlgMaterialImp.h" #include "MaterialSave.h" #include "MaterialsEditor.h" @@ -39,7 +41,7 @@ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //=========================================================================== -// Material_Edit +// Materials_Edit //=========================================================================== DEF_STD_CMD_A(CmdMaterialsEdit) @@ -133,6 +135,60 @@ bool StdCmdSetMaterial::isActive() return (Gui::Control().activeDialog() == nullptr) && (Gui::Selection().size() != 0); } +//=========================================================================== +// Materials_InspectAppearance +//=========================================================================== +DEF_STD_CMD_A(CmdInspectAppearance) + +CmdInspectAppearance::CmdInspectAppearance() + : Command("Materials_InspectAppearance") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Inspect Appearance..."); + sToolTipText = QT_TR_NOOP("Inspect the appearance properties of the selected object"); + sWhatsThis = "Materials_InspectAppearance"; + sStatusTip = QT_TR_NOOP("Inspect the appearance properties of the selected object"); + // sPixmap = "Materials_Edit"; +} + +void CmdInspectAppearance::activated(int iMsg) +{ + Q_UNUSED(iMsg); + Gui::Control().showDialog(new MatGui::TaskInspectAppearance()); +} + +bool CmdInspectAppearance::isActive() +{ + return (Gui::Control().activeDialog() == nullptr); +} + +//=========================================================================== +// Materials_InspectMaterial +//=========================================================================== +DEF_STD_CMD_A(CmdInspectMaterial) + +CmdInspectMaterial::CmdInspectMaterial() + : Command("Materials_InspectMaterial") +{ + sGroup = "Standard-View"; + sMenuText = QT_TR_NOOP("Inspect Material..."); + sToolTipText = QT_TR_NOOP("Inspect the material properties of the selected object"); + sWhatsThis = "Materials_InspectMaterial"; + sStatusTip = QT_TR_NOOP("Inspect the material properties of the selected object"); + // sPixmap = "Materials_Edit"; +} + +void CmdInspectMaterial::activated(int iMsg) +{ + Q_UNUSED(iMsg); + Gui::Control().showDialog(new MatGui::TaskInspectMaterial()); +} + +bool CmdInspectMaterial::isActive() +{ + return (Gui::Control().activeDialog() == nullptr); +} + //--------------------------------------------------------------- void CreateMaterialCommands() @@ -142,4 +198,6 @@ void CreateMaterialCommands() rcCmdMgr.addCommand(new CmdMaterialsEdit()); rcCmdMgr.addCommand(new StdCmdSetAppearance()); rcCmdMgr.addCommand(new StdCmdSetMaterial()); + rcCmdMgr.addCommand(new CmdInspectAppearance()); + rcCmdMgr.addCommand(new CmdInspectMaterial()); } diff --git a/src/Mod/Material/Gui/DlgInspectAppearance.cpp b/src/Mod/Material/Gui/DlgInspectAppearance.cpp new file mode 100644 index 0000000000..86b586d5e0 --- /dev/null +++ b/src/Mod/Material/Gui/DlgInspectAppearance.cpp @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "DlgInspectAppearance.h" +#include "ui_DlgInspectAppearance.h" + + +using namespace MatGui; + +ColorWidget::ColorWidget(const App::Color& color, QWidget* parent) + : QWidget(parent) +{ + _color = color.asValue(); +} + +void ColorWidget::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + + // Draw a white background + auto color = QColor(255, 255, 255); + auto left = event->rect().left(); + auto width = event->rect().width(); + painter.fillRect(left, event->rect().top(), width, event->rect().height(), QBrush(color)); + + // Draw a black border + color = QColor(0, 0, 0); + left = event->rect().left() + 2; + width = event->rect().width() - 4; + if (event->rect().width() > 75) { + left += (event->rect().width() - 75) / 2; + width = 71; + } + painter.fillRect(left, event->rect().top() + 2, width, event->rect().height() - 4, QBrush(color)); + + // Draw the color + left = event->rect().left() + 5; + width = event->rect().width() - 10; + if (event->rect().width() > 75) { + left += (event->rect().width() - 75) / 2; + width = 65; + } + painter.fillRect(left, event->rect().top() + 5, width, event->rect().height() - 10, QBrush(_color)); +} + +/* TRANSLATOR MatGui::DlgInspectAppearance */ + +DlgInspectAppearance::DlgInspectAppearance(QWidget* parent) + : QWidget(parent) + , ui(new Ui_DlgInspectAppearance) +{ + ui->setupUi(this); + + std::vector views = getSelection(); + update(views); + + Gui::Selection().Attach(this); +} + +DlgInspectAppearance::~DlgInspectAppearance() +{ + Gui::Selection().Detach(this); +} + +bool DlgInspectAppearance::accept() +{ + return true; +} + +std::vector DlgInspectAppearance::getSelection() const +{ + std::vector views; + + // get a single selection + std::vector sel = + Gui::Selection().getSelection(nullptr, Gui::ResolveMode::OldStyleElement, true); + for (const auto& it : sel) { + Gui::ViewProvider* view = + Gui::Application::Instance->getDocument(it.pDoc)->getViewProvider(it.pObject); + views.push_back(view); + } + + return views; +} + +/// @cond DOXERR +void DlgInspectAppearance::OnChange(Gui::SelectionSingleton::SubjectType& rCaller, + Gui::SelectionSingleton::MessageType Reason) +{ + Q_UNUSED(rCaller); + + if (Reason.Type == Gui::SelectionChanges::AddSelection + || Reason.Type == Gui::SelectionChanges::RmvSelection + || Reason.Type == Gui::SelectionChanges::SetSelection + || Reason.Type == Gui::SelectionChanges::ClrSelection) { + std::vector views = getSelection(); + update(views); + } +} +/// @endcond + +void DlgInspectAppearance::update(std::vector& views) +{ + App::Document* doc = App::GetApplication().getActiveDocument(); + if (doc) { + ui->editDocument->setText(QString::fromUtf8(doc->Label.getValue())); + + if (views.size() == 1) { + auto view = dynamic_cast(views[0]); + if (!view) { + return; + } + auto* obj = view->getObject(); + if (!obj) { + return; + } + auto* labelProp = dynamic_cast(obj->getPropertyByName("Label")); + if (labelProp) { + ui->editObjectLabel->setText(QString::fromUtf8(labelProp->getValue())); + } + else { + ui->editObjectLabel->setText(QLatin1String("")); + } + ui->editObjectName->setText(QLatin1String(obj->getNameInDocument())); + + auto subElement = Gui::Selection().getSelectionEx(); + if (subElement.size() > 0) { + auto& subObject = subElement[0]; + if (subObject.getSubNames().size() > 0) { + ui->editSubShape->setText(QString::fromStdString(subObject.getSubNames()[0])); + } + else { + ui->editSubShape->setText(QLatin1String("")); + } + } + else { + ui->editSubShape->setText(QLatin1String("")); + } + + auto subShapeType = QString::fromUtf8(obj->getTypeId().getName()); + subShapeType.remove(subShapeType.indexOf(QLatin1String("::")), subShapeType.size()); + ui->editSubShapeType->setText(subShapeType); + ui->editShapeType->setText(QString::fromUtf8(obj->getTypeId().getName())); + + ui->tabAppearance->clear(); + if (labelProp && QString::fromUtf8(labelProp->getValue()).size() > 0) { + auto* prop = + dynamic_cast(view->getPropertyByName("ShapeAppearance")); + if (prop) { + for (int index = 0; index < prop->getSize(); index++) { + auto& material = (prop->getValues())[index]; + auto* tab = makeAppearanceTab(material); + ui->tabAppearance->addTab(tab, QString::number(index)); + + index++; + } + } + } + } + } +} + +QWidget* DlgInspectAppearance::makeAppearanceTab(const App::Material& material) +{ + QWidget* tab = new QWidget(this); + + auto* grid = new QGridLayout(); + tab->setLayout(grid); + + int row = 0; + auto* labelDiffuse = new QLabel(); + labelDiffuse->setText(tr("Diffuse Color")); + auto* colorDiffuse = new ColorWidget(material.diffuseColor); + colorDiffuse->setMaximumHeight(23); + + grid->addWidget(labelDiffuse, row, 0); + grid->addWidget(colorDiffuse, row, 1); + row += 1; + + auto* labelAmbient = new QLabel(); + labelAmbient->setText(tr("Ambient Color")); + auto* colorAmbient = new ColorWidget(material.ambientColor); + colorAmbient->setMaximumHeight(23); + + grid->addWidget(labelAmbient, row, 0); + grid->addWidget(colorAmbient, row, 1); + row += 1; + + auto* labelEmissive = new QLabel(); + labelEmissive->setText(tr("Emissive Color")); + auto* colorEmissive = new ColorWidget(material.emissiveColor); + colorEmissive->setMaximumHeight(23); + + grid->addWidget(labelEmissive, row, 0); + grid->addWidget(colorEmissive, row, 1); + row += 1; + + auto* labelSpecular = new QLabel(); + labelSpecular->setText(tr("Specular Color")); + auto* colorSpecular = new ColorWidget(material.specularColor); + colorSpecular->setMaximumHeight(23); + + grid->addWidget(labelSpecular, row, 0); + grid->addWidget(colorSpecular, row, 1); + row += 1; + + auto* labelShininess = new QLabel(); + labelShininess->setText(tr("Shininess")); + auto* editShininess = new QLineEdit(); + editShininess->setText(QString::number(material.shininess)); + editShininess->setEnabled(false); + + grid->addWidget(labelShininess, row, 0); + grid->addWidget(editShininess, row, 1); + + return tab; +} + + +/* TRANSLATOR MatGui::TaskInspectAppearance */ + +TaskInspectAppearance::TaskInspectAppearance() +{ + widget = new DlgInspectAppearance(); + addTaskBox(Gui::BitmapFactory().pixmap("Part_Loft"), widget); +} + +TaskInspectAppearance::~TaskInspectAppearance() = default; + +void TaskInspectAppearance::open() +{} + +void TaskInspectAppearance::clicked(int) +{} + +bool TaskInspectAppearance::accept() +{ + return widget->accept(); +} + +#include "moc_DlgInspectAppearance.cpp" diff --git a/src/Mod/Material/Gui/DlgInspectAppearance.h b/src/Mod/Material/Gui/DlgInspectAppearance.h new file mode 100644 index 0000000000..fbcdb4855c --- /dev/null +++ b/src/Mod/Material/Gui/DlgInspectAppearance.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MATGUI_DLGINSPECTAPPEARANCE_H +#define MATGUI_DLGINSPECTAPPEARANCE_H + +#include + +#include +#include + +namespace App +{ +class Color; +} + +namespace Gui +{ +class ViewProvider; +} + +namespace MatGui +{ +class Ui_DlgInspectAppearance; + +class ColorWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ColorWidget(const App::Color& color, QWidget* parent = nullptr); + ~ColorWidget() override = default; + + QSize sizeHint() const override { return {75,23}; } + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + QColor _color; +}; + +class DlgInspectAppearance: public QWidget, public Gui::SelectionSingleton::ObserverType +{ + Q_OBJECT + +public: + explicit DlgInspectAppearance(QWidget* parent = nullptr); + ~DlgInspectAppearance() override; + + bool accept(); + // bool reject(); + + /// Observer message from the Selection + void OnChange(Gui::SelectionSingleton::SubjectType& rCaller, + Gui::SelectionSingleton::MessageType Reason) override; + +protected: + +private: + std::unique_ptr ui; + + std::vector getSelection() const; + void update(std::vector& views); + QWidget* makeAppearanceTab(const App::Material& material); +}; + + +class TaskInspectAppearance: public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskInspectAppearance(); + ~TaskInspectAppearance() override; + +public: + void open() override; + bool accept() override; + // bool reject() override; + void clicked(int) override; + + QDialogButtonBox::StandardButtons getStandardButtons() const override + { + return QDialogButtonBox::Ok; + } + +private: + DlgInspectAppearance* widget; +}; + +} // namespace MatGui + +#endif // MATGUI_DLGINSPECTAPPEARANCE_H diff --git a/src/Mod/Material/Gui/DlgInspectAppearance.ui b/src/Mod/Material/Gui/DlgInspectAppearance.ui new file mode 100644 index 0000000000..321dc1a0b4 --- /dev/null +++ b/src/Mod/Material/Gui/DlgInspectAppearance.ui @@ -0,0 +1,122 @@ + + + MatGui::DlgInspectAppearance + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + true + + + + + 0 + 0 + 380 + 280 + + + + + + + Document + + + + + + + + + + + + + + + Document Name + + + + + + + Name of the active document + + + + + + + Label / Internal Name + + + + + + + + + + Sub.Shape / Type + + + + + + + Shape.TypeID / TypeID + + + + + + + + + + + + + Appearance + + + + + + + Tab 1 + + + + + Tab 2 + + + + + + + + + + + + + + + + diff --git a/src/Mod/Material/Gui/DlgInspectMaterial.cpp b/src/Mod/Material/Gui/DlgInspectMaterial.cpp new file mode 100644 index 0000000000..c25586d2b0 --- /dev/null +++ b/src/Mod/Material/Gui/DlgInspectMaterial.cpp @@ -0,0 +1,425 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "DlgInspectMaterial.h" +#include "ui_DlgInspectMaterial.h" + + +using namespace MatGui; + +/* TRANSLATOR MatGui::DlgInspectMaterial */ + +DlgInspectMaterial::DlgInspectMaterial(QWidget* parent) + : QWidget(parent) + , ui(new Ui_DlgInspectMaterial) +{ + ui->setupUi(this); + + auto tree = ui->treeMaterials; + auto model = new QStandardItemModel(); + tree->setModel(model); + + tree->setHeaderHidden(true); + + + std::vector views = getSelection(); + update(views); + + connect(ui->buttonClipboard, &QPushButton::clicked, this, &DlgInspectMaterial::onClipboard); + + Gui::Selection().Attach(this); +} + +DlgInspectMaterial::~DlgInspectMaterial() +{ + Gui::Selection().Detach(this); +} + +bool DlgInspectMaterial::accept() +{ + return true; +} + +void DlgInspectMaterial::onClipboard(bool checked) +{ + Q_UNUSED(checked) + + QApplication::clipboard()->setText(clipboardText); +} + +std::vector DlgInspectMaterial::getSelection() const +{ + std::vector views; + + // get a single selection + std::vector sel = + Gui::Selection().getSelection(nullptr, Gui::ResolveMode::OldStyleElement, true); + for (const auto& it : sel) { + Gui::ViewProvider* view = + Gui::Application::Instance->getDocument(it.pDoc)->getViewProvider(it.pObject); + views.push_back(view); + } + + return views; +} + +/// @cond DOXERR +void DlgInspectMaterial::OnChange(Gui::SelectionSingleton::SubjectType& rCaller, + Gui::SelectionSingleton::MessageType Reason) +{ + Q_UNUSED(rCaller); + + if (Reason.Type == Gui::SelectionChanges::AddSelection + || Reason.Type == Gui::SelectionChanges::RmvSelection + || Reason.Type == Gui::SelectionChanges::SetSelection + || Reason.Type == Gui::SelectionChanges::ClrSelection) { + std::vector views = getSelection(); + update(views); + } +} +/// @endcond + +void DlgInspectMaterial::appendClip(QString text) +{ + // Need to add indent + QString indent(clipboardIndent * 4, QLatin1Char(' ')); + clipboardText += indent + text + QLatin1String("\n"); +} + +QStandardItem* DlgInspectMaterial::clipItem(QString text) +{ + appendClip(text); + auto item = new QStandardItem(text); + return item; +} + +void DlgInspectMaterial::indent() +{ + clipboardIndent += 1; +} + +void DlgInspectMaterial::unindent() +{ + if (clipboardIndent > 0) { + clipboardIndent -= 1; + } +} + +void DlgInspectMaterial::update(std::vector& views) +{ + clipboardText = QLatin1String(""); + clipboardIndent = 0; + App::Document* doc = App::GetApplication().getActiveDocument(); + if (doc) { + appendClip(tr("Document: ") + QString::fromUtf8(doc->Label.getValue())); + ui->editDocument->setText(QString::fromUtf8(doc->Label.getValue())); + + if (views.size() == 1) { + auto view = dynamic_cast(views[0]); + if (!view) { + return; + } + auto* obj = view->getObject(); + if (!obj) { + return; + } + auto* labelProp = dynamic_cast(obj->getPropertyByName("Label")); + if (labelProp) { + ui->editObjectLabel->setText(QString::fromUtf8(labelProp->getValue())); + appendClip(tr("Label: ") + QString::fromUtf8(labelProp->getValue())); + } + else { + ui->editObjectLabel->setText(QLatin1String("")); + } + ui->editObjectName->setText(QLatin1String(obj->getNameInDocument())); + appendClip(tr("Internal Name: ") + QString::fromUtf8(obj->getNameInDocument())); + + auto subElement = Gui::Selection().getSelectionEx(); + if (subElement.size() > 0) { + auto& subObject = subElement[0]; + if (subObject.getSubNames().size() > 0) { + ui->editSubShape->setText(QString::fromStdString(subObject.getSubNames()[0])); + } + else { + ui->editSubShape->setText(QLatin1String("")); + } + } + else { + ui->editSubShape->setText(QLatin1String("")); + } + + auto subShapeType = QString::fromUtf8(obj->getTypeId().getName()); + subShapeType.remove(subShapeType.indexOf(QLatin1String("::")), subShapeType.size()); + appendClip(tr("Type: ") + subShapeType); + ui->editSubShapeType->setText(subShapeType); + appendClip(tr("TypeID: ") + QString::fromUtf8(obj->getTypeId().getName())); + ui->editShapeType->setText(QString::fromUtf8(obj->getTypeId().getName())); + + if (labelProp && QString::fromUtf8(labelProp->getValue()).size() > 0) { + auto* prop = dynamic_cast( + obj->getPropertyByName("ShapeMaterial")); + if (prop) { + updateMaterialTree(prop->getValue()); + } + } + } + } +} + +void DlgInspectMaterial::updateMaterialTree(const Materials::Material& material) +{ + Base::Console().Log("Material '%s'\n", material.getName().toStdString().c_str()); + + auto tree = ui->treeMaterials; + auto model = dynamic_cast(tree->model()); + model->clear(); + + addMaterial(tree, model, material); +} + +void DlgInspectMaterial::addMaterial(QTreeView* tree, + QStandardItemModel* parent, + const Materials::Material& material) +{ + auto card = clipItem(tr("Name: ") + material.getName()); + addExpanded(tree, parent, card); + + indent(); + addMaterialDetails(tree, card, material); + unindent(); +} + +void DlgInspectMaterial::addMaterial(QTreeView* tree, + QStandardItem* parent, + const Materials::Material& material) +{ + auto card = clipItem(tr("Name: ") + material.getName()); + addExpanded(tree, parent, card); + + indent(); + addMaterialDetails(tree, card, material); + unindent(); +} + +void DlgInspectMaterial::addModels(QTreeView* tree, + QStandardItem* parent, + const QSet* models) +{ + if (models->isEmpty()) { + auto none = clipItem(tr("None")); + addExpanded(tree, parent, none); + } + else { + for (const QString& uuid : *models) { + auto model = modelManager.getModel(uuid); + auto name = clipItem(tr("Name: ") + model->getName()); + addExpanded(tree, parent, name); + + indent(); + addModelDetails(tree, name, model); + unindent(); + } + } +} + +void DlgInspectMaterial::addModelDetails(QTreeView* tree, + QStandardItem* parent, + std::shared_ptr& model) +{ + auto uuid = clipItem(tr("UUID: ") + model->getUUID()); + addExpanded(tree, parent, uuid); + + auto library = clipItem(tr("Library: ") + model->getLibrary()->getName()); + addExpanded(tree, parent, library); + + auto libraryPath = + clipItem(tr("Library Directory: ") + model->getLibrary()->getDirectoryPath()); + addExpanded(tree, parent, libraryPath); + + auto directory = clipItem(tr("Sub Directory: ") + model->getDirectory()); + addExpanded(tree, parent, directory); + + auto inherits = clipItem(tr("Inherits:")); + addExpanded(tree, parent, inherits); + + auto& inheritedUuids = model->getInheritance(); + indent(); + if (inheritedUuids.isEmpty()) { + auto none = clipItem(tr("None")); + addExpanded(tree, inherits, none); + } + else { + for (const QString& inherited : inheritedUuids) { + auto inheritedModel = modelManager.getModel(inherited); + + auto name = clipItem(tr("Name: ") + inheritedModel->getName()); + addExpanded(tree, inherits, name); + + indent(); + addModelDetails(tree, name, inheritedModel); + unindent(); + } + } + unindent(); +} + +void DlgInspectMaterial::addProperties( + QTreeView* tree, + QStandardItem* parent, + const std::map>& properties) +{ + if (properties.empty()) { + auto none = clipItem(tr("None")); + addExpanded(tree, parent, none); + } + else { + for (auto& property : properties) { + auto name = clipItem(tr("Name: ") + property.second->getName()); + addExpanded(tree, parent, name); + + indent(); + addPropertyDetails(tree, name, property.second); + unindent(); + } + } +} + +void DlgInspectMaterial::addPropertyDetails( + QTreeView* tree, + QStandardItem* parent, + const std::shared_ptr& property) +{ + auto uuid = clipItem(tr("Model UUID: ") + property->getModelUUID()); + addExpanded(tree, parent, uuid); + auto type = clipItem(tr("Type: ") + property->getPropertyType()); + addExpanded(tree, parent, type); + auto hasValue = clipItem(tr("Has value: ") + (property->isNull() ? tr("No") : tr("Yes"))); + addExpanded(tree, parent, hasValue); +} + +void DlgInspectMaterial::addMaterialDetails(QTreeView* tree, + QStandardItem* parent, + const Materials::Material& material) +{ + auto uuid = clipItem(tr("UUID: ") + material.getUUID()); + addExpanded(tree, parent, uuid); + auto library = clipItem(tr("Library: ") + material.getLibrary()->getName()); + addExpanded(tree, parent, library); + auto libraryPath = + clipItem(tr("Library Directory: ") + material.getLibrary()->getDirectoryPath()); + addExpanded(tree, parent, libraryPath); + auto directory = clipItem(tr("Sub Directory: ") + material.getDirectory()); + addExpanded(tree, parent, directory); + auto inherits = clipItem(tr("Inherits:")); + addExpanded(tree, parent, inherits); + + indent(); + auto parentUUID = material.getParentUUID(); + if (!parentUUID.isEmpty()) { + auto parentMaterial = materialManager.getMaterial(material.getParentUUID()); + addMaterial(tree, inherits, *parentMaterial); + } + else { + auto none = clipItem(tr("None")); + addExpanded(tree, inherits, none); + } + unindent(); + + auto appearance = clipItem(tr("Appearance Models:")); + addExpanded(tree, parent, appearance); + indent(); + addModels(tree, appearance, material.getAppearanceModels()); + unindent(); + + auto physical = clipItem(tr("Physical Models:")); + addExpanded(tree, parent, physical); + indent(); + addModels(tree, physical, material.getPhysicalModels()); + unindent(); + + auto appearanceProperties = clipItem(tr("Appearance Properties:")); + addExpanded(tree, parent, appearanceProperties); + indent(); + addProperties(tree, appearanceProperties, material.getAppearanceProperties()); + unindent(); + + auto physicalProperties = clipItem(tr("Physical Properties:")); + addExpanded(tree, parent, physicalProperties); + indent(); + addProperties(tree, physicalProperties, material.getPhysicalProperties()); + unindent(); +} + +void DlgInspectMaterial::addExpanded(QTreeView* tree, + QStandardItemModel* parent, + QStandardItem* child) +{ + parent->appendRow(child); + tree->setExpanded(child->index(), true); +} + +void DlgInspectMaterial::addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child) +{ + parent->appendRow(child); + tree->setExpanded(child->index(), true); +} + +/* TRANSLATOR MatGui::TaskInspectMaterial */ + +TaskInspectMaterial::TaskInspectMaterial() +{ + widget = new DlgInspectMaterial(); + addTaskBox(Gui::BitmapFactory().pixmap("Part_Loft"), widget); +} + +TaskInspectMaterial::~TaskInspectMaterial() = default; + +void TaskInspectMaterial::open() +{} + +void TaskInspectMaterial::clicked(int) +{} + +bool TaskInspectMaterial::accept() +{ + return widget->accept(); +} + +#include "moc_DlgInspectMaterial.cpp" diff --git a/src/Mod/Material/Gui/DlgInspectMaterial.h b/src/Mod/Material/Gui/DlgInspectMaterial.h new file mode 100644 index 0000000000..710212a2a4 --- /dev/null +++ b/src/Mod/Material/Gui/DlgInspectMaterial.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MATGUI_DLGINSPECTMATERIAL_H +#define MATGUI_DLGINSPECTMATERIAL_H + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +namespace Gui +{ +class ViewProvider; +} + +namespace MatGui +{ +class Ui_DlgInspectMaterial; + +class DlgInspectMaterial: public QWidget, public Gui::SelectionSingleton::ObserverType +{ + Q_OBJECT + +public: + explicit DlgInspectMaterial(QWidget* parent = nullptr); + ~DlgInspectMaterial() override; + + bool accept(); + void onClipboard(bool checked); + + /// Observer message from the Selection + void OnChange(Gui::SelectionSingleton::SubjectType& rCaller, + Gui::SelectionSingleton::MessageType Reason) override; + +private: + std::unique_ptr ui; + Materials::MaterialManager materialManager; + Materials::ModelManager modelManager; + QString clipboardText; + int clipboardIndent; + + void appendClip(QString text); + QStandardItem* clipItem(QString text); + void indent(); + void unindent(); + + std::vector getSelection() const; + void update(std::vector& views); + void updateMaterialTree(const Materials::Material& material); + void + addMaterial(QTreeView* tree, QStandardItemModel* parent, const Materials::Material& material); + void addMaterial(QTreeView* tree, QStandardItem* parent, const Materials::Material& material); + void + addMaterialDetails(QTreeView* tree, QStandardItem* parent, const Materials::Material& material); + void addModels(QTreeView* tree, QStandardItem* parent, const QSet* models); + void addModelDetails(QTreeView* tree, + QStandardItem* parent, + std::shared_ptr& model); + void addProperties( + QTreeView* tree, + QStandardItem* parent, + const std::map>& properties); + void addPropertyDetails(QTreeView* tree, + QStandardItem* parent, + const std::shared_ptr& property); + + void addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child); + void addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child); +}; + + +class TaskInspectMaterial: public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskInspectMaterial(); + ~TaskInspectMaterial() override; + +public: + void open() override; + bool accept() override; + // bool reject() override; + void clicked(int) override; + + QDialogButtonBox::StandardButtons getStandardButtons() const override + { + return QDialogButtonBox::Ok; + } + +private: + DlgInspectMaterial* widget; +}; + +} // namespace MatGui + +#endif // MATGUI_DLGINSPECTMATERIAL_H diff --git a/src/Mod/Material/Gui/DlgInspectMaterial.ui b/src/Mod/Material/Gui/DlgInspectMaterial.ui new file mode 100644 index 0000000000..dec62fce7c --- /dev/null +++ b/src/Mod/Material/Gui/DlgInspectMaterial.ui @@ -0,0 +1,145 @@ + + + MatGui::DlgInspectMaterial + + + + 0 + 0 + 400 + 432 + + + + Form + + + + + + true + + + + + 0 + 0 + 376 + 408 + + + + + + + Document + + + + + + + + + + + + + + + Document Name + + + + + + + Name of the active document + + + + + + + Label / Internal Name + + + + + + + + + + Sub.Shape / Type + + + + + + + Shape.TypeID / TypeID + + + + + + + + + + + + + Material + + + + + + Qt::ScrollBarAsNeeded + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Copy to clipboard + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Material/Gui/Workbench.cpp b/src/Mod/Material/Gui/Workbench.cpp index 65ee6aa6b8..b988ef4791 100644 --- a/src/Mod/Material/Gui/Workbench.cpp +++ b/src/Mod/Material/Gui/Workbench.cpp @@ -47,10 +47,10 @@ Gui::MenuItem* Workbench::setupMenuBar() const Gui::MenuItem* root = StdWorkbench::setupMenuBar(); Gui::MenuItem* item = root->findItem("&Windows"); - Gui::MenuItem* part = new Gui::MenuItem; - root->insertItem(item, part); - part->setCommand("&Materials"); - *part << "Materials_Edit"; + Gui::MenuItem* material = new Gui::MenuItem; + root->insertItem(item, material); + material->setCommand("&Materials"); + *material << "Materials_Edit"; return root; } @@ -59,9 +59,9 @@ Gui::ToolBarItem* Workbench::setupToolBars() const { Gui::ToolBarItem* root = StdWorkbench::setupToolBars(); - Gui::ToolBarItem* solids = new Gui::ToolBarItem(root); - solids->setCommand("Materials"); - *solids << "Materials_Edit"; + Gui::ToolBarItem* material = new Gui::ToolBarItem(root); + material->setCommand("Materials"); + *material << "Materials_Edit"; return root; } diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index e2a1b4453f..98e36e5ede 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -117,6 +117,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const << copy << "Part_CheckGeometry" << "Part_Defeaturing" + << "Materials_InspectAppearance" + << "Materials_InspectMaterial" << "Separator" << bop << join << split << compound << "Separator" diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 8d7ab5d3c1..e49c6275ed 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -564,6 +564,9 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Separator" << dressups << "Separator" + << "Materials_InspectAppearance" + << "Materials_InspectMaterial" + << "Separator" << "PartDesign_Boolean" << "Separator" << "Part_CheckGeometry" diff --git a/tests/src/Mod/Material/App/TestMaterialProperties.cpp b/tests/src/Mod/Material/App/TestMaterialProperties.cpp index 455424c399..447adb1c8d 100644 --- a/tests/src/Mod/Material/App/TestMaterialProperties.cpp +++ b/tests/src/Mod/Material/App/TestMaterialProperties.cpp @@ -130,8 +130,9 @@ TEST_F(TestMaterialProperties, TestEmpty) TEST_F(TestMaterialProperties, TestSingle) { - Materials::MaterialProperty prop(modelProp1); + Materials::MaterialProperty prop(modelProp1, QLatin1String("sampleUUID")); EXPECT_EQ(prop.getType(), Materials::MaterialValue::Quantity); + EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); EXPECT_TRUE(prop.isNull()); auto variant = prop.getValue(); EXPECT_TRUE(variant.canConvert()); @@ -145,6 +146,7 @@ TEST_F(TestMaterialProperties, TestSingle) void check2DArray(Materials::MaterialProperty& prop) { EXPECT_EQ(prop.getType(), Materials::MaterialValue::Array2D); + EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); EXPECT_TRUE(prop.isNull()); auto array = std::static_pointer_cast(prop.getMaterialValue()); EXPECT_EQ(array->rows(), 0); @@ -160,20 +162,20 @@ void check2DArray(Materials::MaterialProperty& prop) TEST_F(TestMaterialProperties, Test2DArray) { - Materials::MaterialProperty prop(modelProp); + Materials::MaterialProperty prop(modelProp, QLatin1String("sampleUUID")); check2DArray(prop); } TEST_F(TestMaterialProperties, Test2DArrayCopy) { - Materials::MaterialProperty propBase(modelProp); + Materials::MaterialProperty propBase(modelProp, QLatin1String("sampleUUID")); Materials::MaterialProperty prop(propBase); check2DArray(prop); } TEST_F(TestMaterialProperties, Test2DArrayAssignment) { - Materials::MaterialProperty propBase(modelProp); + Materials::MaterialProperty propBase(modelProp, QLatin1String("sampleUUID")); Materials::MaterialProperty prop; prop = propBase; @@ -183,6 +185,7 @@ TEST_F(TestMaterialProperties, Test2DArrayAssignment) void check3DArray(Materials::MaterialProperty& prop) { EXPECT_EQ(prop.getType(), Materials::MaterialValue::Array3D); + EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); EXPECT_TRUE(prop.isNull()); auto array = std::static_pointer_cast(prop.getMaterialValue()); EXPECT_EQ(array->depth(), 0); @@ -198,20 +201,20 @@ void check3DArray(Materials::MaterialProperty& prop) TEST_F(TestMaterialProperties, Test3DArray) { - Materials::MaterialProperty prop(model3DProp); + Materials::MaterialProperty prop(model3DProp, QLatin1String("sampleUUID")); check3DArray(prop); } TEST_F(TestMaterialProperties, Test3DArrayCopy) { - Materials::MaterialProperty propBase(model3DProp); + Materials::MaterialProperty propBase(model3DProp, QLatin1String("sampleUUID")); Materials::MaterialProperty prop(propBase); check3DArray(prop); } TEST_F(TestMaterialProperties, Test3DArrayAssignment) { - Materials::MaterialProperty propBase(model3DProp); + Materials::MaterialProperty propBase(model3DProp, QLatin1String("sampleUUID")); Materials::MaterialProperty prop; prop = propBase;