From 9f43b0ff76904df1cd7a02a768df5a03143b6113 Mon Sep 17 00:00:00 2001 From: David Carter Date: Tue, 16 Apr 2024 14:40:24 -0400 Subject: [PATCH] Material: MaterialTreeWidget usability enhancements Improves the MaterialTreeWidget beyond minimum viable product. - Filters can now be filter lists to allow a variety of filtering options. - User preferences allow the inclusion/exclusion of favorites and recents. - Widget state such as expansion, tree expansions, etc are saved and restored. - show current appearancee material when editing. - implements a python interface #fixes 13421: always opens full tree --- src/Mod/Material/App/AppMaterial.cpp | 5 +- src/Mod/Material/App/MaterialFilter.cpp | 25 +- src/Mod/Material/App/MaterialFilter.h | 129 +++++-- src/Mod/Material/App/MaterialFilterPy.xml | 12 +- src/Mod/Material/App/MaterialFilterPyImpl.cpp | 19 +- src/Mod/Material/App/MaterialLibrary.cpp | 12 +- src/Mod/Material/App/MaterialLibrary.h | 7 +- src/Mod/Material/App/MaterialManager.cpp | 1 + src/Mod/Material/App/MaterialManager.h | 20 +- .../Material/App/MaterialManagerPyImpl.cpp | 2 +- src/Mod/Material/Gui/AppMatGui.cpp | 17 +- src/Mod/Material/Gui/CMakeLists.txt | 9 + src/Mod/Material/Gui/DlgDisplayProperties.ui | 12 - .../Material/Gui/DlgDisplayPropertiesImp.cpp | 78 +++- .../Material/Gui/DlgDisplayPropertiesImp.h | 1 + src/Mod/Material/Gui/DlgSettingsMaterial.cpp | 10 + src/Mod/Material/Gui/DlgSettingsMaterial.ui | 85 ++++- src/Mod/Material/Gui/MaterialTreeWidget.cpp | 333 +++++++++++++++--- src/Mod/Material/Gui/MaterialTreeWidget.h | 130 +++++-- src/Mod/Material/Gui/MaterialTreeWidgetPy.xml | 71 ++++ .../Material/Gui/MaterialTreeWidgetPyImpl.cpp | 238 +++++++++++++ 21 files changed, 1051 insertions(+), 165 deletions(-) create mode 100644 src/Mod/Material/Gui/MaterialTreeWidgetPy.xml create mode 100644 src/Mod/Material/Gui/MaterialTreeWidgetPyImpl.cpp diff --git a/src/Mod/Material/App/AppMaterial.cpp b/src/Mod/Material/App/AppMaterial.cpp index ea5b7bf1e1..6b4b137c9c 100644 --- a/src/Mod/Material/App/AppMaterial.cpp +++ b/src/Mod/Material/App/AppMaterial.cpp @@ -27,9 +27,7 @@ #include #include -// #include "Model.h" -#include "MaterialFilter.h" - +#include "MaterialFilterPy.h" #include "MaterialManagerPy.h" #include "MaterialPy.h" #include "ModelManagerPy.h" @@ -68,6 +66,7 @@ PyMOD_INIT_FUNC(Materials) Base::Console().Log("Loading Material module... done\n"); Base::Interpreter().addType(&Materials::MaterialManagerPy::Type, module, "MaterialManager"); + Base::Interpreter().addType(&Materials::MaterialFilterPy::Type, module, "MaterialFilter"); Base::Interpreter().addType(&Materials::MaterialPy::Type, module, "Material"); Base::Interpreter().addType(&Materials::ModelManagerPy::Type, module, "ModelManager"); Base::Interpreter().addType(&Materials::ModelPropertyPy::Type, module, "ModelProperty"); diff --git a/src/Mod/Material/App/MaterialFilter.cpp b/src/Mod/Material/App/MaterialFilter.cpp index 2a0450d02b..c0448b8c60 100644 --- a/src/Mod/Material/App/MaterialFilter.cpp +++ b/src/Mod/Material/App/MaterialFilter.cpp @@ -33,12 +33,31 @@ using namespace Materials; +MaterialFilterOptions::MaterialFilterOptions() + : _includeFavorites(true) + , _includeRecent(true) + , _includeFolders(true) + , _includeLibraries(true) + , _includeLegacy(false) +{} + +MaterialFilterTreeWidgetOptions::MaterialFilterTreeWidgetOptions() +{ + auto param = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/TreeWidget"); + _includeFavorites = param->GetBool("ShowFavorites", true); + _includeRecent = param->GetBool("ShowRecent", true); + _includeFolders = param->GetBool("ShowEmptyFolders", false); + _includeLibraries = param->GetBool("ShowEmptyLibraries", true); + _includeLegacy = param->GetBool("ShowLegacy", false); +} + +//=== + TYPESYSTEM_SOURCE(Materials::MaterialFilter, Base::BaseClass) MaterialFilter::MaterialFilter() - : _includeFolders(true) - , _includeLegacy(true) - , _required() + : _required() , _requiredComplete() {} diff --git a/src/Mod/Material/App/MaterialFilter.h b/src/Mod/Material/App/MaterialFilter.h index 99ab1d7f3d..44285a2d94 100644 --- a/src/Mod/Material/App/MaterialFilter.h +++ b/src/Mod/Material/App/MaterialFilter.h @@ -24,6 +24,7 @@ #include +#include #include #include @@ -36,6 +37,102 @@ namespace Materials class Material; +/* + * This class is used to set options for a material tree search + * + */ +class MaterialsExport MaterialFilterOptions +{ + +public: + MaterialFilterOptions(); + virtual ~MaterialFilterOptions() = default; + + /* Indicates if we should show favourite materials + * + * Default is to show favourite materials + */ + bool includeFavorites() const + { + return _includeFavorites; + } + void setIncludeFavorites(bool value) + { + _includeFavorites = value; + } + + /* Indicates if we should show recent materials + * + * Default is to show recent materials + */ + bool includeRecent() const + { + return _includeRecent; + } + void setIncludeRecent(bool value) + { + _includeRecent = value; + } + + /* Indicates if we should include empty folders + * + * Default is not to include empty folders + */ + bool includeEmptyFolders() const + { + return _includeFolders; + } + void setIncludeEmptyFolders(bool value) + { + _includeFolders = value; + } + + /* Indicates if we should include empty libraries + * + * Default is to include empty libraries + */ + bool includeEmptyLibraries() const + { + return _includeLibraries; + } + void setIncludeEmptyLibraries(bool value) + { + _includeLibraries = value; + } + + /* Indicates if we should include materials in the older format + * + * Default is not to include legacy format materials + */ + bool includeLegacy() const + { + return _includeLegacy; + } + void setIncludeLegacy(bool legacy) + { + _includeLegacy = legacy; + } + +protected: + bool _includeFavorites; + bool _includeRecent; + bool _includeFolders; + bool _includeLibraries; + bool _includeLegacy; +}; + +/* + * The same class initialized with preferences for the MaterialTreeWidget + * + */ +class MaterialsExport MaterialFilterTreeWidgetOptions: public MaterialFilterOptions +{ + +public: + MaterialFilterTreeWidgetOptions(); + ~MaterialFilterTreeWidgetOptions() override = default; +}; + /* * This class is used to filter materials during a material tree search * @@ -48,30 +145,17 @@ public: MaterialFilter(); virtual ~MaterialFilter() = default; - /* Indicates if we should include empty folders - * - * Default is to include empty folders + /* + * Filter name when used in a list of filters. The name should be + * unique within the list. */ - bool includeEmptyFolders() const + QString name() const { - return _includeFolders; + return _name; } - void setIncludeEmptyFolders(bool value) + void setName(const QString& name) { - _includeFolders = value; - } - - /* Indicates if we should include materials in the older format - * - * Default is to include legacy format materials - */ - bool includeLegacy() const - { - return _includeLegacy; - } - void setIncludeLegacy(bool legacy) - { - _includeLegacy = legacy; + _name = name; } /* Sets of model UUIDs that should be included. Optionally, we can @@ -101,12 +185,13 @@ public: } private: - bool _includeFolders; - bool _includeLegacy; + QString _name; QSet _required; QSet _requiredComplete; }; } // namespace Materials +Q_DECLARE_METATYPE(Materials::MaterialFilter) + #endif // MATERIAL_MATERIALFILTER_H diff --git a/src/Mod/Material/App/MaterialFilterPy.xml b/src/Mod/Material/App/MaterialFilterPy.xml index 3dbbe10074..ac7825ff31 100644 --- a/src/Mod/Material/App/MaterialFilterPy.xml +++ b/src/Mod/Material/App/MaterialFilterPy.xml @@ -15,17 +15,11 @@ Material filters. - + - Include empty folders in the material list. + Name of the filter used to select a filter in a list - - - - - Include legacy materials in the material list. - - + diff --git a/src/Mod/Material/App/MaterialFilterPyImpl.cpp b/src/Mod/Material/App/MaterialFilterPyImpl.cpp index bc8e635c20..2b467e5587 100644 --- a/src/Mod/Material/App/MaterialFilterPyImpl.cpp +++ b/src/Mod/Material/App/MaterialFilterPyImpl.cpp @@ -61,24 +61,15 @@ int MaterialFilterPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) return 0; } -Py::Boolean MaterialFilterPy::getIncludeEmptyFolders() const +Py::String MaterialFilterPy::getName() const { - return {getMaterialFilterPtr()->includeEmptyFolders()}; + auto filterName = getMaterialFilterPtr()->name(); + return {filterName.toStdString()}; } -void MaterialFilterPy::setIncludeEmptyFolders(const Py::Boolean value) +void MaterialFilterPy::setName(const Py::String value) { - getMaterialFilterPtr()->setIncludeEmptyFolders(value.isTrue()); -} - -Py::Boolean MaterialFilterPy::getIncludeLegacy() const -{ - return {getMaterialFilterPtr()->includeLegacy()}; -} - -void MaterialFilterPy::setIncludeLegacy(const Py::Boolean value) -{ - getMaterialFilterPtr()->setIncludeLegacy(value.isTrue()); + getMaterialFilterPtr()->setName(QString::fromStdString(value)); } Py::List MaterialFilterPy::getRequiredModels() const diff --git a/src/Mod/Material/App/MaterialLibrary.cpp b/src/Mod/Material/App/MaterialLibrary.cpp index 28ffdd27eb..221dcbcc0e 100644 --- a/src/Mod/Material/App/MaterialLibrary.cpp +++ b/src/Mod/Material/App/MaterialLibrary.cpp @@ -266,7 +266,8 @@ QString MaterialLibrary::getUUIDFromPath(const QString& path) const } bool MaterialLibrary::materialInTree(const std::shared_ptr& material, - const std::shared_ptr& filter) const + const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const { if (!filter) { // If there's no filter we always include @@ -274,7 +275,7 @@ bool MaterialLibrary::materialInTree(const std::shared_ptr& material, } // filter out old format files - if (material->isOldFormat() && !filter->includeLegacy()) { + if (material->isOldFormat() && !options.includeLegacy()) { return false; } @@ -283,7 +284,8 @@ bool MaterialLibrary::materialInTree(const std::shared_ptr& material, } std::shared_ptr>> -MaterialLibrary::getMaterialTree(const std::shared_ptr& filter) const +MaterialLibrary::getMaterialTree(const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const { std::shared_ptr>> materialTree = std::make_shared>>(); @@ -292,7 +294,7 @@ MaterialLibrary::getMaterialTree(const std::shared_ptrincludeEmptyFolders()) { + if (!filter || options.includeEmptyFolders()) { auto folderList = MaterialLoader::getMaterialFolders(*this); for (auto& folder : *folderList) { QStringList list = folder.split(QString::fromStdString("/")); diff --git a/src/Mod/Material/App/MaterialLibrary.h b/src/Mod/Material/App/MaterialLibrary.h index ec0175743a..bb40951258 100644 --- a/src/Mod/Material/App/MaterialLibrary.h +++ b/src/Mod/Material/App/MaterialLibrary.h @@ -41,6 +41,7 @@ namespace Materials class Material; class MaterialManager; class MaterialFilter; +class MaterialFilterOptions; class MaterialsExport MaterialLibrary: public LibraryBase, public std::enable_shared_from_this @@ -79,7 +80,8 @@ public: std::shared_ptr addMaterial(const std::shared_ptr& material, const QString& path); std::shared_ptr>> - getMaterialTree(const std::shared_ptr& filter) const; + getMaterialTree(const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const; bool isReadOnly() const { @@ -99,7 +101,8 @@ protected: void updatePaths(const QString& oldPath, const QString& newPath); QString getUUIDFromPath(const QString& path) const; bool materialInTree(const std::shared_ptr& material, - const std::shared_ptr& filter) const; + const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const; bool _readOnly; std::unique_ptr>> _materialPathMap; diff --git a/src/Mod/Material/App/MaterialManager.cpp b/src/Mod/Material/App/MaterialManager.cpp index 6e03f2720c..afb0c9024b 100644 --- a/src/Mod/Material/App/MaterialManager.cpp +++ b/src/Mod/Material/App/MaterialManager.cpp @@ -24,6 +24,7 @@ #include #endif +#include #include #include diff --git a/src/Mod/Material/App/MaterialManager.h b/src/Mod/Material/App/MaterialManager.h index ed6926e657..e8c1c5b13f 100644 --- a/src/Mod/Material/App/MaterialManager.h +++ b/src/Mod/Material/App/MaterialManager.h @@ -24,8 +24,6 @@ #include -#include - #include #include @@ -34,9 +32,12 @@ #include "Materials.h" #include "MaterialLibrary.h" +#include "MaterialFilter.h" namespace fs = boost::filesystem; +class QMutex; + namespace App { class Material; @@ -45,8 +46,6 @@ class Material; namespace Materials { -class MaterialFilter; - class MaterialsExport MaterialManager: public Base::BaseClass { TYPESYSTEM_HEADER_WITH_OVERRIDE(); @@ -77,13 +76,22 @@ public: getMaterialTree(const std::shared_ptr& library, const std::shared_ptr& filter) const { - return library->getMaterialTree(filter); + MaterialFilterOptions options; + return library->getMaterialTree(filter, options); + } + std::shared_ptr>> + getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) const + { + return library->getMaterialTree(filter, options); } std::shared_ptr>> getMaterialTree(const std::shared_ptr& library) const { std::shared_ptr filter; - return library->getMaterialTree(filter); + MaterialFilterOptions options; + return library->getMaterialTree(filter, options); } std::shared_ptr> getMaterialFolders(const std::shared_ptr& library) const; diff --git a/src/Mod/Material/App/MaterialManagerPyImpl.cpp b/src/Mod/Material/App/MaterialManagerPyImpl.cpp index e6dd15e7ab..e5af5059e4 100644 --- a/src/Mod/Material/App/MaterialManagerPyImpl.cpp +++ b/src/Mod/Material/App/MaterialManagerPyImpl.cpp @@ -71,7 +71,7 @@ PyObject* MaterialManagerPy::getMaterial(PyObject* args) PyObject* MaterialManagerPy::getMaterialByPath(PyObject* args) { - char* path; + char* path {}; const char* lib = ""; if (!PyArg_ParseTuple(args, "et|s", "utf-8", &path, &lib)) { return nullptr; diff --git a/src/Mod/Material/Gui/AppMatGui.cpp b/src/Mod/Material/Gui/AppMatGui.cpp index 0c5f673cf1..db053bcb5f 100644 --- a/src/Mod/Material/Gui/AppMatGui.cpp +++ b/src/Mod/Material/Gui/AppMatGui.cpp @@ -34,6 +34,8 @@ #include "DlgSettingsMaterial.h" #include "Workbench.h" #include "WorkbenchManipulator.h" +#include "MaterialTreeWidget.h" +#include "MaterialTreeWidgetPy.h" // use a different name to CreateCommand() void CreateMaterialCommands(); @@ -78,7 +80,7 @@ PyMOD_INIT_FUNC(MatGui) // load needed modules try { - Base::Interpreter().runString("import Material"); + Base::Interpreter().runString("import Materials"); } catch (const Base::Exception& e) { PyErr_SetString(PyExc_ImportError, e.what()); @@ -107,5 +109,18 @@ PyMOD_INIT_FUNC(MatGui) // add resources and reloads the translators loadMaterialResource(); + Base::Interpreter().addType(&MatGui::MaterialTreeWidgetPy::Type, + matGuiModule, + "MaterialTreeWidget"); + + + // Initialize types + + MatGui::MaterialTreeWidget::init(); + + // Add custom widgets + new Gui::WidgetProducer; + + PyMOD_Return(matGuiModule); } diff --git a/src/Mod/Material/Gui/CMakeLists.txt b/src/Mod/Material/Gui/CMakeLists.txt index 3e9dee2510..6c754a369c 100644 --- a/src/Mod/Material/Gui/CMakeLists.txt +++ b/src/Mod/Material/Gui/CMakeLists.txt @@ -36,6 +36,14 @@ qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts" qt_create_resource_file(${Material_TR_QRC} ${QM_SRCS}) qt_add_resources(MatGui_QRC_SRCS Resources/Material.qrc ${Material_TR_QRC}) +generate_from_xml(MaterialTreeWidgetPy) + +SET(Python_SRCS + MaterialTreeWidgetPy.xml + MaterialTreeWidgetPyImpl.cpp +) +SOURCE_GROUP("Python" FILES ${Python_SRCS}) + set(MatGui_UIC_SRCS Array2D.ui Array3D.ui @@ -51,6 +59,7 @@ set(MatGui_UIC_SRCS ) SET(MatGui_SRCS + ${Python_SRCS} ${MatGui_QRC_SRCS} ${MatGui_UIC_HDRS} AppearancePreview.h diff --git a/src/Mod/Material/Gui/DlgDisplayProperties.ui b/src/Mod/Material/Gui/DlgDisplayProperties.ui index cc054d201e..fd80c1d23d 100644 --- a/src/Mod/Material/Gui/DlgDisplayProperties.ui +++ b/src/Mod/Material/Gui/DlgDisplayProperties.ui @@ -402,18 +402,6 @@ - - - 0 - 0 - - - - - 40 - 32767 - - ... diff --git a/src/Mod/Material/Gui/DlgDisplayPropertiesImp.cpp b/src/Mod/Material/Gui/DlgDisplayPropertiesImp.cpp index 08d7dc37dd..eb68612826 100644 --- a/src/Mod/Material/Gui/DlgDisplayPropertiesImp.cpp +++ b/src/Mod/Material/Gui/DlgDisplayPropertiesImp.cpp @@ -163,11 +163,7 @@ DlgDisplayPropertiesImp::DlgDisplayPropertiesImp(bool floating, QWidget* parent, // Create a filter to only include current format materials // that contain the basic render model. - auto filter = std::make_shared(); - filter->setIncludeEmptyFolders(false); - filter->setIncludeLegacy(false); - filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic); - d->ui.widgetMaterial->setFilter(filter); + setupFilters(); std::vector views = getSelection(); setDisplayModes(views); @@ -209,6 +205,32 @@ DlgDisplayPropertiesImp::~DlgDisplayPropertiesImp() Gui::Selection().Detach(this); } +void DlgDisplayPropertiesImp::setupFilters() +{ + // Create a filter to only include current format materials + // that contain the basic render model. + auto filterList = std::make_shared>>(); + + auto filter = std::make_shared(); + filter->setName(tr("Basic Appearance")); + filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic); + filterList->push_back(filter); + + filter = std::make_shared(); + filter->setName(tr("Texture Appearance")); + filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Texture); + filterList->push_back(filter); + + filter = std::make_shared(); + filter->setName(tr("All Materials")); + filterList->push_back(filter); + + d->ui.widgetMaterial->setIncludeEmptyFolders(false); + d->ui.widgetMaterial->setIncludeLegacy(false); + + d->ui.widgetMaterial->setFilter(filterList); +} + void DlgDisplayPropertiesImp::setupConnections() { #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) @@ -337,6 +359,7 @@ void DlgDisplayPropertiesImp::slotChangedObject(const Gui::ViewProvider& obj, else if (prop.isDerivedFrom()) { //auto& value = static_cast(prop).getValue(); if (prop_name == "ShapeAppearance") { + // Base::Console().Log("slotChangeObject(ShapeAppearance)\n"); // bool blocked = d->ui.buttonColor->blockSignals(true); // auto color = value.diffuseColor; // d->ui.buttonColor->setColor(QColor((int)(255.0f * color.r), @@ -565,17 +588,21 @@ void DlgDisplayPropertiesImp::setDisplayModes(const std::vector& views) { - bool material = false; - //App::Material::MaterialType matType = App::Material::DEFAULT; - for (auto view : views) { - if (auto* prop = - dynamic_cast(view->getPropertyByName("ShapeMaterial"))) { - material = true; - // matType = prop->getValue().getType(); - break; - } - } - d->ui.buttonUserDefinedMaterial->setEnabled(material); + Q_UNUSED(views); + // bool material = false; + // App::Material mat = App::Material(App::Material::DEFAULT); + // for (auto view : views) { + // if (auto* prop = + // dynamic_cast(view->getPropertyByName("ShapeMaterial"))) { + // mat = prop->getValue(); + // material = mat.uuid.empty(); + // if (!material) { + // d->ui.widgetMaterial->setMaterial(QString::fromStdString(mat.uuid)); + // } + // break; + // } + // } + // d->ui.buttonUserDefinedMaterial->setEnabled(material); } void DlgDisplayPropertiesImp::setColorPlot(const std::vector& views) @@ -595,9 +622,21 @@ void DlgDisplayPropertiesImp::setColorPlot(const std::vector void DlgDisplayPropertiesImp::setShapeAppearance(const std::vector& views) { - Q_UNUSED(views) - - // Private::setElementAppearance(views, "ShapeColor", d->ui.buttonColor); + bool material = false; + App::Material mat = App::Material(App::Material::DEFAULT); + for (auto view : views) { + if (auto* prop = + dynamic_cast(view->getPropertyByName("ShapeAppearance"))) { + mat = prop->getValues()[0]; + material = mat.uuid.empty(); + if (!material) { + d->ui.widgetMaterial->setMaterial(QString::fromStdString(mat.uuid)); + } + break; + } + } + // d->ui.buttonUserDefinedMaterial->setEnabled(material); + d->ui.buttonUserDefinedMaterial->setEnabled(true); } void DlgDisplayPropertiesImp::setLineColor(const std::vector& views) @@ -668,6 +707,7 @@ void DlgDisplayPropertiesImp::onMaterialSelected( material->getAppearanceProperty(QString::fromLatin1("Shininess"))->getFloat(); mat.transparency = material->getAppearanceProperty(QString::fromLatin1("Transparency"))->getFloat(); + mat.uuid = material->getUUID().toStdString(); prop->setValue(mat); } } diff --git a/src/Mod/Material/Gui/DlgDisplayPropertiesImp.h b/src/Mod/Material/Gui/DlgDisplayPropertiesImp.h index 3914256b58..d16b2aea88 100644 --- a/src/Mod/Material/Gui/DlgDisplayPropertiesImp.h +++ b/src/Mod/Material/Gui/DlgDisplayPropertiesImp.h @@ -84,6 +84,7 @@ protected: private: void setupConnections(); + void setupFilters(); void slotChangedObject(const Gui::ViewProvider&, const App::Property& Prop); void setDisplayModes(const std::vector&); void setMaterial(const std::vector&); diff --git a/src/Mod/Material/Gui/DlgSettingsMaterial.cpp b/src/Mod/Material/Gui/DlgSettingsMaterial.cpp index 5c0436ae68..882687c857 100644 --- a/src/Mod/Material/Gui/DlgSettingsMaterial.cpp +++ b/src/Mod/Material/Gui/DlgSettingsMaterial.cpp @@ -43,6 +43,11 @@ void DlgSettingsMaterial::saveSettings() ui->fc_custom_mat_dir->onSave(); ui->cb_delete_duplicates->onSave(); ui->cb_sort_by_resources->onSave(); + ui->cb_show_favorites->onSave(); + ui->cb_show_recent->onSave(); + ui->cb_show_empty_libraries->onSave(); + ui->cb_show_empty_folders->onSave(); + ui->cb_show_legacy->onSave(); // Temporary for testing ui->cb_legacy_editor->onSave(); @@ -57,6 +62,11 @@ void DlgSettingsMaterial::loadSettings() ui->fc_custom_mat_dir->onRestore(); ui->cb_delete_duplicates->onRestore(); ui->cb_sort_by_resources->onRestore(); + ui->cb_show_favorites->onRestore(); + ui->cb_show_recent->onRestore(); + ui->cb_show_empty_libraries->onRestore(); + ui->cb_show_empty_folders->onRestore(); + ui->cb_show_legacy->onRestore(); // Temporary for testing ui->cb_legacy_editor->onRestore(); diff --git a/src/Mod/Material/Gui/DlgSettingsMaterial.ui b/src/Mod/Material/Gui/DlgSettingsMaterial.ui index fa3afcd8d8..9a96f2fd6d 100644 --- a/src/Mod/Material/Gui/DlgSettingsMaterial.ui +++ b/src/Mod/Material/Gui/DlgSettingsMaterial.ui @@ -7,7 +7,7 @@ 0 0 434 - 341 + 553 @@ -231,6 +231,89 @@ If unchecked, they will be sorted by their name. + + + + Material Selector + + + + + + Show favorites + + + true + + + ShowFavorites + + + Mod/Material/TreeWidget + + + + + + + Show recent + + + true + + + ShowRecent + + + Mod/Material/TreeWidget + + + + + + + Show empty libraries + + + true + + + ShowEmptyLibraries + + + Mod/Material/TreeWidget + + + + + + + Show empty folders + + + ShowEmptyFolders + + + Mod/Material/TreeWidget + + + + + + + Show legacy files + + + ShowLegacy + + + Mod/Material/TreeWidget + + + + + + diff --git a/src/Mod/Material/Gui/MaterialTreeWidget.cpp b/src/Mod/Material/Gui/MaterialTreeWidget.cpp index b8b82cbd82..aa49c94733 100644 --- a/src/Mod/Material/Gui/MaterialTreeWidget.cpp +++ b/src/Mod/Material/Gui/MaterialTreeWidget.cpp @@ -38,12 +38,14 @@ #include #include +#include #include #include "MaterialTreeWidget.h" #include "MaterialsEditor.h" #include "ui_MaterialsEditor.h" +Q_DECLARE_METATYPE(Materials::MaterialFilterPy*) using Base::Console; using namespace MatGui; @@ -51,7 +53,9 @@ using namespace MatGui; /** Constructs a Material tree widget. */ -MaterialTreeWidget::MaterialTreeWidget(std::shared_ptr filter, +TYPESYSTEM_SOURCE(MatGui::MaterialTreeWidget, Base::BaseClass) + +MaterialTreeWidget::MaterialTreeWidget(const std::shared_ptr& filter, QWidget* parent) : QWidget(parent) , m_expanded(false) @@ -60,10 +64,21 @@ MaterialTreeWidget::MaterialTreeWidget(std::shared_ptr>>& filterList, + QWidget* parent) + : QWidget(parent) + , m_expanded(false) + , _filter(std::make_shared()) + , _filterList(filterList) +{ + setup(); +} + MaterialTreeWidget::MaterialTreeWidget(QWidget* parent) : QWidget(parent) , m_expanded(false) - , _filter(nullptr) + , _filter(std::make_shared()) { setup(); } @@ -80,7 +95,12 @@ void MaterialTreeWidget::setup() /** * Destroys the widget and detaches it from its parameter group. */ -MaterialTreeWidget::~MaterialTreeWidget() = default; +MaterialTreeWidget::~MaterialTreeWidget() +{ + addRecent(m_uuid); + saveWidgetSettings(); + saveMaterialTree(); +} void MaterialTreeWidget::createLayout() { @@ -88,9 +108,9 @@ void MaterialTreeWidget::createLayout() m_expand = new QPushButton(this); m_expand->setIcon(style()->standardIcon(QStyle::SP_TitleBarUnshadeButton)); m_materialTree = new QTreeView(this); + m_filterCombo = new QComboBox(this); m_editor = new QPushButton(tr("Launch editor"), this); - // m_materialTree->setSelectionModel(QAbstractItemView::SingleSelection); m_materialTree->setSelectionMode(QAbstractItemView::SingleSelection); m_materialTree->setSelectionBehavior(QAbstractItemView::SelectItems); @@ -102,6 +122,7 @@ void MaterialTreeWidget::createLayout() treeLayout->addWidget(m_materialTree); auto buttonLayout = new QHBoxLayout(); + buttonLayout->addWidget(m_filterCombo); buttonLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Preferred)); buttonLayout->addWidget(m_editor); @@ -112,18 +133,34 @@ void MaterialTreeWidget::createLayout() layout->addItem(buttonLayout); setLayout(layout); - // Start in an unexpanded state. Store the state? - openWidgetState(false); + // Set the filter if using a filter list + if (hasMultipleFilters()) { + _filter = _filterList->front(); + } + + fillFilterCombo(); + + // Start in the previous expanded state + auto param = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/TreeWidget"); + auto expanded = param->GetBool("WidgetExpanded", false); + setExpanded(expanded); connect(m_expand, &QPushButton::clicked, this, &MaterialTreeWidget::expandClicked); connect(m_editor, &QPushButton::clicked, this, &MaterialTreeWidget::editorClicked); + connect(m_filterCombo, + &QComboBox::currentTextChanged, + this, + &MaterialTreeWidget::onFilter); } -void MaterialTreeWidget::openWidgetState(bool open) +void MaterialTreeWidget::setExpanded(bool open) { m_materialTree->setVisible(open); m_editor->setVisible(open); + setFilterVisible(open); + m_expanded = open; if (open) { @@ -134,12 +171,33 @@ void MaterialTreeWidget::openWidgetState(bool open) } } +void MaterialTreeWidget::setFilterVisible(bool open) +{ + if (open && hasMultipleFilters()) { + m_filterCombo->setVisible(true); + } + else { + m_filterCombo->setVisible(false); + } +} + +void MaterialTreeWidget::fillFilterCombo() +{ + m_filterCombo->clear(); + if (hasMultipleFilters()) { + for (auto const& filter : *_filterList) { + m_filterCombo->addItem(filter->name()); + } + } +} + + void MaterialTreeWidget::expandClicked(bool checked) { Q_UNUSED(checked) // Toggle the open state - openWidgetState(!m_expanded); + setExpanded(!m_expanded); } void MaterialTreeWidget::editorClicked(bool checked) @@ -160,7 +218,7 @@ void MaterialTreeWidget::editorClicked(bool checked) // Gui::Application::Instance->commandManager().runCommandByName("Materials_Edit"); // Toggle the open state - // openWidgetState(!m_expanded); + // setExpanded(!m_expanded); } void MaterialTreeWidget::updateMaterial(const QString& uuid) @@ -212,8 +270,14 @@ QModelIndex MaterialTreeWidget::findInTree(const QString& uuid) auto root = model->invisibleRootItem(); QModelIndex index; - if (findInTree(*root, &index, uuid)) { - return index; + // Find the original item, not the reference in favourites or recents + for (int i = 0; i < root->rowCount(); i++) { + auto child = root->child(i); + if (child->text() != tr("Favorites") && child->text() != tr("Recent")) { + if (findInTree(*child, &index, uuid)) { + return index; + } + } } return {}; @@ -231,6 +295,7 @@ void MaterialTreeWidget::setMaterial(const QString& uuid) if (index.isValid()) { QItemSelectionModel* selectionModel = m_materialTree->selectionModel(); selectionModel->select(index, QItemSelectionModel::SelectCurrent); + m_materialTree->scrollTo(index); } } @@ -239,14 +304,61 @@ QString MaterialTreeWidget::getMaterialUUID() const return m_uuid; } -void MaterialTreeWidget::setFilter(std::shared_ptr filter) +void MaterialTreeWidget::setFilter(const std::shared_ptr& filter) { - _filter.reset(); + if (_filter) { + _filter.reset(); + } + if (_filterList) { + _filterList.reset(); + } + _filter = filter; + fillFilterCombo(); + setFilterVisible(m_expanded); + updateMaterialTree(); } +void MaterialTreeWidget::setFilter( + const std::shared_ptr>>& filterList) +{ + _filter.reset(); + if (_filterList) { + _filterList.reset(); + } + + _filterList = filterList; + if (hasMultipleFilters()) { + _filter = _filterList->front(); + } + + fillFilterCombo(); + setFilterVisible(m_expanded); + + updateMaterialTree(); +} + +void MaterialTreeWidget::setActiveFilter(const QString& name) +{ + if (_filterList) { + for (auto const& filter : *_filterList) { + if (filter->name() == name) { + _filter.reset(); + + _filter = filter; + + // Save the library/folder expansion state + saveMaterialTree(); + + updateMaterialTree(); + return; + } + } + } +} + void MaterialTreeWidget::updateMaterialTree() { _favorites.clear(); @@ -293,6 +405,70 @@ void MaterialTreeWidget::getRecents() } } +void MaterialTreeWidget::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; static_cast(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 MaterialTreeWidget::addRecent(const QString& uuid) +{ + // Ensure it is a material. New, unsaved materials will not be + try { + auto 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(_recentMax)) { + _recents.pop_back(); + } + + saveRecents(); +} + +bool MaterialTreeWidget::isRecent(const QString& uuid) const +{ + for (auto& it : _recents) { + if (it == uuid) { + return true; + } + } + return false; +} + void MaterialTreeWidget::createMaterialTree() { auto model = new QStandardItemModel(this); @@ -312,36 +488,44 @@ void MaterialTreeWidget::createMaterialTree() void MaterialTreeWidget::fillMaterialTree() { + auto param = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/TreeWidget/MaterialTree"); + auto model = dynamic_cast(m_materialTree->model()); - auto lib = new QStandardItem(tr("Favorites")); - lib->setFlags(Qt::ItemIsEnabled); - addExpanded(model, lib); - addFavorites(lib); + if (_filterOptions.includeFavorites()) { + auto lib = new QStandardItem(tr("Favorites")); + lib->setFlags(Qt::ItemIsEnabled); + addExpanded(model, lib, param); + addFavorites(lib); + } - lib = new QStandardItem(tr("Recent")); - lib->setFlags(Qt::ItemIsEnabled); - addExpanded(model, lib); - addRecents(lib); - - // // Create a filter to only include current format materials - // // that contain the basic render model. - // Materials::MaterialFilter filter; - // filter.setIncludeEmptyFolders(false); - // filter.setIncludeLegacy(false); - // filter.addRequired(Materials::ModelUUIDs::ModelUUID_Rendering_Basic); + if (_filterOptions.includeRecent()) { + auto lib = new QStandardItem(tr("Recent")); + lib->setFlags(Qt::ItemIsEnabled); + addExpanded(model, lib, param); + addRecents(lib); + } auto libraries = _materialManager.getMaterialLibraries(); for (const auto& library : *libraries) { - lib = new QStandardItem(library->getName()); - lib->setFlags(Qt::ItemIsEnabled); - addExpanded(model, lib); + auto modelTree = _materialManager.getMaterialTree(library, _filter, _filterOptions); - QIcon icon(library->getIconPath()); - QIcon folderIcon(QString::fromStdString(":/icons/folder.svg")); + bool showLibraries = _filterOptions.includeEmptyLibraries(); + if (!_filterOptions.includeEmptyLibraries() && modelTree->size() > 0) { + showLibraries = true; + } - auto modelTree = _materialManager.getMaterialTree(library, _filter); - addMaterials(*lib, modelTree, folderIcon, icon); + if (showLibraries) { + auto lib = new QStandardItem(library->getName()); + lib->setFlags(Qt::ItemIsEnabled); + addExpanded(model, lib, param); + + QIcon icon(library->getIconPath()); + QIcon folderIcon(QString::fromStdString(":/icons/folder.svg")); + + addMaterials(*lib, modelTree, folderIcon, icon, param); + } } } @@ -351,12 +535,34 @@ void MaterialTreeWidget::addExpanded(QStandardItem* parent, QStandardItem* child m_materialTree->setExpanded(child->index(), true); } +void MaterialTreeWidget::addExpanded(QStandardItem* parent, + QStandardItem* child, + const Base::Reference& param) +{ + parent->appendRow(child); + + // Restore to any previous expansion state + auto expand = param->GetBool(child->text().toStdString().c_str(), true); + m_materialTree->setExpanded(child->index(), expand); +} + void MaterialTreeWidget::addExpanded(QStandardItemModel* model, QStandardItem* child) { model->appendRow(child); m_materialTree->setExpanded(child->index(), true); } +void MaterialTreeWidget::addExpanded(QStandardItemModel* model, + QStandardItem* child, + const Base::Reference& param) +{ + model->appendRow(child); + + // Restore to any previous expansion state + auto expand = param->GetBool(child->text().toStdString().c_str(), true); + m_materialTree->setExpanded(child->index(), expand); +} + void MaterialTreeWidget::addRecents(QStandardItem* parent) { for (auto& uuid : _recents) { @@ -397,17 +603,16 @@ void MaterialTreeWidget::addMaterials( const std::shared_ptr>>& modelTree, const QIcon& folderIcon, - const QIcon& icon) + const QIcon& icon, + const Base::Reference& param) { + auto childParam = param->GetGroup(parent.text().toStdString().c_str()); for (auto& mat : *modelTree) { auto nodePtr = mat.second; if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) { auto 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); card->setData(QVariant(uuid), Qt::UserRole); @@ -416,10 +621,10 @@ void MaterialTreeWidget::addMaterials( } else { auto node = new QStandardItem(folderIcon, mat.first); - addExpanded(&parent, node); + addExpanded(&parent, node, childParam); node->setFlags(Qt::ItemIsEnabled); auto treeMap = nodePtr->getFolder(); - addMaterials(*node, treeMap, folderIcon, icon); + addMaterials(*node, treeMap, folderIcon, icon, childParam); } } } @@ -446,6 +651,7 @@ void MaterialTreeWidget::onSelectMaterial(const QItemSelection& selected, std::string _uuid = uuid.toStdString(); Q_EMIT materialSelected(getMaterialManager().getMaterial(uuid)); + Q_EMIT onMaterial(uuid); } void MaterialTreeWidget::onDoubleClick(const QModelIndex& index) @@ -458,3 +664,48 @@ void MaterialTreeWidget::onDoubleClick(const QModelIndex& index) updateMaterial(uuid); } } + +void MaterialTreeWidget::onFilter(const QString& text) +{ + setActiveFilter(text); +} + +void MaterialTreeWidget::saveWidgetSettings() +{ + auto param = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/TreeWidget"); + param->SetBool("WidgetExpanded", m_expanded); +} + +void MaterialTreeWidget::saveMaterialTree() +{ + auto param = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/TreeWidget/MaterialTree"); + param->Clear(); + + auto tree = m_materialTree; + auto model = dynamic_cast(tree->model()); + + auto root = model->invisibleRootItem(); + for (int i = 0; i < root->rowCount(); i++) { + auto child = root->child(i); + saveMaterialTreeChildren(param, tree, model, child); + } +} + +void MaterialTreeWidget::saveMaterialTreeChildren(const Base::Reference& param, + QTreeView* tree, + QStandardItemModel* model, + QStandardItem* item) +{ + if (item->hasChildren()) { + param->SetBool(item->text().toStdString().c_str(), tree->isExpanded(item->index())); + + auto treeParam = param->GetGroup(item->text().toStdString().c_str()); + for (int i = 0; i < item->rowCount(); i++) { + auto child = item->child(i); + + saveMaterialTreeChildren(treeParam, tree, model, child); + } + } +} diff --git a/src/Mod/Material/Gui/MaterialTreeWidget.h b/src/Mod/Material/Gui/MaterialTreeWidget.h index cfcd508d56..af7a1be11b 100644 --- a/src/Mod/Material/Gui/MaterialTreeWidget.h +++ b/src/Mod/Material/Gui/MaterialTreeWidget.h @@ -41,6 +41,7 @@ #include +#include #include #include @@ -48,6 +49,7 @@ namespace MatGui { class CommandManager; class WidgetFactoryInst; +class MaterialTreeWidgetPy; /** The Material Tree widget class * This widget is intended for use wherever materials are used. It is a light weight @@ -65,36 +67,21 @@ class WidgetFactoryInst; * * \author David Carter */ -class MatGuiExport MaterialTreeWidget: public QWidget +class MatGuiExport MaterialTreeWidget: public QWidget, public Base::BaseClass { Q_OBJECT + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + public: - explicit MaterialTreeWidget(std::shared_ptr filter, + explicit MaterialTreeWidget(const std::shared_ptr& filter, QWidget* parent = nullptr); + explicit MaterialTreeWidget( + const std::shared_ptr>>& filterList, + QWidget* parent = nullptr); explicit MaterialTreeWidget(QWidget* parent = nullptr); ~MaterialTreeWidget() override; - // void setEntryName( const QByteArray& name ); - // QByteArray entryName() const; - // /** Does the same as setEntryName(). - // * This function is added for convenience because the ui compiler - // * will use this function if the attribute stdset isn't set to 0 in a .ui file. - // */ - // void setPrefEntry(const QByteArray& name); - - // void setParamGrpPath( const QByteArray& path ); - // QByteArray paramGrpPath() const; - // /** Does the same as setParamGrpPath(). - // * This function is added for convenience because the ui compiler - // * will use this function if the attribute stdset isn't set to 0 in a .ui file. - // */ - // void setPrefPath(const QByteArray& name); - - // void OnChange(Base::Subject &rCaller, const char * sReason) override; - // void onSave(); - // void onRestore(); - /** Set the material by specifying its UUID */ void setMaterial(const QString& uuid); @@ -103,17 +90,84 @@ public: QString getMaterialUUID() const; /** Set the material filter */ - void setFilter(std::shared_ptr filter); + void setFilter(const std::shared_ptr& filter); + void setFilter( + const std::shared_ptr>>& filterList); + void setActiveFilter(const QString &name); + + void setExpanded(bool open); + bool getExpanded() + { + return m_expanded; + } + + /* Indicates if we should show favourite materials + */ + bool includeFavorites() const + { + return _filterOptions.includeFavorites(); + } + void setIncludeFavorites(bool value) + { + _filterOptions.setIncludeFavorites(value); + } + + /* Indicates if we should show recent materials + */ + bool includeRecent() const + { + return _filterOptions.includeRecent(); + } + void setIncludeRecent(bool value) + { + _filterOptions.setIncludeRecent(value); + } + + /* Indicates if we should include empty folders + */ + bool includeEmptyFolders() const + { + return _filterOptions.includeEmptyFolders(); + } + void setIncludeEmptyFolders(bool value) + { + _filterOptions.setIncludeEmptyFolders(value); + } + + /* Indicates if we should include empty libraries + */ + bool includeEmptyLibraries() const + { + return _filterOptions.includeEmptyLibraries(); + } + void setIncludeEmptyLibraries(bool value) + { + Base::Console().Log("setIncludeEmptyLibraries(%s)\n", (value ? "true" : "false")); + _filterOptions.setIncludeEmptyLibraries(value); + } + + /* Indicates if we should include materials in the older format + */ + bool includeLegacy() const + { + return _filterOptions.includeLegacy(); + } + void setIncludeLegacy(bool legacy) + { + _filterOptions.setIncludeLegacy(legacy); + } Q_SIGNALS: /** Emits this signal when a material has been selected */ void materialSelected(const std::shared_ptr& material); + void onMaterial(const QString& uuid); private Q_SLOTS: void expandClicked(bool checked); void editorClicked(bool checked); void onSelectMaterial(const QItemSelection& selected, const QItemSelection& deselected); void onDoubleClick(const QModelIndex& index); + void onFilter(const QString& text); private: void setup(); @@ -122,6 +176,7 @@ private: QPushButton* m_expand; QTreeView* m_materialTree; QPushButton* m_editor; + QComboBox* m_filterCombo; bool m_expanded; QString m_materialDisplay; @@ -130,7 +185,10 @@ private: std::list _favorites; std::list _recents; std::shared_ptr _filter; + Materials::MaterialFilterTreeWidgetOptions _filterOptions; + std::shared_ptr>> _filterList; int _recentMax; + MaterialTreeWidgetPy* pyTreeWidget {nullptr}; Materials::MaterialManager _materialManager; @@ -146,7 +204,17 @@ protected: } void getFavorites(); + void getRecents(); + void saveRecents(); + void addRecent(const QString& uuid); + bool isRecent(const QString& uuid) const; + void saveWidgetSettings(); + void saveMaterialTreeChildren(const Base::Reference& param, + QTreeView* tree, + QStandardItemModel* model, + QStandardItem* item); + void saveMaterialTree(); /** Create the widgets UI objects */ @@ -159,7 +227,13 @@ protected: void fillMaterialTree(); void updateMaterialTree(); void addExpanded(QStandardItem* parent, QStandardItem* child); + void addExpanded(QStandardItem* parent, + QStandardItem* child, + const Base::Reference& param); void addExpanded(QStandardItemModel* model, QStandardItem* child); + void addExpanded(QStandardItemModel* model, + QStandardItem* child, + const Base::Reference& param); void addRecents(QStandardItem* parent); void addFavorites(QStandardItem* parent); void addMaterials( @@ -167,9 +241,13 @@ protected: const std::shared_ptr>>& modelTree, const QIcon& folderIcon, - const QIcon& icon); - - void openWidgetState(bool open); + const QIcon& icon, + const Base::Reference& param); + void setFilterVisible(bool open); + void fillFilterCombo(); + bool hasMultipleFilters() const { + return (_filterList && _filterList->size() > 1); + } }; } // namespace MatGui diff --git a/src/Mod/Material/Gui/MaterialTreeWidgetPy.xml b/src/Mod/Material/Gui/MaterialTreeWidgetPy.xml new file mode 100644 index 0000000000..182aa1eb0e --- /dev/null +++ b/src/Mod/Material/Gui/MaterialTreeWidgetPy.xml @@ -0,0 +1,71 @@ + + + + + + Material tree widget. + + + + Material UUID. + + + + + + Expand material tree. + + + + + + Include favorites in the material list. + + + + + + Include recently used materials in the material list. + + + + + + Include empty folders in the material list. + + + + + + Include empty libraries in the material list. + + + + + + Include legacy materials in the material list. + + + + + + Set the material filter or list of filters. + + + + + Set the current material filter. + + + + diff --git a/src/Mod/Material/Gui/MaterialTreeWidgetPyImpl.cpp b/src/Mod/Material/Gui/MaterialTreeWidgetPyImpl.cpp new file mode 100644 index 0000000000..c107880518 --- /dev/null +++ b/src/Mod/Material/Gui/MaterialTreeWidgetPyImpl.cpp @@ -0,0 +1,238 @@ +/*************************************************************************** + * Copyright (c) 2023 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" + +#include + +#include "MaterialTreeWidget.h" +#include "MaterialTreeWidgetPy.h" + +#include "MaterialTreeWidgetPy.cpp" + +using namespace MatGui; + +// returns a string which represents the object e.g. when printed in python +std::string MaterialTreeWidgetPy::representation() const +{ + std::ostringstream str; + str << ""; + return str.str(); +} + +PyObject* MaterialTreeWidgetPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // never create such objects with the constructor + return new MaterialTreeWidgetPy(new MaterialTreeWidget()); +} + +// constructor method +int MaterialTreeWidgetPy::PyInit(PyObject* args, PyObject* /*kwd*/) +{ + PyObject* obj {}; + if (PyArg_ParseTuple(args, "")) { + return 0; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "O!", &(MatGui::MaterialTreeWidgetPy::Type), &obj)) { + auto widget = static_cast(obj)->getMaterialTreeWidgetPtr(); + _pcTwinPointer = widget; + return 0; + } + + // PyErr_Clear(); + // if (PyArg_ParseTuple(args, "O!", &(QWidget::Type), &obj)) { + // auto widget = static_cast(obj); + // _pcTwinPointer = widget; + // return 0; + // } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "O", &obj)) { + if (QLatin1String(obj->ob_type->tp_name) == QLatin1String("PySide2.QtWidgets.QWidget")) { + Gui::PythonWrapper wrap; + wrap.loadWidgetsModule(); + auto qObject = wrap.toQObject(Py::Object(obj)); + auto widget = static_cast(qObject); + _pcTwinPointer = widget; + return 0; + } + else { + PyErr_Format(PyExc_TypeError, + "empty parameter list, or MaterialTreeWidget expected not '%s'", + obj->ob_type->tp_name); + return -1; + } + } + + PyErr_SetString(PyExc_TypeError, "empty parameter list, or MaterialTreeWidget expected"); + // PyErr_Format(PyExc_TypeError, + // "empty parameter list, or MaterialTreeWidget expected not '%s'", + // obj->ob_type->tp_name); + return -1; +} + +Py::String MaterialTreeWidgetPy::getUUID() const +{ + return Py::String(getMaterialTreeWidgetPtr()->getMaterialUUID().toStdString()); +} + +void MaterialTreeWidgetPy::setUUID(const Py::String value) +{ + getMaterialTreeWidgetPtr()->setMaterial(QString::fromStdString(value)); +} + +Py::Boolean MaterialTreeWidgetPy::getexpanded() const +{ + return {getMaterialTreeWidgetPtr()->getExpanded()}; +} + +void MaterialTreeWidgetPy::setexpanded(const Py::Boolean value) +{ + getMaterialTreeWidgetPtr()->setExpanded(value.isTrue()); +} + +Py::Boolean MaterialTreeWidgetPy::getIncludeFavorites() const +{ + return {getMaterialTreeWidgetPtr()->includeFavorites()}; +} + +void MaterialTreeWidgetPy::setIncludeFavorites(const Py::Boolean value) +{ + getMaterialTreeWidgetPtr()->setIncludeFavorites(value.isTrue()); +} + +Py::Boolean MaterialTreeWidgetPy::getIncludeRecent() const +{ + return {getMaterialTreeWidgetPtr()->includeRecent()}; +} + +void MaterialTreeWidgetPy::setIncludeRecent(const Py::Boolean value) +{ + getMaterialTreeWidgetPtr()->setIncludeRecent(value.isTrue()); +} + +Py::Boolean MaterialTreeWidgetPy::getIncludeEmptyFolders() const +{ + return {getMaterialTreeWidgetPtr()->includeEmptyFolders()}; +} + +void MaterialTreeWidgetPy::setIncludeEmptyFolders(const Py::Boolean value) +{ + getMaterialTreeWidgetPtr()->setIncludeEmptyFolders(value.isTrue()); +} + +Py::Boolean MaterialTreeWidgetPy::getIncludeEmptyLibraries() const +{ + return {getMaterialTreeWidgetPtr()->includeEmptyLibraries()}; +} + +void MaterialTreeWidgetPy::setIncludeEmptyLibraries(const Py::Boolean value) +{ + getMaterialTreeWidgetPtr()->setIncludeEmptyLibraries(value.isTrue()); +} + +Py::Boolean MaterialTreeWidgetPy::getIncludeLegacy() const +{ + return {getMaterialTreeWidgetPtr()->includeLegacy()}; +} + +void MaterialTreeWidgetPy::setIncludeLegacy(const Py::Boolean value) +{ + getMaterialTreeWidgetPtr()->setIncludeLegacy(value.isTrue()); +} + +PyObject* MaterialTreeWidgetPy::setFilter(PyObject* args) +{ + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)) { + return nullptr; + } + if (PyObject_TypeCheck(obj, &(Materials::MaterialFilterPy::Type))) { + auto filter = static_cast(obj)->getMaterialFilterPtr(); + Base::Console().Log("Filter '%s'\n", filter->name().toStdString().c_str()); + auto filterPtr = std::make_shared(*filter); + getMaterialTreeWidgetPtr()->setFilter(filterPtr); + } + else if (PyList_Check(obj)) { + // The argument is a list of filters + Base::Console().Log("Filter List\n"); + Py_ssize_t n = PyList_Size(obj); + Base::Console().Log("n = %d\n", n); + if (n < 0) { + Py_Return; + } + PyObject* item; + auto filterList = std::make_shared>>(); + for (int i = 0; i < n; i++) { + item = PyList_GetItem(obj, i); + if (PyObject_TypeCheck(item, &(Materials::MaterialFilterPy::Type))) { + auto filter = + static_cast(item)->getMaterialFilterPtr(); + Base::Console().Log("\tFilter '%s'\n", + filter->name().toStdString().c_str()); auto filterPtr = + std::make_shared(*filter); + filterList->push_back(filterPtr); + // getMaterialTreeWidgetPtr()->setFilter( + // + // *static_cast(obj)->getMaterialFilterPtr()); + } + else { + PyErr_Format(PyExc_TypeError, + "List entry must be of type 'MaterialFilter' not '%s'", + obj->ob_type->tp_name); + return nullptr; + } + } + getMaterialTreeWidgetPtr()->setFilter(filterList); + } + else { + PyErr_Format(PyExc_TypeError, + "Type must be 'MaterialFilter' or list of 'MaterialFilter' not '%s'", + obj->ob_type->tp_name); + return nullptr; + } + + Py_Return; +} + +PyObject* MaterialTreeWidgetPy::selectFilter(PyObject* args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) { + return nullptr; + } + + Base::Console().Log("selectFilter(%s)\n", name); + + Py_Return; +} + +PyObject* MaterialTreeWidgetPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MaterialTreeWidgetPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +}