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