From 6c3454b776210752d28b0107db2077daa064adc3 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 24 Jul 2022 20:08:31 +0200 Subject: [PATCH] #6021: Migrate Entity Class Tree to use the DeclarationTreeView widget --- radiant/ui/common/SkinChooser.cpp | 2 + radiant/ui/eclasstree/EClassTree.cpp | 96 +++++---------- radiant/ui/eclasstree/EClassTree.h | 30 +---- radiant/ui/eclasstree/EClassTreeBuilder.cpp | 130 +++++++------------- radiant/ui/eclasstree/EClassTreeBuilder.h | 55 ++++----- 5 files changed, 105 insertions(+), 208 deletions(-) diff --git a/radiant/ui/common/SkinChooser.cpp b/radiant/ui/common/SkinChooser.cpp index 19ae7b4bba..de888384a0 100644 --- a/radiant/ui/common/SkinChooser.cpp +++ b/radiant/ui/common/SkinChooser.cpp @@ -129,6 +129,8 @@ namespace row[_columns.declName] = fullSkinName; row[_columns.isFolder] = isFolder; row[_columns.isFavourite] = isFavourite; + + row.SendItemAdded(); } }; } diff --git a/radiant/ui/eclasstree/EClassTree.cpp b/radiant/ui/eclasstree/EClassTree.cpp index 11337f31d3..0f3be84059 100644 --- a/radiant/ui/eclasstree/EClassTree.cpp +++ b/radiant/ui/eclasstree/EClassTree.cpp @@ -2,13 +2,10 @@ #include "ieclass.h" #include "ientity.h" -#include "ui/imainframe.h" #include "iselection.h" - -#include "EClassTreeBuilder.h" #include "i18n.h" -#include +#include "EClassTreeBuilder.h" #include #include @@ -19,69 +16,41 @@ namespace ui namespace { - const char* const ECLASSTREE_TITLE = N_("Entity Class Tree"); + constexpr const char* const ECLASSTREE_TITLE = N_("Entity Class Tree"); } EClassTree::EClassTree() : DialogBase(_(ECLASSTREE_TITLE)), - _eclassStore(NULL), - _eclassView(NULL), - _propertyStore(NULL), - _propertyView(NULL) + _eclassView(nullptr), + _propertyStore(nullptr), + _propertyView(nullptr) { - // Create a new tree store for the entityclasses - _eclassStore = new wxutil::TreeModel(_eclassColumns); - // Construct the window's widgets populateWindow(); - wxutil::TreeModel::Row row = _eclassStore->AddItem(); - - wxIcon icon; - icon.CopyFromBitmap(wxutil::GetLocalBitmap("cmenu_add_entity.png")); - row[_eclassColumns.name] = wxVariant(wxDataViewIconText(_("Loading, please wait..."), icon)); - - row.SendItemAdded(); - - // Construct an eclass visitor and traverse the entity classes - _treeBuilder.reset(new EClassTreeBuilder(_eclassColumns, this)); - - Connect(wxutil::EV_TREEMODEL_POPULATION_FINISHED, - TreeModelPopulationFinishedHandler(EClassTree::onTreeStorePopulationFinished), NULL, this); - - _treeBuilder->populate(); + _eclassView->Populate(std::make_shared(_eclassColumns)); } -void EClassTree::onTreeStorePopulationFinished(wxutil::TreeModel::PopulationFinishedEvent& ev) +void EClassTree::onTreeViewPopulationFinished(wxutil::ResourceTreeView::PopulationFinishedEvent& ev) { - _eclassStore = ev.GetTreeModel(); - wxDataViewItem preselectItem; + std::string className; // Do we have anything selected if (GlobalSelectionSystem().countSelected() > 0) { // Get the last selected node and check if it's an entity - scene::INodePtr lastSelected = GlobalSelectionSystem().ultimateSelected(); + auto lastSelected = GlobalSelectionSystem().ultimateSelected(); - Entity* entity = Node_getEntity(lastSelected); - - if (entity != NULL) + if (const auto* entity = Node_getEntity(lastSelected); entity) { // There is an entity selected, extract the classname - std::string classname = entity->getKeyValue("classname"); - - // Find and select the classname - preselectItem = _eclassStore->FindString(classname, _eclassColumns.name); + className = entity->getKeyValue("classname"); } } - _eclassView->AssociateModel(_eclassStore.get()); - - if (preselectItem.IsOk()) + if (!className.empty()) { - _eclassView->Select(preselectItem); - _eclassView->EnsureVisible(preselectItem); - handleSelectionChange(); + _eclassView->SetSelectedFullname(className); } else { @@ -95,8 +64,7 @@ void EClassTree::populateWindow() // Create the overall vbox SetSizer(new wxBoxSizer(wxVERTICAL)); - wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, - wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE); + auto* splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE); splitter->SetMinimumPaneSize(10); // disallow unsplitting createEClassTreeView(splitter); @@ -118,17 +86,18 @@ void EClassTree::populateWindow() void EClassTree::createEClassTreeView(wxWindow* parent) { - _eclassView = wxutil::TreeView::CreateWithModel(parent, _eclassStore.get()); + _eclassView = new wxutil::DeclarationTreeView(parent, decl::Type::EntityDef, _eclassColumns); // Use the TreeModel's full string search function - _eclassView->AddSearchColumn(_eclassColumns.name); + _eclassView->AddSearchColumn(_eclassColumns.leafName); + _eclassView->EnableSetFavouritesRecursively(false); // Tree selection - _eclassView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, - wxDataViewEventHandler(EClassTree::onSelectionChanged), NULL, this); + _eclassView->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &EClassTree::onSelectionChanged, this); + _eclassView->Bind(wxutil::EV_TREEVIEW_POPULATION_FINISHED, &EClassTree::onTreeViewPopulationFinished, this); // Single column with icon and name - _eclassView->AppendIconTextColumn(_("Classname"), _eclassColumns.name.getColumnIndex(), + _eclassView->AppendIconTextColumn(_("Classname"), _eclassColumns.iconAndName.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE); } @@ -172,22 +141,21 @@ void EClassTree::updatePropertyView(const std::string& eclassName) // Clear the existing list _propertyStore->Clear(); - IEntityClassPtr eclass = GlobalEntityClassManager().findClass(eclassName); - if (!eclass) - return; + auto eclass = GlobalEntityClassManager().findClass(eclassName); + + if (!eclass) return; - eclass->forEachAttribute( - [&](const EntityClassAttribute& a, bool inherited) { - addToListStore(a, inherited); - }, - true); + eclass->forEachAttribute([&](const EntityClassAttribute& attr, bool inherited) + { + addToListStore(attr, inherited); + }, true); } // Static command target void EClassTree::ShowDialog(const cmd::ArgumentList& args) { // Construct a new instance, this enters the main loop - EClassTree* tree = new EClassTree; + auto* tree = new EClassTree(); tree->ShowModal(); tree->Destroy(); @@ -196,7 +164,7 @@ void EClassTree::ShowDialog(const cmd::ArgumentList& args) void EClassTree::handleSelectionChange() { // Prepare to check for a selection - wxDataViewItem item = _eclassView->GetSelection(); + auto item = _eclassView->GetSelection(); // Add button is enabled if there is a selection and it is not a folder. if (item.IsOk()) @@ -204,11 +172,9 @@ void EClassTree::handleSelectionChange() _propertyView->Enable(true); // Set the panel text with the usage information - wxutil::TreeModel::Row row(item, *_eclassStore); - - wxDataViewIconText value = row[_eclassColumns.name]; + wxutil::TreeModel::Row row(item, *_eclassView->GetTreeModel()); - updatePropertyView(value.GetText().ToStdString()); + updatePropertyView(row[_eclassColumns.declName].getString().ToStdString()); } else { diff --git a/radiant/ui/eclasstree/EClassTree.h b/radiant/ui/eclasstree/EClassTree.h index dec0a92d87..03d2da2a43 100644 --- a/radiant/ui/eclasstree/EClassTree.h +++ b/radiant/ui/eclasstree/EClassTree.h @@ -1,41 +1,23 @@ #pragma once -#include "iradiant.h" #include "icommandsystem.h" + #include "wxutil/dialog/DialogBase.h" -#include #include "wxutil/dataview/TreeView.h" -#include +#include "wxutil/dataview/DeclarationTreeView.h" class EntityClassAttribute; namespace ui { -class EClassTreeBuilder; - -class EClassTree; -typedef std::shared_ptr EClassTreePtr; - -struct EClassTreeColumns : - public wxutil::TreeModel::ColumnRecord -{ - EClassTreeColumns() : - name(add(wxutil::TreeModel::Column::IconText)) - {} - - wxutil::TreeModel::Column name; // name -}; - class EClassTree : public wxutil::DialogBase { private: // The EClass treeview widget and underlying liststore - EClassTreeColumns _eclassColumns; - wxutil::TreeModel::Ptr _eclassStore; - - wxutil::TreeView* _eclassView; + wxutil::DeclarationTreeView::Columns _eclassColumns; + wxutil::DeclarationTreeView* _eclassView; struct PropertyListColumns : public wxutil::TreeModel::ColumnRecord @@ -56,8 +38,6 @@ class EClassTree : wxutil::TreeModel::Ptr _propertyStore; wxutil::TreeView* _propertyView; - std::unique_ptr _treeBuilder; - // Private constructor, traverses the entity classes EClassTree(); @@ -80,7 +60,7 @@ class EClassTree : // callbacks void onSelectionChanged(wxDataViewEvent& ev); - void onTreeStorePopulationFinished(wxutil::TreeModel::PopulationFinishedEvent& ev); + void onTreeViewPopulationFinished(wxutil::ResourceTreeView::PopulationFinishedEvent& ev); }; } // namespace ui diff --git a/radiant/ui/eclasstree/EClassTreeBuilder.cpp b/radiant/ui/eclasstree/EClassTreeBuilder.cpp index dd23429e5b..b5c1e865b9 100644 --- a/radiant/ui/eclasstree/EClassTreeBuilder.cpp +++ b/radiant/ui/eclasstree/EClassTreeBuilder.cpp @@ -1,132 +1,92 @@ #include "EClassTreeBuilder.h" -#include "itextstream.h" #include "EClassTree.h" -#include "debugging/ScopedDebugTimer.h" -#include "wxutil/dataview/TreeModel.h" +#include "ifavourites.h" #include "wxutil/Bitmap.h" +#include "wxutil/dataview/VFSTreePopulator.h" +#include "wxutil/dataview/TreeModel.h" +#include "wxutil/dataview/TreeViewItemStyle.h" + namespace ui { namespace { - const char* ENTITY_ICON = "cmenu_add_entity.png"; - const std::string INHERIT_KEY("inherit"); + constexpr const char* ENTITY_ICON = "cmenu_add_entity.png"; } -EClassTreeBuilder::EClassTreeBuilder(const EClassTreeColumns& columns, - wxEvtHandler* finishedHandler) : - wxThread(wxTHREAD_JOINABLE), - _columns(columns), - _treeStore(new wxutil::TreeModel(_columns)), - _finishedHandler(finishedHandler), - _treePopulator(_treeStore) +EClassTreeBuilder::EClassTreeBuilder(const wxutil::DeclarationTreeView::Columns& columns) : + ThreadedResourceTreePopulator(columns), + _columns(columns) { - wxBitmap icon = wxutil::GetLocalBitmap(ENTITY_ICON); - _entityIcon.CopyFromBitmap(icon); + // Get the list of favourites + _favourites = GlobalFavouritesManager().getFavourites(decl::getTypeName(decl::Type::EntityDef)); + + _entityIcon.CopyFromBitmap(wxutil::GetLocalBitmap(ENTITY_ICON)); } EClassTreeBuilder::~EClassTreeBuilder() { - // We might have a running thread, wait for it - if (IsRunning()) - { - Delete(); - } + EnsureStopped(); } -wxThread::ExitCode EClassTreeBuilder::Entry() +void EClassTreeBuilder::PopulateModel(const wxutil::TreeModel::Ptr& model) { - ScopedDebugTimer timer("EClassTreeBuilder::run()"); - - // Travese the entity classes, this will call visit() for each eclass - GlobalEntityClassManager().forEachEntityClass(*this); - - if (TestDestroy()) return static_cast(0); - - // Visit the tree populator in order to fill in the column data - _treePopulator.forEachNode(*this); - - if (TestDestroy()) return static_cast(0); + _treePopulator = std::make_unique(model); - // Sort the model before returning it - _treeStore->SortModelByColumn(_columns.name); + ThrowIfCancellationRequested(); - if (!TestDestroy()) - { - // Send the event to our listener, only if we are not forced to finish - wxQueueEvent(_finishedHandler, new wxutil::TreeModel::PopulationFinishedEvent(_treeStore)); - } + GlobalEntityClassManager().forEachEntityClass(*this); - return static_cast(0); + ThrowIfCancellationRequested(); } -void EClassTreeBuilder::populate() +void EClassTreeBuilder::SortModel(const wxutil::TreeModel::Ptr& model) { - if (IsRunning()) return; - - Run(); + model->SortModelByColumn(_columns.leafName); } void EClassTreeBuilder::visit(const IEntityClassPtr& eclass) { - if (TestDestroy()) - { - return; - } - - std::string fullPath; + ThrowIfCancellationRequested(); // Prefix mod name - fullPath = eclass->getModName() + "/"; + std::string fullPath = eclass->getModName() + "/"; // Prefix inheritance path (recursively) - fullPath += getInheritancePathRecursive(eclass); + fullPath += GetInheritancePathRecursively(*eclass); // The entityDef name itself fullPath += eclass->getDeclName(); - // Let the VFSTreePopulator do the insertion - _treePopulator.addPath(fullPath); + // Let the VFSTreePopulator sort this into the tree + _treePopulator->addPath(fullPath, [&](wxutil::TreeModel::Row& row, + const std::string& path, const std::string& leafName, bool isFolder) + { + bool isFavourite = _favourites.count(leafName) > 0; + + row[_columns.iconAndName] = wxVariant(wxDataViewIconText(leafName, _entityIcon)); + row[_columns.iconAndName] = wxutil::TreeViewItemStyle::Declaration(isFavourite); + row[_columns.fullName] = leafName; + row[_columns.leafName] = leafName; + row[_columns.declName] = leafName; + row[_columns.isFolder] = false; + row[_columns.isFavourite] = isFavourite; + + row.SendItemAdded(); + }); } -void EClassTreeBuilder::visit(wxutil::TreeModel& /* store */, wxutil::TreeModel::Row& row, - const std::string& path, bool isExplicit) -{ - if (TestDestroy()) return; - - // Get the display path, everything after rightmost slash - row[_columns.name] = wxVariant(wxDataViewIconText( - path.substr(path.rfind("/") + 1), _entityIcon)); -} - -std::string EClassTreeBuilder::getInheritancePathRecursive(const IEntityClassPtr& eclass) +std::string EClassTreeBuilder::GetInheritancePathRecursively(IEntityClass& eclass) { std::string returnValue; - try { - std::string attribute = eclass->getAttributeValue( - INHERIT_KEY, false /* includeInherited*/ - ); - - // Don't use empty "inherit" keys - if (!attribute.empty()) { - // Get the inherited eclass first and resolve the path - IEntityClassPtr parent = GlobalEntityClassManager().findClass(attribute); - if (parent) { - returnValue += getInheritancePathRecursive(parent); - } else { - rError() << "EClassTreeBuilder: Cannot resolve inheritance path for " - << eclass->getDeclName() << std::endl; - } - - returnValue += attribute + "/"; - } - } - catch (std::runtime_error&) { - // no inherit key + if (auto parent = eclass.getParent(); parent) + { + returnValue += GetInheritancePathRecursively(*parent); + returnValue += parent->getDeclName() + "/"; } return returnValue; diff --git a/radiant/ui/eclasstree/EClassTreeBuilder.h b/radiant/ui/eclasstree/EClassTreeBuilder.h index 0b35e5eb15..d09b983eeb 100644 --- a/radiant/ui/eclasstree/EClassTreeBuilder.h +++ b/radiant/ui/eclasstree/EClassTreeBuilder.h @@ -1,58 +1,47 @@ #pragma once #include "ieclass.h" -#include "wxutil/dataview/VFSTreePopulator.h" + +#include "wxutil/dataview/DeclarationTreeView.h" +#include "wxutil/dataview/ThreadedResourceTreePopulator.h" + #include -#include + +namespace wxutil { class VFSTreePopulator; } namespace ui { -struct EClassTreeColumns; - /** - * greebo: This traverses all the entity classes loaded so far and - * pushes them into the given tree store. + * Visitor class to retrieve entityDef names and sort them into the hierarchy tree. */ -class EClassTreeBuilder : - public EntityClassVisitor, - public wxutil::VFSTreePopulator::Visitor, - public wxThread +class EClassTreeBuilder final : + public wxutil::ThreadedResourceTreePopulator, + public EntityClassVisitor { private: - const EClassTreeColumns& _columns; - - // The tree store to populate - wxutil::TreeModel::Ptr _treeStore; + const wxutil::DeclarationTreeView::Columns& _columns; - // The event handler to notify on completion - wxEvtHandler* _finishedHandler; - - // The helper class, doing the tedious treeview insertion for us. - wxutil::VFSTreePopulator _treePopulator; + std::set _favourites; wxIcon _entityIcon; -public: - EClassTreeBuilder(const EClassTreeColumns& columns, wxEvtHandler* finishedHandler); - - ~EClassTreeBuilder(); // waits for thread to finish + std::unique_ptr _treePopulator; - void populate(); +public: + EClassTreeBuilder(const wxutil::DeclarationTreeView::Columns& columns); - // Visitor implementation - virtual void visit(const IEntityClassPtr& eclass); + ~EClassTreeBuilder(); - void visit(wxutil::TreeModel& store, wxutil::TreeModel::Row& row, - const std::string& path, bool isExplicit); + void visit(const IEntityClassPtr& eclass) override; protected: - // Thread entry point - ExitCode Entry(); + void PopulateModel(const wxutil::TreeModel::Ptr& model) override; + void SortModel(const wxutil::TreeModel::Ptr& model) override; private: - // Returns an inheritance path, like this: "moveables/swords/" - std::string getInheritancePathRecursive(const IEntityClassPtr& eclass); + // Returns an inheritance path, like this: "moveables/swords/" + static std::string GetInheritancePathRecursively(IEntityClass& eclass); }; -} // namespace ui +} // namespace