From 59534ee1878bb2644cfaecf5ee1d4c1695d3d4b1 Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Thu, 12 Sep 2024 23:35:30 -0500 Subject: [PATCH] Memory leak exists in proxy model --- include/gui/project/window.hpp | 1 + include/model/fsmodel.hpp | 10 +- src/gui/project/window.cpp | 25 +++-- src/image/imagehandle.cpp | 1 + src/model/fsmodel.cpp | 191 ++++++++++++++++++++++----------- vcpkg.json | 2 +- 6 files changed, 153 insertions(+), 77 deletions(-) diff --git a/include/gui/project/window.hpp b/include/gui/project/window.hpp index 453f9bac..95fd05a1 100644 --- a/include/gui/project/window.hpp +++ b/include/gui/project/window.hpp @@ -82,6 +82,7 @@ namespace Toolbox::UI { [[nodiscard]] bool onLoadData(const std::filesystem::path& path) override { m_project_root = path; m_file_system_model = make_referable(); + m_file_system_model->initialize(); m_file_system_model->setRoot(m_project_root); m_tree_proxy.setSourceModel(m_file_system_model); m_tree_proxy.setDirsOnly(true); diff --git a/include/model/fsmodel.hpp b/include/model/fsmodel.hpp index a0577a0d..4bca060d 100644 --- a/include/model/fsmodel.hpp +++ b/include/model/fsmodel.hpp @@ -49,6 +49,8 @@ namespace Toolbox { FileSystemModel() = default; ~FileSystemModel() = default; + void initialize(); + [[nodiscard]] UUID64 getUUID() const override { return m_uuid; } [[nodiscard]] const fs_path &getRoot() const &; @@ -138,6 +140,7 @@ namespace Toolbox { UUID64 m_root_index; mutable std::unordered_map m_index_map; + mutable std::unordered_map> m_icon_map; }; class FileSystemModelSortFilterProxy : public IDataModel { @@ -206,13 +209,15 @@ namespace Toolbox { [[nodiscard]] bool canFetchMore(const ModelIndex &index); void fetchMore(const ModelIndex &index); - protected: [[nodiscard]] ModelIndex toSourceIndex(const ModelIndex &index) const; [[nodiscard]] ModelIndex toProxyIndex(const ModelIndex &index) const; + + protected: [[nodiscard]] ModelIndex toProxyIndex(int64_t row, int64_t column, const ModelIndex &parent = ModelIndex()) const; [[nodiscard]] bool isFiltered(const UUID64 &uuid) const; + void cacheIndex(const ModelIndex &index) const; ModelIndex makeIndex(const fs_path &path, int64_t row, const ModelIndex &parent) { return ModelIndex(); @@ -227,6 +232,9 @@ namespace Toolbox { std::string m_filter = ""; bool m_dirs_only = false; + + mutable std::unordered_map m_filter_map; + mutable std::unordered_map> m_row_map; }; } // namespace Toolbox \ No newline at end of file diff --git a/src/gui/project/window.cpp b/src/gui/project/window.cpp index 63bc1b1a..108f3a4a 100644 --- a/src/gui/project/window.cpp +++ b/src/gui/project/window.cpp @@ -1,8 +1,8 @@ #include "gui/project/window.hpp" #include "model/fsmodel.hpp" -#include #include +#include namespace Toolbox::UI { @@ -30,15 +30,15 @@ namespace Toolbox::UI { } void ProjectViewWindow::renderProjectFolderView() { - if (!m_view_proxy.validateIndex(m_view_index)) { + if (!m_file_system_model->validateIndex(m_view_index)) { return; } if (ImGui::BeginChild("Folder View", {0, 0}, true, ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoDecoration)) { - if (m_view_proxy.hasChildren(m_view_index)) { - if (m_view_proxy.canFetchMore(m_view_index)) { - m_view_proxy.fetchMore(m_view_index); + if (m_file_system_model->hasChildren(m_view_index)) { + if (m_file_system_model->canFetchMore(m_view_index)) { + m_file_system_model->fetchMore(m_view_index); } ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {2, 2}); @@ -53,16 +53,16 @@ namespace Toolbox::UI { x_count = 1; } - for (size_t i = 0; i < m_view_proxy.getRowCount(m_view_index); ++i) { - ModelIndex child_index = m_view_proxy.getIndex(i, 0, m_view_index); + for (size_t i = 0; i < m_file_system_model->getRowCount(m_view_index); ++i) { + ModelIndex child_index = m_file_system_model->getIndex(i, 0, m_view_index); if (ImGui::BeginChild(child_index.getUUID(), {76, 92}, true, ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoDecoration)) { - m_icon_painter.render(*m_view_proxy.getDecoration(child_index), {72, 72}); + m_icon_painter.render(*m_file_system_model->getDecoration(child_index), {72, 72}); - std::string text = m_view_proxy.getDisplayText(child_index); + std::string text = m_file_system_model->getDisplayText(child_index); ImVec2 text_size = ImGui::CalcTextSize(text.c_str()); ImVec2 pos = ImGui::GetCursorScreenPos(); @@ -71,7 +71,7 @@ namespace Toolbox::UI { ImGui::RenderTextEllipsis( ImGui::GetWindowDrawList(), pos, pos + ImVec2(64, 20), pos.x + 64.0f, - pos.x + 76.0f, m_view_proxy.getDisplayText(child_index).c_str(), + pos.x + 76.0f, m_file_system_model->getDisplayText(child_index).c_str(), nullptr, nullptr); } ImGui::EndChild(); @@ -113,6 +113,11 @@ namespace Toolbox::UI { } is_open = ImGui::TreeNodeEx(m_tree_proxy.getDisplayText(index).c_str(), ImGuiTreeNodeFlags_OpenOnArrow); + + if (ImGui::IsItemClicked()) { + m_view_index = m_tree_proxy.toSourceIndex(index); + } + if (is_open) { for (size_t i = 0; i < m_tree_proxy.getRowCount(index); ++i) { ModelIndex child_index = m_tree_proxy.getIndex(i, 0, index); diff --git a/src/image/imagehandle.cpp b/src/image/imagehandle.cpp index 487e3041..1a3b1d4a 100644 --- a/src/image/imagehandle.cpp +++ b/src/image/imagehandle.cpp @@ -118,6 +118,7 @@ namespace Toolbox { } void ImageHandle::moveGL(ImageHandle &&image) { + unloadGL(); m_image_handle = image.m_image_handle; m_image_format = image.m_image_format; m_image_width = image.m_image_width; diff --git a/src/model/fsmodel.cpp b/src/model/fsmodel.cpp index a83bc593..ece9ba7e 100644 --- a/src/model/fsmodel.cpp +++ b/src/model/fsmodel.cpp @@ -110,6 +110,22 @@ namespace Toolbox { } } + void FileSystemModel::initialize() { + m_icon_map.clear(); + m_index_map.clear(); + m_root_path = fs_path(); + m_root_index = 0; + m_options = FileSystemModelOptions(); + m_read_only = false; + + fs_path fs_icon_path = + GUIApplication::instance().getResourcePath("Images/Icons/Filesystem/"); + + for (auto &[key, value] : TypeMap()) { + m_icon_map[key] = make_referable(fs_icon_path / value.m_image_name); + } + } + const fs_path &FileSystemModel::getRoot() const & { return m_root_path; } void FileSystemModel::setRoot(const fs_path &path) { @@ -384,8 +400,9 @@ namespace Toolbox { parent_data->m_children.end(), index.getUUID()), parent_data->m_children.end()); - m_index_map.erase(index.getUUID()); } + delete index.data<_FileSystemIndexData>(); + m_index_map.erase(index.getUUID()); } return result; @@ -428,8 +445,9 @@ namespace Toolbox { parent_data->m_children.end(), index.getUUID()), parent_data->m_children.end()); - m_index_map.erase(index.getUUID()); } + delete index.data<_FileSystemIndexData>(); + m_index_map.erase(index.getUUID()); } return result; @@ -449,12 +467,7 @@ namespace Toolbox { return ModelIndex(); } - ModelIndex FileSystemModel::getIndex(const UUID64 &uuid) const { - if (m_index_map.find(uuid) == m_index_map.end()) { - return ModelIndex(); - } - return m_index_map.at(uuid); - } + ModelIndex FileSystemModel::getIndex(const UUID64 &uuid) const { return m_index_map.at(uuid); } ModelIndex FileSystemModel::getIndex(int64_t row, int64_t column, const ModelIndex &parent) const { @@ -668,23 +681,17 @@ namespace Toolbox { std::string ext = data->m_path.extension().string(); if (!validateIndex(index)) { - data->m_icon = make_referable( - fs_icon_path / TypeMap().at("_Invalid").m_image_name); + data->m_icon = m_icon_map["_Invalid"]; } else if (data->m_type == _FileSystemIndexData::Type::DIRECTORY) { - data->m_icon = make_referable( - fs_icon_path / TypeMap().at("_Folder").m_image_name); + data->m_icon = m_icon_map["_Folder"]; } else if (data->m_type == _FileSystemIndexData::Type::ARCHIVE) { - data->m_icon = make_referable( - fs_icon_path / TypeMap().at("_Archive").m_image_name); + data->m_icon = m_icon_map["_Archive"]; } else if (ext.empty()) { - data->m_icon = make_referable( - fs_icon_path / TypeMap().at("_Folder").m_image_name); - } else if (TypeMap().find(ext) == TypeMap().end()) { - data->m_icon = make_referable( - fs_icon_path / TypeMap().at("_File").m_image_name); + data->m_icon = m_icon_map["_Folder"]; + } else if (m_icon_map.find(ext) == m_icon_map.end()) { + data->m_icon = m_icon_map["_File"]; } else { - data->m_icon = make_referable(fs_icon_path / - TypeMap().at(ext).m_image_name); + data->m_icon = m_icon_map[ext]; } } @@ -708,11 +715,6 @@ namespace Toolbox { return index; } - void FileSystemModel::cacheIndex(ModelIndex &index) {} - void FileSystemModel::cacheFolder(ModelIndex &index) {} - void FileSystemModel::cacheFile(ModelIndex &index) {} - void FileSystemModel::cacheArchive(ModelIndex &index) {} - ModelIndex FileSystemModel::getParentArchive(const ModelIndex &index) const { ModelIndex parent = getParent(index); do { @@ -866,19 +868,36 @@ namespace Toolbox { } fs_path FileSystemModelSortFilterProxy::getPath(const ModelIndex &index) const { - ModelIndex &&source_index = toSourceIndex(index); + ModelIndex source_index = toSourceIndex(index); return m_source_model->getPath(std::move(source_index)); } ModelIndex FileSystemModelSortFilterProxy::getParent(const ModelIndex &index) const { - ModelIndex &&source_index = toSourceIndex(index); + ModelIndex source_index = toSourceIndex(index); return toProxyIndex(m_source_model->getParent(std::move(source_index))); } ModelIndex FileSystemModelSortFilterProxy::getSibling(int64_t row, int64_t column, const ModelIndex &index) const { - ModelIndex &&source_index = toSourceIndex(index); - return toProxyIndex(m_source_model->getSibling(row, column, source_index)); + ModelIndex source_index = toSourceIndex(index); + ModelIndex src_parent = getParent(source_index); + const UUID64 &src_parent_uuid = src_parent.getUUID(); + + u64 map_key = src_parent_uuid; + if (!m_source_model->validateIndex(src_parent)) { + map_key = 0; + } + + if (m_row_map.find(map_key) == m_row_map.end()) { + cacheIndex(src_parent); + } + + if (row < m_row_map[map_key].size()) { + int64_t the_row = m_row_map[map_key][row]; + return toProxyIndex(m_source_model->getIndex(the_row, column, src_parent)); + } + + return ModelIndex(); } size_t FileSystemModelSortFilterProxy::getColumnCount(const ModelIndex &index) const { @@ -888,6 +907,11 @@ namespace Toolbox { size_t FileSystemModelSortFilterProxy::getRowCount(const ModelIndex &index) const { ModelIndex &&source_index = toSourceIndex(index); + + if (m_row_map.find(source_index.getUUID()) != m_row_map.end()) { + return m_row_map[source_index.getUUID()].size(); + } + return m_source_model->getRowCount(source_index); } @@ -943,32 +967,83 @@ namespace Toolbox { ModelIndex FileSystemModelSortFilterProxy::toProxyIndex(int64_t row, int64_t column, const ModelIndex &src_parent) const { - std::vector filtered_children = {}; + const UUID64 &src_parent_uuid = src_parent.getUUID(); + u64 map_key = src_parent_uuid; if (!m_source_model->validateIndex(src_parent)) { + map_key = 0; + } + + if (m_row_map.find(map_key) == m_row_map.end()) { + cacheIndex(src_parent); + } + + if (row < m_row_map[map_key].size()) { + int64_t the_row = m_row_map[map_key][row]; + return toProxyIndex(m_source_model->getIndex(the_row, column, src_parent)); + } + + return ModelIndex(); + } + + bool FileSystemModelSortFilterProxy::isFiltered(const UUID64 &uuid) const { + ModelIndex child_index = m_source_model->getIndex(uuid); + bool is_file = + child_index.data<_FileSystemIndexData>()->m_type == _FileSystemIndexData::Type::FILE; + + if (isDirsOnly() && is_file) { + return true; + } + + const std::string &name = child_index.data<_FileSystemIndexData>()->m_name; + return !name.starts_with(m_filter); + } + + void FileSystemModelSortFilterProxy::cacheIndex(const ModelIndex &dir_index) const { + std::vector orig_children = {}; + std::vector proxy_children = {}; + + const UUID64 &src_parent_uuid = dir_index.getUUID(); + + u64 map_key = src_parent_uuid; + if (!m_source_model->validateIndex(dir_index)) { + map_key = 0; + } + + if (m_row_map.find(map_key) != m_row_map.end()) { + return; + } + + if (!m_source_model->validateIndex(dir_index)) { size_t i = 0; ModelIndex root_s = m_source_model->getIndex(i++, 0); while (m_source_model->validateIndex(root_s)) { - filtered_children.push_back(root_s.getUUID()); + orig_children.push_back(root_s.getUUID()); + if (isFiltered(root_s.getUUID())) { + root_s = m_source_model->getIndex(i++, 0); + continue; + } + proxy_children.push_back(root_s.getUUID()); root_s = m_source_model->getIndex(i++, 0); } } else { - const std::vector &children = - src_parent.data<_FileSystemIndexData>()->m_children; - if (children.empty()) { - return ModelIndex(); + orig_children = dir_index.data<_FileSystemIndexData>()->m_children; + if (orig_children.empty()) { + return; } - filtered_children = children; + std::copy_if(orig_children.begin(), orig_children.end(), + std::back_inserter(proxy_children), + [&](const UUID64 &uuid) { return !isFiltered(uuid); }); } - if (row >= filtered_children.size()) { - return ModelIndex(); + if (proxy_children.empty()) { + return; } switch (m_sort_role) { case FileSystemModelSortRole::SORT_ROLE_NAME: { - std::sort(filtered_children.begin(), filtered_children.end(), + std::sort(proxy_children.begin(), proxy_children.end(), [&](const UUID64 &lhs, const UUID64 &rhs) { return _FileSystemIndexDataCompareByName( *m_source_model->getIndex(lhs).data<_FileSystemIndexData>(), @@ -978,7 +1053,7 @@ namespace Toolbox { break; } case FileSystemModelSortRole::SORT_ROLE_SIZE: { - std::sort(filtered_children.begin(), filtered_children.end(), + std::sort(proxy_children.begin(), proxy_children.end(), [&](const UUID64 &lhs, const UUID64 &rhs) { return _FileSystemIndexDataCompareBySize( *m_source_model->getIndex(lhs).data<_FileSystemIndexData>(), @@ -988,7 +1063,7 @@ namespace Toolbox { break; } case FileSystemModelSortRole::SORT_ROLE_DATE: - std::sort(filtered_children.begin(), filtered_children.end(), + std::sort(proxy_children.begin(), proxy_children.end(), [&](const UUID64 &lhs, const UUID64 &rhs) { return _FileSystemIndexDataCompareByDate( *m_source_model->getIndex(lhs).data<_FileSystemIndexData>(), @@ -1000,31 +1075,17 @@ namespace Toolbox { break; } - size_t i = 0; - for (const UUID64 &uuid : filtered_children) { - if (isFiltered(uuid)) { - continue; - } - if (i++ == row) { - ModelIndex src_index = m_source_model->getIndex(uuid); - return toProxyIndex(src_index); + // Build the row map + m_row_map[map_key] = {}; + m_row_map[map_key].resize(proxy_children.size()); + for (size_t i = 0; i < proxy_children.size(); i++) { + for (size_t j = 0; j < orig_children.size(); j++) { + if (proxy_children[i] == orig_children[j]) { + m_row_map[map_key][i] = j; + break; + } } } - - return ModelIndex(); - } - - bool FileSystemModelSortFilterProxy::isFiltered(const UUID64 &uuid) const { - ModelIndex child_index = m_source_model->getIndex(uuid); - bool is_file = - child_index.data<_FileSystemIndexData>()->m_type == _FileSystemIndexData::Type::FILE; - - if (isDirsOnly() && is_file) { - return true; - } - - const std::string &name = child_index.data<_FileSystemIndexData>()->m_name; - return !name.starts_with(m_filter); } } // namespace Toolbox \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index a462341a..0087cda5 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,5 +4,5 @@ "dependencies": [ "libiconv" ], - "builtin-baseline": "5016afa7ed49f8bac5dc801555b7d6af9fe79f58" + "builtin-baseline": "036916f2ca7fbf3effec439119500da00ae478dd" } \ No newline at end of file