29 inline void ShowFileDialog(
const bool* open,
char* buffer, [[maybe_unused]]
unsigned int buffer_size,
32 static int file_dialog_file_select_index = 0;
33 static int file_dialog_folder_select_index = 0;
35 static std::string file_dialog_current_file;
36 static std::string file_dialog_current_folder;
37 static char file_dialog_error[500] =
"";
43 static bool initial_path_set =
false;
48 if (!initial_path_set && strlen(buffer) > 0) {
49 auto path = std::filesystem::path(buffer);
50 if (std::filesystem::is_directory(path)) {
51 file_dialog_current_path = buffer;
56 if (std::filesystem::exists(path)) {
58 file_dialog_current_path = path.remove_filename().string();
62 file_dialog_current_path = std::filesystem::current_path().string();
65 initial_path_set =
true;
68 const float ui = ImGui::GetIO().FontGlobalScale;
69 ImGui::SetNextWindowSize(ImVec2(1100.0f * ui, 450.0f * ui));
70 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
71 ImGui::SetNextWindowBgAlpha(1.0f);
73 ImGui::Begin(window_title,
nullptr, ImGuiWindowFlags_NoResize);
75 std::vector<std::filesystem::directory_entry> files;
76 std::vector<std::filesystem::directory_entry> folders;
78 for (
auto& p : std::filesystem::directory_iterator(file_dialog_current_path)) {
79 if (p.is_directory()) {
90 ImGui::Text(
"%s", file_dialog_current_path.c_str());
92 const float dirW = 200.0f * ui;
93 const float availW = ImGui::GetContentRegionAvail().x;
94 const float filesW = availW - dirW - ImGui::GetStyle().ItemSpacing.x;
95 const float btnRowH = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y + 6.0f;
96 const float listH = ImGui::GetContentRegionAvail().y - btnRowH - ImGui::GetStyle().ItemSpacing.y;
98 ImGui::BeginChild(
"Directories##1", ImVec2(dirW, listH),
true,
99 ImGuiWindowFlags_HorizontalScrollbar);
101 if (ImGui::Selectable(
"..",
false, ImGuiSelectableFlags_AllowDoubleClick,
102 ImVec2(ImGui::GetContentRegionAvail().x, 0)))
104 if (ImGui::IsMouseDoubleClicked(0)) {
105 file_dialog_current_path = std::filesystem::path(file_dialog_current_path).parent_path().string();
108 for (
int i = 0; i < folders.size(); ++i) {
109 if (ImGui::Selectable(folders[i].path().stem().
string().c_str(),
110 i == file_dialog_folder_select_index, ImGuiSelectableFlags_AllowDoubleClick,
111 ImVec2(ImGui::GetContentRegionAvail().x, 0)))
113 file_dialog_current_file =
"";
114 if (ImGui::IsMouseDoubleClicked(0)) {
115 file_dialog_current_path = folders[i].path().string();
116 file_dialog_folder_select_index = 0;
117 file_dialog_file_select_index = 0;
118 ImGui::SetScrollHereY(0.0f);
122 file_dialog_folder_select_index = i;
123 file_dialog_current_folder = folders[i].path().stem().string();
131 ImGui::BeginChild(
"Files##1", ImVec2(filesW, listH),
true,
132 ImGuiWindowFlags_HorizontalScrollbar);
134 static float initial_spacing_column_0 = 500.0f;
135 if (initial_spacing_column_0 > 0) {
136 ImGui::SetColumnWidth(0, initial_spacing_column_0);
137 initial_spacing_column_0 = 0.0f;
139 static float initial_spacing_column_1 = 100.0f;
140 if (initial_spacing_column_1 > 0) {
141 ImGui::SetColumnWidth(1, initial_spacing_column_1);
142 initial_spacing_column_1 = 0.0f;
144 static float initial_spacing_column_2 = 100.0f;
145 if (initial_spacing_column_2 > 0) {
146 ImGui::SetColumnWidth(2, initial_spacing_column_2);
147 initial_spacing_column_2 = 0.0f;
149 if (ImGui::Selectable(
"File")) {
153 file_name_sort_order =
157 if (ImGui::Selectable(
"Size")) {
165 if (ImGui::Selectable(
"Type")) {
173 if (ImGui::Selectable(
"Date")) {
185 std::ranges::sort(files,
186 [](
const std::filesystem::directory_entry& a,
const std::filesystem::directory_entry& b)
189 return a.path().filename().
string() > b.path().filename().string();
192 return a.path().filename().string() < b.path().filename().string();
197 std::ranges::sort(files,
198 [](
const std::filesystem::directory_entry& a,
const std::filesystem::directory_entry& b)
201 return a.file_size() > b.file_size();
204 return a.file_size() < b.file_size();
209 std::ranges::sort(files,
210 [](
const std::filesystem::directory_entry& a,
const std::filesystem::directory_entry& b)
213 return a.path().extension().
string() > b.path().extension().string();
216 return a.path().extension().string() < b.path().extension().string();
221 std::ranges::sort(files,
222 [](
const std::filesystem::directory_entry& a,
const std::filesystem::directory_entry& b)
225 return a.last_write_time() > b.last_write_time();
228 return a.last_write_time() < b.last_write_time();
233 for (
int i = 0; i < files.size(); ++i) {
234 if (ImGui::Selectable(files[i].path().filename().
string().c_str(),
235 i == file_dialog_file_select_index, ImGuiSelectableFlags_AllowDoubleClick,
236 ImVec2(ImGui::GetContentRegionAvail().x, 0)))
238 file_dialog_file_select_index = i;
239 file_dialog_current_file = files[i].path().filename().string();
240 file_dialog_current_folder =
"";
243 ImGui::TextUnformatted(std::to_string(files[i].file_size()).c_str());
245 ImGui::TextUnformatted(files[i].path().extension().
string().c_str());
247 auto ftime = files[i].last_write_time();
249 std::chrono::time_point_cast<std::chrono::system_clock::duration>(ftime -
decltype(ftime)::clock::now()
250 + std::chrono::system_clock::now());
251 std::time_t tt = std::chrono::system_clock::to_time_t(st);
257 throw std::runtime_error(
"Failed to convert time");
259 std::stringstream ss;
260 ss << std::put_time(&mt,
"%F %R");
262 ImGui::TextUnformatted(ss.str().c_str());
268 std::filesystem::path basePath(file_dialog_current_path);
269 std::filesystem::path selectedPath;
271 if (!file_dialog_current_folder.empty()) {
272 selectedPath = basePath / file_dialog_current_folder;
273 }
else if (!file_dialog_current_file.empty()) {
274 selectedPath = basePath / file_dialog_current_file;
276 selectedPath = basePath;
279 std::string selected_file_path = selectedPath.string();
281 ImGui::PushItemWidth(availW);
283 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 6);
285 if (ImGui::Button(
"New folder")) {
286 ImGui::OpenPopup(
"NewFolderPopup");
290 static bool disable_delete_button =
false;
291 disable_delete_button = (file_dialog_current_folder.empty());
292 if (disable_delete_button) {
293 ImGui::PushItemFlag(ImGuiItemFlags_Disabled,
true);
294 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
296 if (ImGui::Button(
"Delete folder")) {
297 ImGui::OpenPopup(
"DeleteFolderPopup");
299 if (disable_delete_button) {
300 ImGui::PopStyleVar();
301 ImGui::PopItemFlag();
304 ImVec2 center(ImGui::GetWindowPos().x + ImGui::GetWindowSize().x * 0.5f, ImGui::GetWindowPos().y + ImGui::GetWindowSize().y * 0.5f);
305 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
306 if (ImGui::BeginPopup(
"NewFolderPopup", ImGuiWindowFlags_Modal)) {
307 ImGui::Text(
"Enter a name for the new folder");
308 static char new_folder_name[500] =
"";
309 static char new_folder_error[500] =
"";
310 ImGui::InputText(
"##newfolder", new_folder_name,
sizeof(new_folder_name));
311 if (ImGui::Button(
"Create##1")) {
312 if (strlen(new_folder_name) <= 0) {
313 snprintf(new_folder_error,
sizeof(new_folder_error),
"%s",
"Folder name can't be empty");
317 std::filesystem::path new_folder_path = std::filesystem::path(file_dialog_current_path) / new_folder_name;
318 std::filesystem::create_directory(new_folder_path);
319 ImGui::CloseCurrentPopup();
323 if (ImGui::Button(
"Cancel##1")) {
324 snprintf(new_folder_name,
sizeof(new_folder_name),
"%s",
"");
325 snprintf(new_folder_error,
sizeof(new_folder_error),
"%s",
"");
326 ImGui::CloseCurrentPopup();
328 ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f),
"%s", new_folder_error);
332 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
333 if (ImGui::BeginPopup(
"DeleteFolderPopup", ImGuiWindowFlags_Modal)) {
334 ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f),
"Are you sure you want to delete this folder?");
335 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 6);
336 ImGui::TextUnformatted(file_dialog_current_folder.c_str());
337 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 6);
338 if (ImGui::Button(
"Yes")) {
340 std::filesystem::path folder_to_delete = std::filesystem::path(file_dialog_current_path) / file_dialog_current_folder;
341 std::filesystem::remove(folder_to_delete);
342 ImGui::CloseCurrentPopup();
345 if (ImGui::Button(
"No")) {
346 ImGui::CloseCurrentPopup();
350 const float cancelW = ImGui::CalcTextSize(
"Cancel").x + ImGui::GetStyle().FramePadding.x * 2.0f;
351 const float chooseW = ImGui::CalcTextSize(
"Choose").x + ImGui::GetStyle().FramePadding.x * 2.0f;
352 const float btnGap = ImGui::GetStyle().ItemSpacing.x;
353 const float rightPad = ImGui::GetStyle().WindowPadding.x;
355 ImGui::SetCursorPosX(ImGui::GetWindowWidth() - rightPad - chooseW - btnGap - cancelW);
357 static auto reset_everything = [&]() {
358 file_dialog_file_select_index = 0;
359 file_dialog_folder_select_index = 0;
360 file_dialog_current_file =
"";
361 snprintf(file_dialog_error,
sizeof(file_dialog_error),
"%s",
"");
362 initial_path_set =
false;
366 if (ImGui::Button(
"Cancel")) {
370 if (ImGui::Button(
"Choose")) {
372 if (file_dialog_current_folder.empty()) {
373 snprintf(file_dialog_error,
sizeof(file_dialog_error),
"%s",
"Error: You must select a folder!");
377 std::filesystem::path folder_path = std::filesystem::path(file_dialog_current_path) / file_dialog_current_folder;
378 std::string path = folder_path.string();
379 snprintf(buffer, path.length() + 1,
"%s", path.c_str());
380 snprintf(file_dialog_error,
sizeof(file_dialog_error),
"%s",
"");
385 if (file_dialog_current_file.empty()) {
386 snprintf(file_dialog_error,
sizeof(file_dialog_error),
"%s",
"Error: You must select a file!");
390 std::filesystem::path file_path = std::filesystem::path(file_dialog_current_path) / file_dialog_current_file;
391 std::string path = file_path.string();
392 snprintf(buffer, path.length() + 1,
"%s", path.c_str());
393 snprintf(file_dialog_error,
sizeof(file_dialog_error),
"%s",
"");
399 if (strlen(file_dialog_error) > 0) {
400 ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f),
"%s", file_dialog_error);