Fling Engine  0.00.1
Fling Engine is a game engine written in Vulkan
ImFileBrowser.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 // From https://github.com/AirGuanZ/imgui-filebrowser
4 // MIT liscence
5 
6 #include <array>
7 #include <filesystem>
8 #include <functional>
9 #include <memory>
10 #include <string>
11 
12 #ifndef IMGUI_VERSION
13 # error "include imgui.h before this header"
14 #endif
15 
17 
19 {
20  ImGuiFileBrowserFlags_SelectDirectory = 1 << 0, // select directory instead of regular file
21  ImGuiFileBrowserFlags_EnterNewFilename = 1 << 1, // allow user to enter new filename when selecting regular file
22  ImGuiFileBrowserFlags_NoModal = 1 << 2, // file browsing window is modal by default. specify this to use a popup window
23  ImGuiFileBrowserFlags_NoTitleBar = 1 << 3, // hide window title bar
24  ImGuiFileBrowserFlags_NoStatusBar = 1 << 4, // hide status bar at the bottom of browsing window
25  ImGuiFileBrowserFlags_CloseOnEsc = 1 << 5, // close file browser when pressing 'ESC'
26  ImGuiFileBrowserFlags_CreateNewDir = 1 << 6, // allow user to create new directory
27 };
28 
29 namespace ImGui
30 {
32  {
33  public:
34 
35  // pwd is set to current working directory by default
36  explicit FileBrowser(ImGuiFileBrowserFlags flags = 0);
37 
38  FileBrowser(const FileBrowser &copyFrom);
39 
40  FileBrowser &operator=(const FileBrowser &copyFrom);
41 
42  // set the window title text
43  void SetTitle(std::string title);
44 
45  // open the browsing window
46  void Open();
47 
48  // close the browsing window
49  void Close();
50 
51  // the browsing window is opened or not
52  bool IsOpened() const noexcept;
53 
54  // display the browsing window if opened
55  void Display();
56 
57  // returns true when there is a selected filename and the "ok" button was clicked
58  bool HasSelected() const noexcept;
59 
60  // set current browsing directory
61  bool SetPwd(const std::filesystem::path &pwd = std::filesystem::current_path());
62 
63  // returns selected filename. make sense only when HasSelected returns true
64  std::filesystem::path GetSelected() const;
65 
66  // set selected filename to empty
67  void ClearSelected();
68 
69  void SetTypeFilters(const std::vector<const char*> &typeFilters);
70 
71  private:
72 
73  class ScopeGuard
74  {
75  std::function<void()> func_;
76 
77  public:
78 
79  template<typename T>
80  explicit ScopeGuard(T func) : func_(std::move(func)) { }
81  ~ScopeGuard() { func_(); }
82  };
83 
84  void SetPwdUncatched(const std::filesystem::path &pwd);
85 
86 #ifdef _WIN32
87  static std::uint32_t GetDrivesBitMask();
88 #endif
89 
91 
92  std::string title_;
93  std::string openLabel_;
94 
95  bool openFlag_;
96  bool closeFlag_;
97  bool isOpened_;
98  bool ok_;
99 
100  std::string statusStr_;
101 
102  std::vector<const char*> typeFilters_;
104 
105  std::filesystem::path pwd_;
106  std::string selectedFilename_;
107 
108  struct FileRecord
109  {
110  bool isDir = false;
111  std::string name;
112  std::string showName;
113  std::string extension;
114  };
115  std::vector<FileRecord> fileRecords_;
116 
117  // IMPROVE: overflow when selectedFilename_.length() > inputNameBuf_.size() - 1
118  static constexpr size_t INPUT_NAME_BUF_SIZE = 512;
119  std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> inputNameBuf_;
120 
121  std::string openNewDirLabel_;
122  std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> newDirNameBuf_;
123 
124 #ifdef _WIN32
125  uint32_t drives_;
126 #endif
127  };
128 } // namespace ImGui
129 
131  : flags_(flags),
132  openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false),
133  inputNameBuf_(std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>())
134 {
136  newDirNameBuf_ = std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>();
137 
138  inputNameBuf_->at(0) = '\0';
139  SetTitle("file browser");
140  SetPwd(std::filesystem::current_path());
141 
142  typeFilters_.clear();
143  typeFilterIndex_ = 0;
144 
145 #ifdef _WIN32
146  drives_ = GetDrivesBitMask();
147 #endif
148 }
149 
151  : FileBrowser()
152 {
153  *this = copyFrom;
154 }
155 
157 {
158  flags_ = copyFrom.flags_;
159  SetTitle(copyFrom.title_);
160 
161  openFlag_ = copyFrom.openFlag_;
162  closeFlag_ = copyFrom.closeFlag_;
163  isOpened_ = copyFrom.isOpened_;
164  ok_ = copyFrom.ok_;
165 
166  statusStr_ = "";
167  pwd_ = copyFrom.pwd_;
169 
170  fileRecords_ = copyFrom.fileRecords_;
171 
172  *inputNameBuf_ = *copyFrom.inputNameBuf_;
173 
175  {
176  newDirNameBuf_ = std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>();
177  *newDirNameBuf_ = *copyFrom.newDirNameBuf_;
178  }
179 
180  return *this;
181 }
182 
183 inline void ImGui::FileBrowser::SetTitle(std::string title)
184 {
185  title_ = std::move(title);
186  openLabel_ = title_ + "##filebrowser_" + std::to_string(reinterpret_cast<size_t>(this));
187  openNewDirLabel_ = "new dir##new_dir_" + std::to_string(reinterpret_cast<size_t>(this));
188 }
189 
191 {
192  ClearSelected();
193  statusStr_ = std::string();
194  openFlag_ = true;
195  closeFlag_ = false;
196 #ifdef _WIN32
197  GetDrivesBitMask();
198 #endif
199 }
200 
202 {
203  ClearSelected();
204  statusStr_ = std::string();
205  closeFlag_ = true;
206  openFlag_ = false;
207 }
208 
209 inline bool ImGui::FileBrowser::IsOpened() const noexcept
210 {
211  return isOpened_;
212 }
213 
215 {
216  PushID(this);
217  ScopeGuard exitThis([this] { openFlag_ = false; closeFlag_ = false; PopID(); });
218 
219  if(openFlag_)
220  OpenPopup(openLabel_.c_str());
221  isOpened_ = false;
222 
223  // open the popup window
224 
226  SetNextWindowSize(ImVec2(700, 450));
227  else
228  SetNextWindowSize(ImVec2(700, 450), ImGuiCond_FirstUseEver);
229  if(flags_ & ImGuiFileBrowserFlags_NoModal)
230  {
231  if(!BeginPopup(openLabel_.c_str()))
232  return;
233  }
234  else if(!BeginPopupModal(openLabel_.c_str(), nullptr,
235  flags_ & ImGuiFileBrowserFlags_NoTitleBar ? ImGuiWindowFlags_NoTitleBar : 0))
236  {
237  return;
238  }
239  isOpened_ = true;
240  ScopeGuard endPopup([] { EndPopup(); });
241 
242  // display elements in pwd
243 
244 #ifdef _WIN32
245  char currentDrive = static_cast<char>(pwd_.c_str()[0]);
246  char driveStr[] = { currentDrive, ':', '\0' };
247 
248  PushItemWidth(4 * GetFontSize());
249  if(BeginCombo("##SelectDrive", driveStr))
250  {
251  ScopeGuard guard([&] { ImGui::EndCombo(); });
252  for(int i = 0; i < 26; ++i)
253  {
254  if(!(drives_ & (1 << i)))
255  continue;
256  char driveCh = static_cast<char>('A' + i);
257  char selectableStr[] = { driveCh, ':', '\0' };
258  bool selected = currentDrive == driveCh;
259  if(Selectable(selectableStr, selected) && !selected)
260  {
261  char newPwd[] = { driveCh, ':', '\\', '\0' };
262  SetPwd(newPwd);
263  }
264  }
265  }
266  PopItemWidth();
267 
268  SameLine();
269 #endif
270 
271  int secIdx = 0, newPwdLastSecIdx = -1;
272  for(auto &sec : pwd_)
273  {
274 #ifdef _WIN32
275  if(secIdx == 1)
276  {
277  ++secIdx;
278  continue;
279  }
280 #endif
281  PushID(secIdx);
282  if(secIdx > 0)
283  SameLine();
284  if(SmallButton(sec.u8string().c_str()))
285  newPwdLastSecIdx = secIdx;
286  PopID();
287  ++secIdx;
288  }
289 
290  if(newPwdLastSecIdx >= 0)
291  {
292  int i = 0;
293  std::filesystem::path newPwd;
294  for(auto &sec : pwd_)
295  {
296  if(i++ > newPwdLastSecIdx)
297  break;
298  newPwd /= sec;
299  }
300 #ifdef _WIN32
301  if(newPwdLastSecIdx == 0)
302  newPwd /= "\\";
303 #endif
304  SetPwd(newPwd);
305  }
306 
307  SameLine();
308 
309  if(SmallButton("*"))
310  SetPwd(pwd_);
311 
312  if(newDirNameBuf_)
313  {
314  SameLine();
315  if(SmallButton("+"))
316  {
317  OpenPopup(openNewDirLabel_.c_str());
318  (*newDirNameBuf_)[0] = '\0';
319  }
320 
321  if(BeginPopup(openNewDirLabel_.c_str()))
322  {
323  ScopeGuard endNewDirPopup([] { EndPopup(); });
324 
325  InputText("name", newDirNameBuf_->data(), newDirNameBuf_->size()); SameLine();
326  if(Button("ok") && (*newDirNameBuf_)[0] != '\0')
327  {
328  ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); });
329  if(create_directory(pwd_ / newDirNameBuf_->data()))
330  SetPwd(pwd_);
331  else
332  statusStr_ = "failed to create " + std::string(newDirNameBuf_->data());
333  }
334  }
335  }
336 
337  // browse files in a child window
338 
339  float reserveHeight = GetItemsLineHeightWithSpacing();
340  std::filesystem::path newPwd; bool setNewPwd = false;
342  reserveHeight += GetItemsLineHeightWithSpacing();
343  {
344  BeginChild("ch", ImVec2(0, -reserveHeight), true,
345  (flags_ & ImGuiFileBrowserFlags_NoModal) ? ImGuiWindowFlags_AlwaysHorizontalScrollbar : 0);
346  ScopeGuard endChild([] { EndChild(); });
347 
348  for(auto &rsc : fileRecords_)
349  {
350  if (!rsc.isDir && typeFilters_.size() > 0 &&
351  static_cast<size_t>(typeFilterIndex_) < typeFilters_.size() &&
352  !(rsc.extension == typeFilters_[typeFilterIndex_]))
353  continue;
354 
355  if(!rsc.name.empty() && rsc.name[0] == '$')
356  continue;
357 
358  const bool selected = selectedFilename_ == rsc.name;
359  if(Selectable(rsc.showName.c_str(), selected, ImGuiSelectableFlags_DontClosePopups))
360  {
361  if(selected)
362  {
363  selectedFilename_ = std::string();
364  (*inputNameBuf_)[0] = '\0';
365  }
366  else if(rsc.name != "..")
367  {
368  if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) ||
369  (!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory)))
370  {
371  selectedFilename_ = rsc.name;
372  if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
373  std::strcpy(inputNameBuf_->data(), selectedFilename_.c_str());
374  }
375  }
376  }
377 
378  if(IsItemClicked(0) && IsMouseDoubleClicked(0) && rsc.isDir)
379  {
380  setNewPwd = true;
381  newPwd = (rsc.name != "..") ? (pwd_ / rsc.name) : pwd_.parent_path();
382  }
383  }
384  }
385 
386  if(setNewPwd)
387  SetPwd(newPwd);
388 
389  if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) && (flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
390  {
391  PushID(this);
392  ScopeGuard popTextID([] { PopID(); });
393 
394  PushItemWidth(-1);
395  if(InputText("", inputNameBuf_->data(), inputNameBuf_->size()))
397  PopItemWidth();
398  }
399 
400  if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
401  {
402  if(Button(" ok ") && !selectedFilename_.empty())
403  {
404  ok_ = true;
405  CloseCurrentPopup();
406  }
407  }
408  else
409  {
410  if(selectedFilename_.empty())
411  {
412  if(Button(" ok "))
413  {
414  ok_ = true;
415  CloseCurrentPopup();
416  }
417  }
418  else if(Button("open"))
419  SetPwd(pwd_ / selectedFilename_);
420  }
421 
422  SameLine();
423 
424  int escIdx = GetIO().KeyMap[ImGuiKey_Escape];
425  if(Button("cancel") || closeFlag_ ||
426  ((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) && IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && escIdx >= 0 && IsKeyPressed(escIdx)))
427  CloseCurrentPopup();
428 
430  {
431  SameLine();
432  Text("%s", statusStr_.c_str());
433  }
434 
435  if (!typeFilters_.empty())
436  {
437  SameLine();
438  PushItemWidth(8 * GetFontSize());
439  Combo("##Filters", &typeFilterIndex_, typeFilters_.data(), int(typeFilters_.size()));
440  PopItemWidth();
441  }
442 }
443 
444 inline bool ImGui::FileBrowser::HasSelected() const noexcept
445 {
446  return ok_;
447 }
448 
449 inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd)
450 {
451  try
452  {
453  SetPwdUncatched(pwd);
454  return true;
455  }
456  catch(const std::exception &err)
457  {
458  statusStr_ = std::string("last error: ") + err.what();
459  }
460  catch(...)
461  {
462  statusStr_ = "last error: unknown";
463  }
464 
465  SetPwdUncatched(std::filesystem::current_path());
466  return false;
467 }
468 
469 inline std::filesystem::path ImGui::FileBrowser::GetSelected() const
470 {
471  return pwd_ / selectedFilename_;
472 }
473 
475 {
476  selectedFilename_ = std::string();
477  (*inputNameBuf_)[0] = '\0';
478  ok_ = false;
479 }
480 
481 inline void ImGui::FileBrowser::SetTypeFilters(const std::vector<const char*> &typeFilters)
482 {
483  typeFilters_ = typeFilters;
484  typeFilterIndex_ = 0;
485 }
486 
487 inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd)
488 {
489  fileRecords_ = { FileRecord{ true, "..", "[D] .." } };
490 
491  for(auto &p : std::filesystem::directory_iterator(pwd))
492  {
493  FileRecord rcd;
494 
495  if(p.is_regular_file())
496  rcd.isDir = false;
497  else if(p.is_directory())
498  rcd.isDir = true;
499  else
500  continue;
501 
502  rcd.name = p.path().filename().string();
503  if(rcd.name.empty())
504  continue;
505 
506  rcd.extension = p.path().filename().extension().string();
507 
508  rcd.showName = (rcd.isDir ? "[D] " : "[F] ") + p.path().filename().u8string();
509  fileRecords_.push_back(rcd);
510  }
511 
512  std::sort(fileRecords_.begin(), fileRecords_.end(),
513  [](const FileRecord &L, const FileRecord &R)
514  {
515  return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name);
516  });
517 
518  pwd_ = absolute(pwd);
519  selectedFilename_ = std::string();
520  (*inputNameBuf_)[0] = '\0';
521 }
522 
523 #ifdef _WIN32
524 
525 inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask()
526 {
527  char rootName[10];
528  DWORD mask = GetLogicalDrives();
529  uint32_t ret = 0;
530  for(int i = 0; i < 26; ++i)
531  {
532  if(!(mask & (1 << i)))
533  continue;
534  sprintf(rootName, "%c:\\", 'A' + i);
535  UINT type = GetDriveTypeA(rootName);
536  if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED)
537  ret |= (1 << i);
538  }
539  return ret;
540 }
541 
542 #endif
Definition: ImFileBrowser.hpp:24
Definition: ImFileBrowser.hpp:108
std::vector< const char * > typeFilters_
Definition: ImFileBrowser.hpp:102
Definition: ImFileBrowser.hpp:26
std::string name
Definition: ImFileBrowser.hpp:111
void SetTitle(std::string title)
Definition: ImFileBrowser.hpp:183
std::string openLabel_
Definition: ImFileBrowser.hpp:93
std::string statusStr_
Definition: ImFileBrowser.hpp:100
Definition: ImFileBrowser.hpp:25
int typeFilterIndex_
Definition: ImFileBrowser.hpp:103
bool SetPwd(const std::filesystem::path &pwd=std::filesystem::current_path())
Definition: ImFileBrowser.hpp:449
Definition: Vertex.h:74
ImGuiFileBrowserFlags_
Definition: ImFileBrowser.hpp:18
std::string extension
Definition: ImFileBrowser.hpp:113
Definition: ImFileBrowser.hpp:20
Definition: ImFileBrowser.hpp:31
~ScopeGuard()
Definition: ImFileBrowser.hpp:81
FileBrowser & operator=(const FileBrowser &copyFrom)
Definition: ImFileBrowser.hpp:156
std::string title_
Definition: ImFileBrowser.hpp:92
bool isDir
Definition: ImFileBrowser.hpp:110
ScopeGuard(T func)
Definition: ImFileBrowser.hpp:80
bool closeFlag_
Definition: ImFileBrowser.hpp:96
Definition: ImFileBrowser.hpp:23
std::unique_ptr< std::array< char, INPUT_NAME_BUF_SIZE > > inputNameBuf_
Definition: ImFileBrowser.hpp:119
Definition: ImFileBrowser.hpp:22
void SetTypeFilters(const std::vector< const char *> &typeFilters)
Definition: ImFileBrowser.hpp:481
void Display()
Definition: ImFileBrowser.hpp:214
static constexpr size_t INPUT_NAME_BUF_SIZE
Definition: ImFileBrowser.hpp:118
void Close()
Definition: ImFileBrowser.hpp:201
std::filesystem::path pwd_
Definition: ImFileBrowser.hpp:105
std::function< void()> func_
Definition: ImFileBrowser.hpp:75
std::string openNewDirLabel_
Definition: ImFileBrowser.hpp:121
void SetPwdUncatched(const std::filesystem::path &pwd)
Definition: ImFileBrowser.hpp:487
std::string selectedFilename_
Definition: ImFileBrowser.hpp:106
std::filesystem::path GetSelected() const
Definition: ImFileBrowser.hpp:469
Definition: ImFileBrowser.hpp:29
ImGuiFileBrowserFlags flags_
Definition: ImFileBrowser.hpp:90
Definition: ImFileBrowser.hpp:73
int ImGuiFileBrowserFlags
Definition: ImFileBrowser.hpp:16
std::vector< FileRecord > fileRecords_
Definition: ImFileBrowser.hpp:115
bool HasSelected() const noexcept
Definition: ImFileBrowser.hpp:444
void Open()
Definition: ImFileBrowser.hpp:190
Definition: ImFileBrowser.hpp:21
bool IsOpened() const noexcept
Definition: ImFileBrowser.hpp:209
std::string showName
Definition: ImFileBrowser.hpp:112
bool openFlag_
Definition: ImFileBrowser.hpp:95
bool isOpened_
Definition: ImFileBrowser.hpp:97
bool ok_
Definition: ImFileBrowser.hpp:98
FileBrowser(ImGuiFileBrowserFlags flags=0)
Definition: ImFileBrowser.hpp:130
std::unique_ptr< std::array< char, INPUT_NAME_BUF_SIZE > > newDirNameBuf_
Definition: ImFileBrowser.hpp:122
void ClearSelected()
Definition: ImFileBrowser.hpp:474