build file selector list in a thread

This commit is contained in:
Green Sky 2025-04-15 19:52:37 +02:00
parent f18d716924
commit ab40ef7cb1
No known key found for this signature in database
GPG Key ID: DBE05085D874AB4A
3 changed files with 186 additions and 122 deletions

View File

@ -18,6 +18,12 @@ FileSelector::FileSelector(void) {
reset(); reset();
} }
FileSelector::~FileSelector(void) {
if (_future_cache.valid()) {
_future_cache.get();
}
}
void FileSelector::requestFile( void FileSelector::requestFile(
std::function<bool(std::filesystem::path& path)>&& is_valid, std::function<bool(std::filesystem::path& path)>&& is_valid,
std::function<void(const std::filesystem::path& path)>&& on_choose, std::function<void(const std::filesystem::path& path)>&& on_choose,
@ -101,9 +107,32 @@ void FileSelector::render(void) {
static const ImU32 dir_bg0_color = ImGui::GetColorU32(ImVec4(0.6, 0.6, 0.1, 0.15)); static const ImU32 dir_bg0_color = ImGui::GetColorU32(ImVec4(0.6, 0.6, 0.1, 0.15));
static const ImU32 dir_bg1_color = ImGui::GetColorU32(ImVec4(0.7, 0.7, 0.2, 0.15)); static const ImU32 dir_bg1_color = ImGui::GetColorU32(ImVec4(0.7, 0.7, 0.2, 0.15));
std::vector<std::filesystem::directory_entry> dirs; bool start_new_collection_task {false};
std::vector<std::filesystem::directory_entry> files; if (_future_cache.valid()) {
for (auto const& dir_entry : std::filesystem::directory_iterator(current_path)) { if (_future_cache.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
// data is ready
_current_cache = _future_cache.get();
// also queue a new one
start_new_collection_task = true;
}
} else {
start_new_collection_task = true;
}
// check if cache still current
if (_current_cache.has_value() && _current_cache.value().file_path != current_path) {
// and drop it if not
_current_cache.reset();
}
if (start_new_collection_task) {
_future_cache = std::async(std::launch::async, [path = current_path, sorts_specs = ImGui::TableGetSortSpecs()](void) -> CachedData {
CachedData cd;
cd.file_path = path;
auto& dirs = cd.dirs;
auto& files = cd.files;
for (auto const& dir_entry : std::filesystem::directory_iterator(path)) {
if (dir_entry.is_directory()) { if (dir_entry.is_directory()) {
dirs.push_back(dir_entry); dirs.push_back(dir_entry);
} else if (dir_entry.is_regular_file()) { } else if (dir_entry.is_regular_file()) {
@ -113,8 +142,7 @@ void FileSelector::render(void) {
try { try {
// do sorting here // do sorting here
// TODO: cache the result (lol) if (sorts_specs != nullptr && sorts_specs->SpecsCount >= 1) {
if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs(); sorts_specs != nullptr && sorts_specs->SpecsCount >= 1) {
switch (static_cast<SortID>(sorts_specs->Specs->ColumnUserID)) { switch (static_cast<SortID>(sorts_specs->Specs->ColumnUserID)) {
break; case SortID::name: break; case SortID::name:
if (sorts_specs->Specs->SortDirection == ImGuiSortDirection_Descending) { if (sorts_specs->Specs->SortDirection == ImGuiSortDirection_Descending) {
@ -167,7 +195,20 @@ void FileSelector::render(void) {
// we likely saw a file disapear // we likely saw a file disapear
} }
for (auto const& dir_entry : dirs) { return cd;
});
}
if (_current_cache.has_value()) {
const auto& dirs = _current_cache.value().dirs;
const auto& files = _current_cache.value().files;
// dirs
ImGuiListClipper dirs_clipper;
dirs_clipper.Begin(dirs.size());
while (dirs_clipper.Step()) {
for (int row = dirs_clipper.DisplayStart; row < dirs_clipper.DisplayEnd; row++) {
const auto& dir_entry = dirs.at(row);
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
if (tmp_id & 0x01) { if (tmp_id & 0x01) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, dir_bg0_color); ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, dir_bg0_color);
@ -201,33 +242,34 @@ void FileSelector::render(void) {
ImGui::TextDisabled("%2d.%2d.%2d - %2d:%2d", ltime->tm_mday, ltime->tm_mon + 1, ltime->tm_year + 1900, ltime->tm_hour, ltime->tm_min); ImGui::TextDisabled("%2d.%2d.%2d - %2d:%2d", ltime->tm_mday, ltime->tm_mon + 1, ltime->tm_year + 1900, ltime->tm_hour, ltime->tm_min);
} }
} }
}
// files // files
ImGuiListClipper files_clipper; ImGuiListClipper files_clipper;
files_clipper.Begin(files.size()); files_clipper.Begin(files.size());
while (files_clipper.Step()) { while (files_clipper.Step()) {
for (int row = files_clipper.DisplayStart; row < files_clipper.DisplayEnd; row++) { for (int row = files_clipper.DisplayStart; row < files_clipper.DisplayEnd; row++) {
const auto& dir_entry = files.at(row); const auto& file_entry = files.at(row);
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
ImGui::PushID(tmp_id++); ImGui::PushID(tmp_id++);
if (ImGui::Selectable("F", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { if (ImGui::Selectable("F", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
_current_file_path = dir_entry.path(); _current_file_path = file_entry.path();
} }
ImGui::PopID(); ImGui::PopID();
} }
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
ImGui::TextUnformatted(dir_entry.path().filename().generic_u8string().c_str()); ImGui::TextUnformatted(file_entry.path().filename().generic_u8string().c_str());
} }
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
ImGui::TextDisabled("%s", std::to_string(dir_entry.file_size()).c_str()); ImGui::TextDisabled("%s", std::to_string(file_entry.file_size()).c_str());
} }
if (ImGui::TableNextColumn()) { if (ImGui::TableNextColumn()) {
const auto file_time_converted = std::chrono::time_point_cast<std::chrono::system_clock::duration>( const auto file_time_converted = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
dir_entry.last_write_time() file_entry.last_write_time()
- decltype(dir_entry.last_write_time())::clock::now() - decltype(file_entry.last_write_time())::clock::now()
+ std::chrono::system_clock::now() + std::chrono::system_clock::now()
); );
const auto ctime = std::chrono::system_clock::to_time_t(file_time_converted); const auto ctime = std::chrono::system_clock::to_time_t(file_time_converted);
@ -237,6 +279,15 @@ void FileSelector::render(void) {
} }
} }
} }
} else {
// render loading placeholder
if (ImGui::TableNextColumn()) {
ImGui::TextUnformatted("-");
}
if (ImGui::TableNextColumn()) {
ImGui::TextUnformatted("... loading ...");
}
}
ImGui::EndTable(); ImGui::EndTable();
} }

View File

@ -2,10 +2,20 @@
#include <filesystem> #include <filesystem>
#include <functional> #include <functional>
#include <optional>
#include <future>
struct FileSelector { struct FileSelector {
std::filesystem::path _current_file_path = std::filesystem::canonical(".") / ""; // add / std::filesystem::path _current_file_path = std::filesystem::canonical(".") / ""; // add /
struct CachedData {
std::filesystem::path file_path; // can be used to check against current
std::vector<std::filesystem::directory_entry> dirs;
std::vector<std::filesystem::directory_entry> files;
};
std::optional<CachedData> _current_cache;
std::future<CachedData> _future_cache;
bool _open_popup {false}; bool _open_popup {false};
std::function<bool(std::filesystem::path& path)> _is_valid = [](auto){ return true; }; std::function<bool(std::filesystem::path& path)> _is_valid = [](auto){ return true; };
@ -16,6 +26,7 @@ struct FileSelector {
public: public:
FileSelector(void); FileSelector(void);
~FileSelector(void);
// TODO: supply hints // TODO: supply hints
// HACK: until we supply hints, is_valid can modify // HACK: until we supply hints, is_valid can modify

View File

@ -25,7 +25,9 @@ void ImageViewerPopup::render(float) {
ImGui::OpenPopup("Image##ImageViewerPopup"); ImGui::OpenPopup("Image##ImageViewerPopup");
} }
if (!ImGui::BeginPopup("Image##ImageViewerPopup", ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize)) { // TODO: remplace with modal(?), pop up i limited to viewport in size
// or replace with fixed window where the image can be moved
if (!ImGui::BeginPopup("Image##ImageViewerPopup", ImGuiWindowFlags_NoDecoration)) {
_m = {}; // meh, event on close would be nice, but the reset is cheap _m = {}; // meh, event on close would be nice, but the reset is cheap
_scale = 1.f; _scale = 1.f;
return; return;