imfilebrowser.h 25 KB

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