Dynamic Neural Field Composer 0.0.0
A C++20 library and interactive application for building and simulating Dynamic Neural Field (DNF) architectures.
Loading...
Searching...
No Matches
file_dialog.h
Go to the documentation of this file.
1
2#pragma once
3
4#include <imgui.h>
5#include <imgui_internal.h>
6#include <chrono>
7#include <string>
8#include <ctime>
9#include <filesystem>
10#include <sstream>
11#include "utils.h"
12
13namespace FileDialog {
14
15 enum class FileDialogType {
18 };
20 Up,
21 Down,
22 None
23 };
24
25 static bool file_dialog_open = false;
27 static float padding = 10.0;
28
29 inline void ShowFileDialog(const bool* open, char* buffer, [[maybe_unused]] unsigned int buffer_size,
31 {
32 static int file_dialog_file_select_index = 0;
33 static int file_dialog_folder_select_index = 0;
34 static std::string file_dialog_current_path = dnf_composer::tools::utils::getResourceRoot() + "/data/";
35 static std::string file_dialog_current_file;
36 static std::string file_dialog_current_folder;
37 static char file_dialog_error[500] = "";
38 static FileDialogSortOrder file_name_sort_order = FileDialogSortOrder::None;
39 static FileDialogSortOrder size_sort_order = FileDialogSortOrder::None;
40 static FileDialogSortOrder date_sort_order = FileDialogSortOrder::None;
41 static FileDialogSortOrder type_sort_order = FileDialogSortOrder::None;
42
43 static bool initial_path_set = false;
44
45 if (open) {
46 // Check if there was already something in the buffer. If so, try to use that path (if it exists).
47 // If it doesn't exist, just put them into the current path.
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;
52 }
53 else {
54 // Check if this is just a file in a real path. If so, use the real path.
55 // If that still doesn't work, use the current path.
56 if (std::filesystem::exists(path)) {
57 // It's a file! Take the path and set it.
58 file_dialog_current_path = path.remove_filename().string();
59 }
60 else {
61 // An invalid path was entered
62 file_dialog_current_path = std::filesystem::current_path().string();
63 }
64 }
65 initial_path_set = true;
66 }
67
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);
72 const char* window_title = (type == FileDialogType::OpenFile ? "Select a file" : "Select a folder");
73 ImGui::Begin(window_title, nullptr, ImGuiWindowFlags_NoResize);
74
75 std::vector<std::filesystem::directory_entry> files;
76 std::vector<std::filesystem::directory_entry> folders;
77 try {
78 for (auto& p : std::filesystem::directory_iterator(file_dialog_current_path)) {
79 if (p.is_directory()) {
80 folders.push_back(p);
81 }
82 else {
83 files.push_back(p);
84 }
85 }
86 }
87 catch (...)
88 {}
89
90 ImGui::Text("%s", file_dialog_current_path.c_str());
91
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;
97
98 ImGui::BeginChild("Directories##1", ImVec2(dirW, listH), true,
99 ImGuiWindowFlags_HorizontalScrollbar);
100
101 if (ImGui::Selectable("..", false, ImGuiSelectableFlags_AllowDoubleClick,
102 ImVec2(ImGui::GetContentRegionAvail().x, 0)))
103 {
104 if (ImGui::IsMouseDoubleClicked(0)) {
105 file_dialog_current_path = std::filesystem::path(file_dialog_current_path).parent_path().string();
106 }
107 }
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)))
112 {
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);
119 //file_dialog_current_folder = "";
120 }
121 else {
122 file_dialog_folder_select_index = i;
123 file_dialog_current_folder = folders[i].path().stem().string();
124 }
125 }
126 }
127 ImGui::EndChild();
128
129 ImGui::SameLine();
130
131 ImGui::BeginChild("Files##1", ImVec2(filesW, listH), true,
132 ImGuiWindowFlags_HorizontalScrollbar);
133 ImGui::Columns(4);
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;
138 }
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;
143 }
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;
148 }
149 if (ImGui::Selectable("File")) {
150 size_sort_order = FileDialogSortOrder::None;
151 date_sort_order = FileDialogSortOrder::None;
152 type_sort_order = FileDialogSortOrder::None;
153 file_name_sort_order =
155 }
156 ImGui::NextColumn();
157 if (ImGui::Selectable("Size")) {
158 file_name_sort_order = FileDialogSortOrder::None;
159 date_sort_order = FileDialogSortOrder::None;
160 type_sort_order = FileDialogSortOrder::None;
161 size_sort_order =
163 }
164 ImGui::NextColumn();
165 if (ImGui::Selectable("Type")) {
166 file_name_sort_order = FileDialogSortOrder::None;
167 date_sort_order = FileDialogSortOrder::None;
168 size_sort_order = FileDialogSortOrder::None;
169 type_sort_order =
171 }
172 ImGui::NextColumn();
173 if (ImGui::Selectable("Date")) {
174 file_name_sort_order = FileDialogSortOrder::None;
175 size_sort_order = FileDialogSortOrder::None;
176 type_sort_order = FileDialogSortOrder::None;
177 date_sort_order =
179 }
180 ImGui::NextColumn();
181 ImGui::Separator();
182
183 // Sort files
184 if (file_name_sort_order != FileDialogSortOrder::None) {
185 std::ranges::sort(files,
186 [](const std::filesystem::directory_entry& a, const std::filesystem::directory_entry& b)
187 {
188 if (file_name_sort_order == FileDialogSortOrder::Down) {
189 return a.path().filename().string() > b.path().filename().string();
190 }
191 else {
192 return a.path().filename().string() < b.path().filename().string();
193 }
194 });
195 }
196 else if (size_sort_order != FileDialogSortOrder::None) {
197 std::ranges::sort(files,
198 [](const std::filesystem::directory_entry& a, const std::filesystem::directory_entry& b)
199 {
200 if (size_sort_order == FileDialogSortOrder::Down) {
201 return a.file_size() > b.file_size();
202 }
203 else {
204 return a.file_size() < b.file_size();
205 }
206 });
207 }
208 else if (type_sort_order != FileDialogSortOrder::None) {
209 std::ranges::sort(files,
210 [](const std::filesystem::directory_entry& a, const std::filesystem::directory_entry& b)
211 {
212 if (type_sort_order == FileDialogSortOrder::Down) {
213 return a.path().extension().string() > b.path().extension().string();
214 }
215 else {
216 return a.path().extension().string() < b.path().extension().string();
217 }
218 });
219 }
220 else if (date_sort_order != FileDialogSortOrder::None) {
221 std::ranges::sort(files,
222 [](const std::filesystem::directory_entry& a, const std::filesystem::directory_entry& b)
223 {
224 if (date_sort_order == FileDialogSortOrder::Down) {
225 return a.last_write_time() > b.last_write_time();
226 }
227 else {
228 return a.last_write_time() < b.last_write_time();
229 }
230 });
231 }
232
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)))
237 {
238 file_dialog_file_select_index = i;
239 file_dialog_current_file = files[i].path().filename().string();
240 file_dialog_current_folder = "";
241 }
242 ImGui::NextColumn();
243 ImGui::TextUnformatted(std::to_string(files[i].file_size()).c_str());
244 ImGui::NextColumn();
245 ImGui::TextUnformatted(files[i].path().extension().string().c_str());
246 ImGui::NextColumn();
247 auto ftime = files[i].last_write_time();
248 auto st =
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);
252
253 std::tm mt{};
255 // Handle error - you might want to throw an exception or use a default,
256 // For example:
257 throw std::runtime_error("Failed to convert time");
258 }
259 std::stringstream ss;
260 ss << std::put_time(&mt, "%F %R");
261
262 ImGui::TextUnformatted(ss.str().c_str());
263 ImGui::NextColumn();
264 }
265 ImGui::EndChild();
266
267 // Use std::filesystem::path for proper path concatenation
268 std::filesystem::path basePath(file_dialog_current_path);
269 std::filesystem::path selectedPath;
270
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;
275 } else {
276 selectedPath = basePath;
277 }
278
279 std::string selected_file_path = selectedPath.string();
280
281 ImGui::PushItemWidth(availW);
282
283 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 6);
284
285 if (ImGui::Button("New folder")) {
286 ImGui::OpenPopup("NewFolderPopup");
287 }
288 ImGui::SameLine();
289
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);
295 }
296 if (ImGui::Button("Delete folder")) {
297 ImGui::OpenPopup("DeleteFolderPopup");
298 }
299 if (disable_delete_button) {
300 ImGui::PopStyleVar();
301 ImGui::PopItemFlag();
302 }
303
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");
314 }
315 else {
316 // Use std::filesystem::path for proper path concatenation
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();
320 }
321 }
322 ImGui::SameLine();
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();
327 }
328 ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f), "%s", new_folder_error);
329 ImGui::EndPopup();
330 }
331
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")) {
339 // Use std::filesystem::path for proper path concatenation
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();
343 }
344 ImGui::SameLine();
345 if (ImGui::Button("No")) {
346 ImGui::CloseCurrentPopup();
347 }
348 ImGui::EndPopup();
349 }
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;
354 ImGui::SameLine();
355 ImGui::SetCursorPosX(ImGui::GetWindowWidth() - rightPad - chooseW - btnGap - cancelW);
356
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;
363 file_dialog_open = false;
364 };
365
366 if (ImGui::Button("Cancel")) {
367 reset_everything();
368 }
369 ImGui::SameLine();
370 if (ImGui::Button("Choose")) {
371 if (type == FileDialogType::SelectFolder) {
372 if (file_dialog_current_folder.empty()) {
373 snprintf(file_dialog_error, sizeof(file_dialog_error), "%s", "Error: You must select a folder!");
374 }
375 else {
376 // Use std::filesystem::path for proper path concatenation
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", "");
381 reset_everything();
382 }
383 }
384 else if (type == FileDialogType::OpenFile) {
385 if (file_dialog_current_file.empty()) {
386 snprintf(file_dialog_error, sizeof(file_dialog_error), "%s", "Error: You must select a file!");
387 }
388 else {
389 // Use std::filesystem::path for proper path concatenation
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", "");
394 reset_everything();
395 }
396 }
397 }
398
399 if (strlen(file_dialog_error) > 0) {
400 ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f), "%s", file_dialog_error);
401 }
402
403 ImGui::End();
404 }
405 }
406
407 inline void ShowFileDialog_s(const bool* open, char* buffer, FileDialogType type = FileDialogType::OpenFile)
408 {
409 ShowFileDialog(open, buffer, 500, type);
410 }
411}
Definition file_dialog.h:13
static bool file_dialog_open
Definition file_dialog.h:25
static float padding
Definition file_dialog.h:27
void ShowFileDialog(const bool *open, char *buffer, unsigned int buffer_size, FileDialogType type=FileDialogType::OpenFile)
Definition file_dialog.h:29
FileDialogSortOrder
Definition file_dialog.h:19
void ShowFileDialog_s(const bool *open, char *buffer, FileDialogType type=FileDialogType::OpenFile)
Definition file_dialog.h:407
FileDialogType
Definition file_dialog.h:15
static FileDialogType file_dialog_open_type
Definition file_dialog.h:26
std::string getResourceRoot()
Definition utils.cpp:17
bool safe_localtime(const std::time_t *time, std::tm *result)
Definition utils.h:53