#include "./file_selector.hpp" #include #include #include #include #include #include void FileSelector::reset(void) { _is_valid = [](auto){ return true; }; _on_choose = [](auto){}; _on_cancel = [](){}; } FileSelector::FileSelector(void) { try { _current_file_path = std::filesystem::current_path() / ""; } catch (std::filesystem::filesystem_error const& ex) { std::cerr << "FS: exception creating _current_file_path: " << ex.what() << "\n"; } reset(); } FileSelector::~FileSelector(void) { if (_future_cache.valid()) { _future_cache.get(); } } void FileSelector::requestFile( std::function&& is_valid, std::function&& on_choose, std::function&& on_cancel ) { _open_popup = true; _is_valid = std::move(is_valid); _on_choose = std::move(on_choose); _on_cancel = std::move(on_cancel); } void FileSelector::render(void) { if (_open_popup) { _open_popup = false; ImGui::OpenPopup("file picker##FileSelector"); const auto TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const auto TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); ImGui::SetNextWindowSize({TEXT_BASE_WIDTH*100, TEXT_BASE_HEIGHT*30}); } if (ImGui::BeginPopupModal("file picker##FileSelector", nullptr/*, ImGuiWindowFlags_NoDecoration*/)) { const auto TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const auto TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); std::filesystem::path current_path = _current_file_path; current_path.remove_filename(); ImGui::Text("path: %s", _current_file_path.generic_u8string().c_str()); // begin table with selectables constexpr ImGuiTableFlags table_flags = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Sortable ; if (ImGui::BeginTable("dir listing", 4, table_flags, {0, -TEXT_BASE_HEIGHT * 2.5f})) { enum class SortID : ImGuiID { name = 1, size, date }; ImGui::TableSetupColumn("type", 0, TEXT_BASE_WIDTH); ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultSort, 0.f, static_cast(SortID::name)); ImGui::TableSetupColumn("size", 0, 0.f, static_cast(SortID::size)); ImGui::TableSetupColumn("dd.mm.yyyy - hh:mm", 0, 0.f, static_cast(SortID::date)); ImGui::TableSetupScrollFreeze(0, 2); ImGui::TableHeadersRow(); //ImGui::TableNextRow(0, TEXT_BASE_HEIGHT); if (current_path.has_parent_path()) { if (ImGui::TableNextColumn()) { if (ImGui::Selectable("D##..", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { // the first "parent_path()" only removes the filename and the ending "/" _current_file_path = _current_file_path.parent_path().parent_path() / ""; //_current_file_path = _current_file_path.remove_filename().parent_path() / ""; } } if (ImGui::TableNextColumn()) { ImGui::TextUnformatted(".."); } if (ImGui::TableNextColumn()) { ImGui::TextDisabled("---"); } if (ImGui::TableNextColumn()) { ImGui::TextDisabled("---"); } } size_t tmp_id = 0; // dirs 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)); bool start_new_collection_task {false}; if (_future_cache.valid()) { 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; try { for (auto const& dir_entry : std::filesystem::directory_iterator(path)) { if (dir_entry.is_directory()) { dirs.push_back(dir_entry); } else if (dir_entry.is_regular_file()) { files.push_back(dir_entry); } } // do sorting here if (sorts_specs != nullptr && sorts_specs->SpecsCount >= 1) { switch (static_cast(sorts_specs->Specs->ColumnUserID)) { break; case SortID::name: if (sorts_specs->Specs->SortDirection == ImGuiSortDirection_Descending) { std::sort(dirs.begin(), dirs.end(), [](const auto& a, const auto& b) -> bool { return a.path() < b.path(); }); std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) -> bool { return a.path().filename() < b.path().filename(); }); } else { std::sort(dirs.begin(), dirs.end(), [](const auto& a, const auto& b) -> bool { return a.path() > b.path(); }); std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) -> bool { return a.path().filename() > b.path().filename(); }); } break; case SortID::size: if (sorts_specs->Specs->SortDirection == ImGuiSortDirection_Descending) { // TODO: sort dirs? std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) -> bool { return a.file_size() < b.file_size(); }); } else { // TODO: sort dirs? std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) -> bool { return a.file_size() > b.file_size(); }); } break; case SortID::date: if (sorts_specs->Specs->SortDirection == ImGuiSortDirection_Descending) { std::sort(dirs.begin(), dirs.end(), [](const auto& a, const auto& b) -> bool { return a.last_write_time() < b.last_write_time(); }); std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) -> bool { return a.last_write_time() < b.last_write_time(); }); } else { std::sort(dirs.begin(), dirs.end(), [](const auto& a, const auto& b) -> bool { return a.last_write_time() > b.last_write_time(); }); std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) -> bool { return a.last_write_time() > b.last_write_time(); }); } break; default: ; } } } catch (std::filesystem::filesystem_error const& ex) { // we likely saw a file disapear std::cerr << "FS thread exception: " << ex.what() << "\n"; } 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 (tmp_id & 0x01) { ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, dir_bg0_color); } else { ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, dir_bg1_color); } ImGui::PushID(tmp_id++); if (ImGui::Selectable("D", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { try { _current_file_path = dir_entry.path() / ""; } catch (...) {} } ImGui::PopID(); } if (ImGui::TableNextColumn()) { try { ImGui::TextUnformatted((dir_entry.path().filename().generic_u8string() + "/").c_str()); } catch (...) {} } if (ImGui::TableNextColumn()) { ImGui::TextDisabled("---"); } if (ImGui::TableNextColumn()) { try { const auto file_time_converted = std::chrono::time_point_cast( dir_entry.last_write_time() - decltype(dir_entry.last_write_time())::clock::now() + std::chrono::system_clock::now() ); const auto ctime = std::chrono::system_clock::to_time_t(file_time_converted); const auto ltime = std::localtime(&ctime); ImGui::TextDisabled("%2d.%2d.%2d - %2d:%2d", ltime->tm_mday, ltime->tm_mon + 1, ltime->tm_year + 1900, ltime->tm_hour, ltime->tm_min); } catch (...) {} } } } // files ImGuiListClipper files_clipper; files_clipper.Begin(files.size()); while (files_clipper.Step()) { for (int row = files_clipper.DisplayStart; row < files_clipper.DisplayEnd; row++) { const auto& file_entry = files.at(row); if (ImGui::TableNextColumn()) { ImGui::PushID(tmp_id++); if (ImGui::Selectable("F", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { try { _current_file_path = file_entry.path(); } catch(...) {} } ImGui::PopID(); } if (ImGui::TableNextColumn()) { try { ImGui::TextUnformatted(file_entry.path().filename().generic_u8string().c_str()); } catch(...) {} } if (ImGui::TableNextColumn()) { try { ImGui::TextDisabled("%s", std::to_string(file_entry.file_size()).c_str()); } catch(...) {} } if (ImGui::TableNextColumn()) { try { const auto file_time_converted = std::chrono::time_point_cast( file_entry.last_write_time() - decltype(file_entry.last_write_time())::clock::now() + std::chrono::system_clock::now() ); const auto ctime = std::chrono::system_clock::to_time_t(file_time_converted); const auto ltime = std::localtime(&ctime); ImGui::TextDisabled("%2d.%2d.%2d - %2d:%2d", ltime->tm_mday, ltime->tm_mon, ltime->tm_year + 1900, ltime->tm_hour, ltime->tm_min); } catch(...) {} } } } } else { // render loading placeholder if (ImGui::TableNextColumn()) { ImGui::TextUnformatted("-"); } if (ImGui::TableNextColumn()) { ImGui::TextUnformatted("... loading ..."); } } ImGui::EndTable(); } //if (ImGui::Button("X cancel", {ImGui::GetWindowContentRegionWidth()/2.f, TEXT_BASE_HEIGHT*2})) { if (ImGui::Button("X cancel", {ImGui::GetContentRegionAvail().x/2.f, TEXT_BASE_HEIGHT*2})) { ImGui::CloseCurrentPopup(); _on_cancel(); reset(); } ImGui::SameLine(); if (ImGui::Button("choose ->", {-FLT_MIN, TEXT_BASE_HEIGHT*2})) { if (_is_valid(_current_file_path)) { ImGui::CloseCurrentPopup(); _on_choose(_current_file_path); reset(); } } ImGui::EndPopup(); } }