imfilebrowser.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. #pragma once
  2. #include <algorithm>
  3. #include <array>
  4. #include <cctype>
  5. #include <cstring>
  6. #include <filesystem>
  7. #include <memory>
  8. #include <set>
  9. #include <string>
  10. #include <string_view>
  11. #include <vector>
  12. #ifndef IMGUI_VERSION
  13. # error "include imgui.h before this header"
  14. #endif
  15. using ImGuiFileBrowserFlags = int;
  16. enum ImGuiFileBrowserFlags_
  17. {
  18. ImGuiFileBrowserFlags_SelectDirectory = 1 << 0, // select directory instead of regular file
  19. ImGuiFileBrowserFlags_EnterNewFilename = 1 << 1, // allow user to enter new filename when selecting regular file
  20. ImGuiFileBrowserFlags_NoModal = 1 << 2, // file browsing window is modal by default. specify this to use a popup window
  21. ImGuiFileBrowserFlags_NoTitleBar = 1 << 3, // hide window title bar
  22. ImGuiFileBrowserFlags_NoStatusBar = 1 << 4, // hide status bar at the bottom of browsing window
  23. ImGuiFileBrowserFlags_CloseOnEsc = 1 << 5, // close file browser when pressing 'ESC'
  24. ImGuiFileBrowserFlags_CreateNewDir = 1 << 6, // allow user to create new directory
  25. ImGuiFileBrowserFlags_MultipleSelection = 1 << 7, // allow user to select multiple files. this will hide ImGuiFileBrowserFlags_EnterNewFilename
  26. };
  27. namespace ImGui
  28. {
  29. class FileBrowser
  30. {
  31. public:
  32. // pwd is set to current working directory by default
  33. explicit FileBrowser(ImGuiFileBrowserFlags flags = 0);
  34. FileBrowser(const FileBrowser &copyFrom);
  35. FileBrowser &operator=(const FileBrowser &copyFrom);
  36. // set the window position (in pixels)
  37. // default is centered
  38. void SetWindowPos(int posx, int posy) noexcept;
  39. // set the window size (in pixels)
  40. // default is (700, 450)
  41. void SetWindowSize(int width, int height) noexcept;
  42. // set the window title text
  43. void SetTitle(std::string title);
  44. std::string getTitle();
  45. // open the browsing window
  46. void Open();
  47. // close the browsing window
  48. void Close();
  49. // the browsing window is opened or not
  50. bool IsOpened() const noexcept;
  51. // display the browsing window if opened
  52. void Display();
  53. // returns true when there is a selected filename and the "ok" button was clicked
  54. bool HasSelected() const noexcept;
  55. // set current browsing directory
  56. bool SetPwd(const std::filesystem::path &pwd =
  57. std::filesystem::current_path());
  58. // get current browsing directory
  59. const std::filesystem::path &GetPwd() const noexcept;
  60. // returns selected filename. make sense only when HasSelected returns true
  61. // when ImGuiFileBrowserFlags_MultipleSelection is enabled, only one of
  62. // selected filename will be returned
  63. std::filesystem::path GetSelected() const;
  64. // returns all selected filenames.
  65. // when ImGuiFileBrowserFlags_MultipleSelection is enabled, use this
  66. // instead of GetSelected
  67. std::vector<std::filesystem::path> GetMultiSelected() const;
  68. // set selected filename to empty
  69. void ClearSelected();
  70. // (optional) set file type filters. eg. { ".h", ".cpp", ".hpp" }
  71. // ".*" matches any file types
  72. void SetTypeFilters(const std::vector<std::string> &typeFilters);
  73. // set currently applied type filter
  74. // default value is 0 (the first type filter)
  75. void SetCurrentTypeFilterIndex(int index);
  76. // when ImGuiFileBrowserFlags_EnterNewFilename is set
  77. // this function will pre-fill the input dialog with a filename.
  78. void SetInputName(std::string_view input);
  79. private:
  80. template <class Functor>
  81. struct ScopeGuard
  82. {
  83. ScopeGuard(Functor&& t) : func(std::move(t)) { }
  84. ~ScopeGuard() { func(); }
  85. private:
  86. Functor func;
  87. };
  88. struct FileRecord
  89. {
  90. bool isDir = false;
  91. std::filesystem::path name;
  92. std::string showName;
  93. std::filesystem::path extension;
  94. };
  95. static std::string ToLower(const std::string &s);
  96. void UpdateFileRecords();
  97. void SetPwdUncatched(const std::filesystem::path &pwd);
  98. bool IsExtensionMatched(const std::filesystem::path &extension) const;
  99. #ifdef _WIN32
  100. static std::uint32_t GetDrivesBitMask();
  101. #endif
  102. // for c++17 compatibility
  103. #if defined(__cpp_lib_char8_t)
  104. static std::string u8StrToStr(std::u8string s);
  105. #endif
  106. static std::string u8StrToStr(std::string s);
  107. int width_;
  108. int height_;
  109. int posX_;
  110. int posY_;
  111. ImGuiFileBrowserFlags flags_;
  112. std::string title_;
  113. std::string openLabel_;
  114. bool openFlag_;
  115. bool closeFlag_;
  116. bool isOpened_;
  117. bool ok_;
  118. bool posIsSet_;
  119. std::string statusStr_;
  120. std::vector<std::string> typeFilters_;
  121. unsigned int typeFilterIndex_;
  122. bool hasAllFilter_;
  123. std::filesystem::path pwd_;
  124. std::set<std::filesystem::path> selectedFilenames_;
  125. std::vector<FileRecord> fileRecords_;
  126. // IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1
  127. static constexpr size_t INPUT_NAME_BUF_SIZE = 512;
  128. std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> inputNameBuf_;
  129. std::string openNewDirLabel_;
  130. std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> newDirNameBuf_;
  131. #ifdef _WIN32
  132. uint32_t drives_;
  133. #endif
  134. };
  135. }; // namespace ImGui
  136. inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags)
  137. : width_(700), height_(450), posX_(0), posY_(0), flags_(flags),
  138. openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false), posIsSet_(false),
  139. inputNameBuf_(std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>())
  140. {
  141. if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
  142. {
  143. newDirNameBuf_ =
  144. std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>();
  145. }
  146. inputNameBuf_->front() = '\0';
  147. inputNameBuf_->back() = '\0';
  148. SetTitle("file browser");
  149. SetPwd(std::filesystem::current_path());
  150. typeFilters_.clear();
  151. typeFilterIndex_ = 0;
  152. hasAllFilter_ = false;
  153. #ifdef _WIN32
  154. drives_ = GetDrivesBitMask();
  155. #endif
  156. }
  157. inline ImGui::FileBrowser::FileBrowser(const FileBrowser &copyFrom)
  158. : FileBrowser()
  159. {
  160. *this = copyFrom;
  161. }
  162. inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(
  163. const FileBrowser &copyFrom)
  164. {
  165. width_ = copyFrom.width_;
  166. height_ = copyFrom.height_;
  167. posX_ = copyFrom.posX_;
  168. posY_ = copyFrom.posY_;
  169. flags_ = copyFrom.flags_;
  170. SetTitle(copyFrom.title_);
  171. openFlag_ = copyFrom.openFlag_;
  172. closeFlag_ = copyFrom.closeFlag_;
  173. isOpened_ = copyFrom.isOpened_;
  174. ok_ = copyFrom.ok_;
  175. posIsSet_ = copyFrom.posIsSet_;
  176. statusStr_ = "";
  177. typeFilters_ = copyFrom.typeFilters_;
  178. typeFilterIndex_ = copyFrom.typeFilterIndex_;
  179. hasAllFilter_ = copyFrom.hasAllFilter_;
  180. pwd_ = copyFrom.pwd_;
  181. selectedFilenames_ = copyFrom.selectedFilenames_;
  182. fileRecords_ = copyFrom.fileRecords_;
  183. *inputNameBuf_ = *copyFrom.inputNameBuf_;
  184. openNewDirLabel_ = copyFrom.openNewDirLabel_;
  185. if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
  186. {
  187. newDirNameBuf_ = std::make_unique<
  188. std::array<char, INPUT_NAME_BUF_SIZE>>();
  189. *newDirNameBuf_ = *copyFrom.newDirNameBuf_;
  190. }
  191. #ifdef _WIN32
  192. drives_ = copyFrom.drives_;
  193. #endif
  194. return *this;
  195. }
  196. inline void ImGui::FileBrowser::SetWindowPos(int posx, int posy) noexcept
  197. {
  198. posX_ = posx;
  199. posY_ = posy;
  200. posIsSet_ = true;
  201. }
  202. inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept
  203. {
  204. assert(width > 0 && height > 0);
  205. width_ = width;
  206. height_ = height;
  207. }
  208. inline void ImGui::FileBrowser::SetTitle(std::string title)
  209. {
  210. title_ = std::move(title);
  211. openLabel_ = title_ + "##filebrowser_" +
  212. std::to_string(reinterpret_cast<size_t>(this));
  213. openNewDirLabel_ = "new dir##new_dir_" +
  214. std::to_string(reinterpret_cast<size_t>(this));
  215. }
  216. inline std::string ImGui::FileBrowser::getTitle() { return this->title_; }
  217. inline void ImGui::FileBrowser::Open()
  218. {
  219. ClearSelected();
  220. UpdateFileRecords();
  221. statusStr_ = std::string();
  222. openFlag_ = true;
  223. closeFlag_ = false;
  224. }
  225. inline void ImGui::FileBrowser::Close()
  226. {
  227. ClearSelected();
  228. statusStr_ = std::string();
  229. closeFlag_ = true;
  230. openFlag_ = false;
  231. }
  232. inline bool ImGui::FileBrowser::IsOpened() const noexcept
  233. {
  234. return isOpened_;
  235. }
  236. inline void ImGui::FileBrowser::Display()
  237. {
  238. PushID(this);
  239. ScopeGuard exitThis([this]
  240. {
  241. openFlag_ = false;
  242. closeFlag_ = false;
  243. PopID();
  244. });
  245. if(openFlag_)
  246. {
  247. OpenPopup(openLabel_.c_str());
  248. }
  249. isOpened_ = false;
  250. // open the popup window
  251. if(openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal))
  252. {
  253. if (posIsSet_)
  254. SetNextWindowPos(
  255. ImVec2(static_cast<float>(posX_), static_cast<float>(posY_)));
  256. SetNextWindowSize(
  257. ImVec2(static_cast<float>(width_), static_cast<float>(height_)));
  258. }
  259. else
  260. {
  261. if (posIsSet_)
  262. SetNextWindowPos(
  263. ImVec2(static_cast<float>(posX_), static_cast<float>(posY_)),
  264. ImGuiCond_FirstUseEver);
  265. SetNextWindowSize(
  266. ImVec2(static_cast<float>(width_), static_cast<float>(height_)),
  267. ImGuiCond_FirstUseEver);
  268. }
  269. if(flags_ & ImGuiFileBrowserFlags_NoModal)
  270. {
  271. if(!BeginPopup(openLabel_.c_str()))
  272. {
  273. return;
  274. }
  275. }
  276. else if(!BeginPopupModal(openLabel_.c_str(), nullptr,
  277. flags_ & ImGuiFileBrowserFlags_NoTitleBar ?
  278. ImGuiWindowFlags_NoTitleBar : 0))
  279. {
  280. return;
  281. }
  282. isOpened_ = true;
  283. ScopeGuard endPopup([] { EndPopup(); });
  284. // display elements in pwd
  285. #ifdef _WIN32
  286. char currentDrive = static_cast<char>(pwd_.c_str()[0]);
  287. char driveStr[] = { currentDrive, ':', '\0' };
  288. PushItemWidth(4 * GetFontSize());
  289. if(BeginCombo("##select_drive", driveStr))
  290. {
  291. ScopeGuard guard([&] { EndCombo(); });
  292. for(int i = 0; i < 26; ++i)
  293. {
  294. if(!(drives_ & (1 << i)))
  295. {
  296. continue;
  297. }
  298. char driveCh = static_cast<char>('A' + i);
  299. char selectableStr[] = { driveCh, ':', '\0' };
  300. bool selected = currentDrive == driveCh;
  301. if(Selectable(selectableStr, selected) && !selected)
  302. {
  303. char newPwd[] = { driveCh, ':', '\\', '\0' };
  304. SetPwd(newPwd);
  305. }
  306. }
  307. }
  308. PopItemWidth();
  309. SameLine();
  310. #endif
  311. int secIdx = 0, newPwdLastSecIdx = -1;
  312. for(const auto &sec : pwd_)
  313. {
  314. #ifdef _WIN32
  315. if(secIdx == 1)
  316. {
  317. ++secIdx;
  318. continue;
  319. }
  320. #endif
  321. PushID(secIdx);
  322. if(secIdx > 0)
  323. {
  324. SameLine();
  325. }
  326. if(SmallButton(u8StrToStr(sec.u8string()).c_str()))
  327. {
  328. newPwdLastSecIdx = secIdx;
  329. }
  330. PopID();
  331. ++secIdx;
  332. }
  333. if(newPwdLastSecIdx >= 0)
  334. {
  335. int i = 0;
  336. std::filesystem::path newPwd;
  337. for(const auto &sec : pwd_)
  338. {
  339. if(i++ > newPwdLastSecIdx)
  340. {
  341. break;
  342. }
  343. newPwd /= sec;
  344. }
  345. #ifdef _WIN32
  346. if(newPwdLastSecIdx == 0)
  347. {
  348. newPwd /= "\\";
  349. }
  350. #endif
  351. SetPwd(newPwd);
  352. }
  353. SameLine();
  354. if(SmallButton("*"))
  355. {
  356. UpdateFileRecords();
  357. std::set<std::filesystem::path> newSelectedFilenames;
  358. for(auto &name : selectedFilenames_)
  359. {
  360. auto it = std::find_if(
  361. fileRecords_.begin(), fileRecords_.end(),
  362. [&](const FileRecord &record)
  363. {
  364. return name == record.name;
  365. });
  366. if(it != fileRecords_.end())
  367. {
  368. newSelectedFilenames.insert(name);
  369. }
  370. }
  371. if(inputNameBuf_ && (*inputNameBuf_)[0])
  372. {
  373. newSelectedFilenames.insert(inputNameBuf_->data());
  374. }
  375. }
  376. if(newDirNameBuf_)
  377. {
  378. SameLine();
  379. if(SmallButton("+"))
  380. {
  381. OpenPopup(openNewDirLabel_.c_str());
  382. (*newDirNameBuf_)[0] = '\0';
  383. }
  384. if(BeginPopup(openNewDirLabel_.c_str()))
  385. {
  386. ScopeGuard endNewDirPopup([] { EndPopup(); });
  387. InputText("name", newDirNameBuf_->data(), newDirNameBuf_->size());
  388. SameLine();
  389. if(Button("ok") && (*newDirNameBuf_)[0] != '\0')
  390. {
  391. ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); });
  392. if(create_directory(pwd_ / newDirNameBuf_->data()))
  393. {
  394. UpdateFileRecords();
  395. }
  396. else
  397. {
  398. statusStr_ = "failed to create " +
  399. std::string(newDirNameBuf_->data());
  400. }
  401. }
  402. }
  403. }
  404. // browse files in a child window
  405. float reserveHeight = GetFrameHeightWithSpacing();
  406. std::filesystem::path newPwd; bool setNewPwd = false;
  407. if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
  408. (flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
  409. reserveHeight += GetFrameHeightWithSpacing();
  410. {
  411. BeginChild("ch", ImVec2(0, -reserveHeight), true,
  412. (flags_ & ImGuiFileBrowserFlags_NoModal) ?
  413. ImGuiWindowFlags_AlwaysHorizontalScrollbar : 0);
  414. ScopeGuard endChild([] { EndChild(); });
  415. for(auto &rsc : fileRecords_)
  416. {
  417. if(!rsc.isDir && !IsExtensionMatched(rsc.extension))
  418. {
  419. continue;
  420. }
  421. if(!rsc.name.empty() && rsc.name.c_str()[0] == '$')
  422. {
  423. continue;
  424. }
  425. bool selected = selectedFilenames_.find(rsc.name)
  426. != selectedFilenames_.end();
  427. if(Selectable(rsc.showName.c_str(), selected,
  428. ImGuiSelectableFlags_DontClosePopups))
  429. {
  430. const bool multiSelect =
  431. (flags_ & ImGuiFileBrowserFlags_MultipleSelection) &&
  432. IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
  433. (GetIO().KeyCtrl || GetIO().KeyShift);
  434. if(selected)
  435. {
  436. if(!multiSelect)
  437. {
  438. selectedFilenames_.clear();
  439. }
  440. else
  441. {
  442. selectedFilenames_.erase(rsc.name);
  443. }
  444. (*inputNameBuf_)[0] = '\0';
  445. }
  446. else if(rsc.name != "..")
  447. {
  448. if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) ||
  449. (!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory)))
  450. {
  451. if(multiSelect)
  452. {
  453. selectedFilenames_.insert(rsc.name);
  454. }
  455. else
  456. {
  457. selectedFilenames_ = { rsc.name };
  458. }
  459. if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
  460. {
  461. #ifdef _MSC_VER
  462. strcpy_s(
  463. inputNameBuf_->data(), inputNameBuf_->size(),
  464. u8StrToStr(rsc.name.u8string()).c_str());
  465. #else
  466. std::strncpy(inputNameBuf_->data(),
  467. u8StrToStr(rsc.name.u8string()).c_str(),
  468. inputNameBuf_->size() - 1);
  469. #endif
  470. }
  471. }
  472. }
  473. else
  474. {
  475. if(!multiSelect)
  476. {
  477. selectedFilenames_.clear();
  478. }
  479. }
  480. }
  481. if(IsItemClicked(0) && IsMouseDoubleClicked(0))
  482. {
  483. if(rsc.isDir)
  484. {
  485. setNewPwd = true;
  486. newPwd = (rsc.name != "..") ? (pwd_ / rsc.name) :
  487. pwd_.parent_path();
  488. }
  489. else if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
  490. {
  491. selectedFilenames_ = { rsc.name };
  492. ok_ = true;
  493. CloseCurrentPopup();
  494. }
  495. }
  496. }
  497. }
  498. if(setNewPwd)
  499. {
  500. SetPwd(newPwd);
  501. }
  502. if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
  503. (flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
  504. {
  505. PushID(this);
  506. ScopeGuard popTextID([] { PopID(); });
  507. PushItemWidth(-1);
  508. if(InputText("", inputNameBuf_->data(), inputNameBuf_->size()) &&
  509. inputNameBuf_->at(0) != '\0')
  510. {
  511. selectedFilenames_ = { inputNameBuf_->data() };
  512. }
  513. PopItemWidth();
  514. }
  515. if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
  516. {
  517. if(Button(" ok ") && !selectedFilenames_.empty())
  518. {
  519. ok_ = true;
  520. CloseCurrentPopup();
  521. }
  522. }
  523. else
  524. {
  525. if(Button(" ok "))
  526. {
  527. ok_ = true;
  528. CloseCurrentPopup();
  529. }
  530. }
  531. SameLine();
  532. bool shouldExit =
  533. Button("cancel") || closeFlag_ ||
  534. ((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) &&
  535. IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
  536. IsKeyPressed(ImGuiKey_Escape));
  537. if(shouldExit)
  538. {
  539. CloseCurrentPopup();
  540. }
  541. if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar))
  542. {
  543. SameLine();
  544. Text("%s", statusStr_.c_str());
  545. }
  546. if(!typeFilters_.empty())
  547. {
  548. SameLine();
  549. PushItemWidth(8 * GetFontSize());
  550. if(BeginCombo(
  551. "##type_filters", typeFilters_[typeFilterIndex_].c_str()))
  552. {
  553. ScopeGuard guard([&] { EndCombo(); });
  554. for(size_t i = 0; i < typeFilters_.size(); ++i)
  555. {
  556. bool selected = i == typeFilterIndex_;
  557. if(Selectable(typeFilters_[i].c_str(), selected) && !selected)
  558. {
  559. typeFilterIndex_ = static_cast<unsigned int>(i);
  560. }
  561. }
  562. }
  563. PopItemWidth();
  564. }
  565. }
  566. inline bool ImGui::FileBrowser::HasSelected() const noexcept
  567. {
  568. return ok_;
  569. }
  570. inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd)
  571. {
  572. try
  573. {
  574. SetPwdUncatched(pwd);
  575. return true;
  576. }
  577. catch(const std::exception &err)
  578. {
  579. statusStr_ = std::string("last error: ") + err.what();
  580. }
  581. catch(...)
  582. {
  583. statusStr_ = "last error: unknown";
  584. }
  585. SetPwdUncatched(std::filesystem::current_path());
  586. return false;
  587. }
  588. inline const class std::filesystem::path &ImGui::FileBrowser::GetPwd() const noexcept
  589. {
  590. return pwd_;
  591. }
  592. inline std::filesystem::path ImGui::FileBrowser::GetSelected() const
  593. {
  594. // when ok_ is true, selectedFilenames_ may be empty if SelectDirectory
  595. // is enabled. return pwd in that case.
  596. if(selectedFilenames_.empty())
  597. {
  598. return pwd_;
  599. }
  600. return pwd_ / *selectedFilenames_.begin();
  601. }
  602. inline std::vector<std::filesystem::path>
  603. ImGui::FileBrowser::GetMultiSelected() const
  604. {
  605. if(selectedFilenames_.empty())
  606. {
  607. return { pwd_ };
  608. }
  609. std::vector<std::filesystem::path> ret;
  610. ret.reserve(selectedFilenames_.size());
  611. for(auto &s : selectedFilenames_)
  612. {
  613. ret.push_back(pwd_ / s);
  614. }
  615. return ret;
  616. }
  617. inline void ImGui::FileBrowser::ClearSelected()
  618. {
  619. selectedFilenames_.clear();
  620. (*inputNameBuf_)[0] = '\0';
  621. ok_ = false;
  622. }
  623. inline void ImGui::FileBrowser::SetTypeFilters(
  624. const std::vector<std::string> &_typeFilters)
  625. {
  626. typeFilters_.clear();
  627. // remove duplicate filter names due to case unsensitivity on windows
  628. #ifdef _WIN32
  629. std::vector<std::string> typeFilters;
  630. for(auto &rawFilter : _typeFilters)
  631. {
  632. std::string lowerFilter = ToLower(rawFilter);
  633. auto it = std::find(typeFilters.begin(), typeFilters.end(), lowerFilter);
  634. if(it == typeFilters.end())
  635. {
  636. typeFilters.push_back(std::move(lowerFilter));
  637. }
  638. }
  639. #else
  640. auto &typeFilters = _typeFilters;
  641. #endif
  642. // insert auto-generated filter
  643. if(typeFilters.size() > 1)
  644. {
  645. hasAllFilter_ = true;
  646. std::string allFiltersName = std::string();
  647. for(size_t i = 0; i < typeFilters.size(); ++i)
  648. {
  649. if(typeFilters[i] == std::string_view(".*"))
  650. {
  651. hasAllFilter_ = false;
  652. break;
  653. }
  654. if(i > 0)
  655. {
  656. allFiltersName += ",";
  657. }
  658. allFiltersName += typeFilters[i];
  659. }
  660. if(hasAllFilter_)
  661. {
  662. typeFilters_.push_back(std::move(allFiltersName));
  663. }
  664. }
  665. std::copy(
  666. typeFilters.begin(), typeFilters.end(),
  667. std::back_inserter(typeFilters_));
  668. typeFilterIndex_ = 0;
  669. }
  670. inline void ImGui::FileBrowser::SetCurrentTypeFilterIndex(int index)
  671. {
  672. typeFilterIndex_ = static_cast<unsigned int>(index);
  673. }
  674. inline void ImGui::FileBrowser::SetInputName(std::string_view input)
  675. {
  676. if(flags_ & ImGuiFileBrowserFlags_EnterNewFilename)
  677. {
  678. if(input.size() >= static_cast<size_t>(INPUT_NAME_BUF_SIZE))
  679. {
  680. // If input doesn't fit trim off characters
  681. input = input.substr(0, INPUT_NAME_BUF_SIZE - 1);
  682. }
  683. std::copy(input.begin(), input.end(), inputNameBuf_->begin());
  684. inputNameBuf_->at(input.size()) = '\0';
  685. selectedFilenames_ = { inputNameBuf_->data() };
  686. }
  687. }
  688. inline std::string ImGui::FileBrowser::ToLower(const std::string &s)
  689. {
  690. std::string ret = s;
  691. for(char &c : ret)
  692. {
  693. c = static_cast<char>(std::tolower(c));
  694. }
  695. return ret;
  696. }
  697. inline void ImGui::FileBrowser::UpdateFileRecords()
  698. {
  699. fileRecords_ = { FileRecord{ true, "..", "[D] ..", "" } };
  700. for(auto &p : std::filesystem::directory_iterator(pwd_))
  701. {
  702. FileRecord rcd;
  703. if(p.is_regular_file())
  704. {
  705. rcd.isDir = false;
  706. }
  707. else if(p.is_directory())
  708. {
  709. rcd.isDir = true;
  710. }
  711. else
  712. {
  713. continue;
  714. }
  715. rcd.name = p.path().filename();
  716. if(rcd.name.empty())
  717. {
  718. continue;
  719. }
  720. rcd.extension = p.path().filename().extension();
  721. rcd.showName = (rcd.isDir ? "[D] " : "[F] ") +
  722. u8StrToStr(p.path().filename().u8string());
  723. fileRecords_.push_back(rcd);
  724. }
  725. std::sort(fileRecords_.begin(), fileRecords_.end(),
  726. [](const FileRecord &L, const FileRecord &R)
  727. {
  728. return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name);
  729. });
  730. }
  731. inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd)
  732. {
  733. pwd_ = absolute(pwd);
  734. UpdateFileRecords();
  735. selectedFilenames_.clear();
  736. (*inputNameBuf_)[0] = '\0';
  737. }
  738. inline bool ImGui::FileBrowser::IsExtensionMatched(
  739. const std::filesystem::path &_extension) const
  740. {
  741. #ifdef _WIN32
  742. std::filesystem::path extension = ToLower(_extension.string());
  743. #else
  744. auto &extension = _extension;
  745. #endif
  746. // no type filters
  747. if(typeFilters_.empty())
  748. {
  749. return true;
  750. }
  751. // invalid type filter index
  752. if(static_cast<size_t>(typeFilterIndex_) >= typeFilters_.size())
  753. {
  754. return true;
  755. }
  756. // all type filters
  757. if(hasAllFilter_ && typeFilterIndex_ == 0)
  758. {
  759. for(size_t i = 1; i < typeFilters_.size(); ++i)
  760. {
  761. if(extension == typeFilters_[i])
  762. {
  763. return true;
  764. }
  765. }
  766. return false;
  767. }
  768. // universal filter
  769. if(typeFilters_[typeFilterIndex_] == std::string_view(".*"))
  770. {
  771. return true;
  772. }
  773. // regular filter
  774. return extension == typeFilters_[typeFilterIndex_];
  775. }
  776. #if defined(__cpp_lib_char8_t)
  777. inline std::string ImGui::FileBrowser::u8StrToStr(std::u8string s)
  778. {
  779. return std::string(s.begin(), s.end());
  780. }
  781. #endif
  782. inline std::string ImGui::FileBrowser::u8StrToStr(std::string s)
  783. {
  784. return s;
  785. }
  786. #ifdef _WIN32
  787. #ifndef _INC_WINDOWS
  788. #ifndef WIN32_LEAN_AND_MEAN
  789. #define IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
  790. //#define WIN32_LEAN_AND_MEAN
  791. #endif // #ifndef WIN32_LEAN_AND_MEAN
  792. #define NOMINMAX
  793. #include <windows.h>
  794. #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
  795. #undef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
  796. #undef WIN32_LEAN_AND_MEAN
  797. #endif // #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
  798. #endif // #ifdef _INC_WINDOWS
  799. inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask()
  800. {
  801. DWORD mask = GetLogicalDrives();
  802. uint32_t ret = 0;
  803. for(int i = 0; i < 26; ++i)
  804. {
  805. if(!(mask & (1 << i)))
  806. {
  807. continue;
  808. }
  809. char rootName[4] = { static_cast<char>('A' + i), ':', '\\', '\0' };
  810. UINT type = GetDriveTypeA(rootName);
  811. if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED || type == DRIVE_REMOTE)
  812. {
  813. ret |= (1 << i);
  814. }
  815. }
  816. return ret;
  817. }
  818. #endif