Skip to content

Commit

Permalink
#6021: Refactor ShaderSelector to use the DeclarationTreeView type, r…
Browse files Browse the repository at this point in the history
…emoving lots of redundant code.
  • Loading branch information
codereader committed Jul 29, 2022
1 parent 3d6781f commit 09c6210
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 132 deletions.
214 changes: 90 additions & 124 deletions radiant/ui/common/ShaderSelector.cpp
Expand Up @@ -2,16 +2,12 @@

#include "i18n.h"

#include "wxutil/dataview/TreeModel.h"
#include "wxutil/dataview/TreeView.h"
#include "wxutil/dataview/VFSTreePopulator.h"
#include "wxutil/menu/IconTextMenuItem.h"
#include "wxutil/DeclarationSourceView.h"

#include "texturelib.h"
#include "string/string.h"
#include "ishaders.h"
#include "iregistry.h"

#include <wx/dataview.h>
#include <wx/sizer.h>
Expand All @@ -21,13 +17,15 @@

#include <vector>
#include <string>
#include <map>

#include "string/split.h"
#include "string/predicate.h"
#include "string/case_conv.h"
#include <functional>

#include "ifavourites.h"
#include "wxutil/dataview/ThreadedResourceTreePopulator.h"
#include "wxutil/dataview/TreeViewItemStyle.h"

namespace ui
{

Expand All @@ -37,21 +35,93 @@ namespace
{
const char* const FOLDER_ICON = "folder16.png";
const char* const TEXTURE_ICON = "icon_texture.png";

constexpr const char* const SHOW_MATERIAL_DEF_TEXT = N_("Show Material Definition");
constexpr const char* const SHOW_MATERIAL_DEF_ICON = "icon_script.png";
}

/**
* Visitor class to retrieve material names and add them to folders.
*/
class ThreadedMaterialLoader final :
public wxutil::ThreadedResourceTreePopulator
{
private:
const wxutil::DeclarationTreeView::Columns& _columns;
const ShaderSelector::PrefixList& _prefixes;

std::set<std::string> _favourites;

wxIcon _folderIcon;
wxIcon _textureIcon;

public:
ThreadedMaterialLoader(const wxutil::DeclarationTreeView::Columns& columns, const ShaderSelector::PrefixList& prefixes) :
ThreadedResourceTreePopulator(columns),
_columns(columns),
_prefixes(prefixes)
{
// Get the list of favourites
_favourites = GlobalFavouritesManager().getFavourites(decl::getTypeName(decl::Type::Skin));

_textureIcon.CopyFromBitmap(wxutil::GetLocalBitmap(TEXTURE_ICON));
_folderIcon.CopyFromBitmap(wxutil::GetLocalBitmap(FOLDER_ICON));
}

~ThreadedMaterialLoader()
{
EnsureStopped();
}

protected:
void PopulateModel(const wxutil::TreeModel::Ptr& model) override
{
wxutil::VFSTreePopulator populator(model);

GlobalMaterialManager().foreachShaderName([&](const std::string& materialName)
{
for (const std::string& prefix : _prefixes)
{
if (string::istarts_with(materialName, prefix + "/"))
{
populator.addPath(materialName, [&](wxutil::TreeModel::Row& row,
const std::string& path, const std::string& leafName, bool isFolder)
{
StoreMaterialValues(row, path, leafName, isFolder);
});
break; // don't consider any further prefixes
}
}
});
}

void SortModel(const wxutil::TreeModel::Ptr& model) override
{
model->SortModelFoldersFirst(_columns.leafName, _columns.isFolder);
}

private:
void StoreMaterialValues(wxutil::TreeModel::Row& row, const std::string& materialName, const std::string& leafName, bool isFolder)
{
bool isFavourite = _favourites.count(materialName) > 0;

row[_columns.iconAndName] = wxVariant(wxDataViewIconText(leafName, !isFolder ? _textureIcon : _folderIcon));
row[_columns.iconAndName] = wxutil::TreeViewItemStyle::Declaration(isFavourite);
row[_columns.fullName] = materialName;
row[_columns.leafName] = leafName;
row[_columns.declName] = materialName;
row[_columns.isFolder] = isFolder;
row[_columns.isFavourite] = isFavourite;

row.SendItemAdded();
}
};

// Constructor creates elements
ShaderSelector::ShaderSelector(wxWindow* parent, Client* client, const std::string& prefixes, bool isLightTexture) :
wxPanel(parent, wxID_ANY),
_treeView(NULL),
_treeStore(NULL),
_glWidget(NULL),
_treeView(nullptr),
_glWidget(nullptr),
_client(client),
_isLightTexture(isLightTexture),
_infoStore(new wxutil::TreeModel(_infoStoreColumns, true)), // is a listmodel
_contextMenu(new wxutil::PopupMenu)
_infoStore(new wxutil::TreeModel(_infoStoreColumns, true)) // is a listmodel
{
SetSizer(new wxBoxSizer(wxVERTICAL));

Expand All @@ -61,13 +131,6 @@ ShaderSelector::ShaderSelector(wxWindow* parent, Client* client, const std::stri
// Pack in TreeView and info panel
createTreeView();
createPreview();

// Construct the context menu
_contextMenu->addItem(
new wxutil::IconTextMenuItem(_(SHOW_MATERIAL_DEF_TEXT), SHOW_MATERIAL_DEF_ICON),
std::bind(&ShaderSelector::_onShowShaderDefinition, this),
[this]() { return !getSelection().empty(); }
);
}

void ShaderSelector::_onShowShaderDefinition()
Expand All @@ -83,130 +146,33 @@ void ShaderSelector::_onShowShaderDefinition()
// Return the selection to the calling code
std::string ShaderSelector::getSelection()
{
wxDataViewItem item = _treeView->GetSelection();

if (!item.IsOk()) return "";

wxutil::TreeModel::Row row(item, *_treeView->GetModel());

return row[_shaderTreeColumns.shaderName];
return _treeView->GetSelectedDeclName();
}

// Set the selection in the treeview
void ShaderSelector::setSelection(const std::string& sel)
{
// If the selection string is empty, collapse the treeview and return with
// no selection
if (sel.empty())
{
_treeView->UnselectAll();
_treeView->Collapse(_treeStore->GetRoot());
return;
}

// Use the wxutil TreeModel algorithms to select the shader
wxDataViewItem found = _treeStore->FindString(
string::to_lower_copy(sel), _shaderTreeColumns.shaderName);

if (found.IsOk())
{
_treeView->Select(found);
_treeView->EnsureVisible(found);
}
_treeView->SetSelectedDeclName(sel);
}

namespace
{
// VFSPopulatorVisitor to fill in column data for the populator tree nodes
class DataInserter :
public wxutil::VFSTreePopulator::Visitor
{
private:
const ShaderSelector::ShaderTreeColumns& _columns;

wxIcon _folderIcon;
wxIcon _textureIcon;

public:
DataInserter(const ShaderSelector::ShaderTreeColumns& columns) :
_columns(columns)
{
_folderIcon.CopyFromBitmap(
wxutil::GetLocalBitmap(FOLDER_ICON));
_textureIcon.CopyFromBitmap(
wxutil::GetLocalBitmap(TEXTURE_ICON));
}

virtual ~DataInserter() {}

// Required visit function
void visit(wxutil::TreeModel& /* store */,
wxutil::TreeModel::Row& row,
const std::string& path,
bool isExplicit)
{
// Get the display name by stripping off everything before the last
// slash
std::string displayName = path.substr(path.rfind("/") + 1);

// Pathname is the model VFS name for a model, and blank for a folder
std::string fullPath = isExplicit ? path : "";

// Fill in the column values
row[_columns.iconAndName] = wxVariant(
wxDataViewIconText(displayName, isExplicit ? _textureIcon : _folderIcon));
row[_columns.shaderName] = fullPath;
}
};
}

// Create the Tree View
void ShaderSelector::createTreeView()
{
_treeStore = new wxutil::TreeModel(_shaderTreeColumns);

// Instantiate the helper class that populates the tree according to the paths
wxutil::VFSTreePopulator populator(_treeStore);

// Iterate over material names and pass matching shaders to the populator
GlobalMaterialManager().foreachShaderName(
[&](const std::string& shaderName)
{
for (const std::string& prefix : _prefixes)
{
if (!shaderName.empty() && string::istarts_with(shaderName, prefix + "/"))
{
populator.addPath(string::to_lower_copy(shaderName));
break; // don't consider any further prefixes
}
}
}
);

// Now visit the created iterators to load the actual data into the tree
DataInserter inserter(_shaderTreeColumns);
populator.forEachNode(inserter);

// Tree view
_treeView = wxutil::TreeView::CreateWithModel(this, _treeStore.get(), wxDV_NO_HEADER | wxDV_SINGLE);
_treeView = new wxutil::DeclarationTreeView(this, decl::Type::Material,
_shaderTreeColumns, wxDV_NO_HEADER | wxDV_SINGLE);

// Single visible column, containing the directory/shader name and the icon
_treeView->AppendIconTextColumn(_("Value"), _shaderTreeColumns.iconAndName.getColumnIndex(),
wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE);

// Use the TreeModel's full string search function
_treeView->AddSearchColumn(_shaderTreeColumns.iconAndName);
_treeView->AddSearchColumn(_shaderTreeColumns.leafName);

// Get selection and connect the changed callback
_treeView->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &ShaderSelector::_onSelChange, this);
_treeView->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &ShaderSelector::_onContextMenu, this);

GetSizer()->Add(_treeView, 1, wxEXPAND);
}

void ShaderSelector::_onContextMenu(wxDataViewEvent& ev)
{
_contextMenu->show(_treeView);
_treeView->Populate(std::make_shared<ThreadedMaterialLoader>(_shaderTreeColumns, _prefixes));
}

// Create the preview panel (GL widget and info table)
Expand Down
11 changes: 3 additions & 8 deletions radiant/ui/common/ShaderSelector.h
Expand Up @@ -5,6 +5,7 @@
#include <memory>
#include "wxutil/GLWidget.h"
#include "wxutil/dataview/TreeView.h"
#include "wxutil/dataview/DeclarationTreeView.h"
#include "wxutil/menu/PopupMenu.h"

#include <wx/panel.h>
Expand Down Expand Up @@ -69,11 +70,8 @@ class ShaderSelector :
};

private:
ShaderTreeColumns _shaderTreeColumns;

// Tree view and selection object
wxutil::TreeView* _treeView;
wxutil::TreeModel::Ptr _treeStore;
wxutil::DeclarationTreeView::Columns _shaderTreeColumns;
wxutil::DeclarationTreeView* _treeView;

// GL preview widget
wxutil::GLWidget* _glWidget;
Expand Down Expand Up @@ -101,8 +99,6 @@ class ShaderSelector :
// List store for info table
wxutil::TreeModel::Ptr _infoStore;

wxutil::PopupMenuPtr _contextMenu;

public:
// This is where the prefixes are stored (needed to filter the possible shaders)
typedef std::vector<std::string> PrefixList;
Expand Down Expand Up @@ -161,7 +157,6 @@ class ShaderSelector :
// callbacks
bool onPreviewRender();
void _onSelChange(wxDataViewEvent& ev);
void _onContextMenu(wxDataViewEvent& ev);

void _onShowShaderDefinition();
};
Expand Down

0 comments on commit 09c6210

Please sign in to comment.