diff --git a/imfilebrowser.h b/imfilebrowser.h index ff7e42c..4ea184c 100644 --- a/imfilebrowser.h +++ b/imfilebrowser.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -8,811 +8,747 @@ #include #include #include +#include #ifndef IMGUI_VERSION -# error "include imgui.h before this header" + #error "include imgui.h before this header" #endif using ImGuiFileBrowserFlags = int; -enum ImGuiFileBrowserFlags_ -{ - ImGuiFileBrowserFlags_SelectDirectory = 1 << 0, // select directory instead of regular file - ImGuiFileBrowserFlags_EnterNewFilename = 1 << 1, // allow user to enter new filename when selecting regular file - ImGuiFileBrowserFlags_NoModal = 1 << 2, // file browsing window is modal by default. specify this to use a popup window - ImGuiFileBrowserFlags_NoTitleBar = 1 << 3, // hide window title bar - ImGuiFileBrowserFlags_NoStatusBar = 1 << 4, // hide status bar at the bottom of browsing window - ImGuiFileBrowserFlags_CloseOnEsc = 1 << 5, // close file browser when pressing 'ESC' - ImGuiFileBrowserFlags_CreateNewDir = 1 << 6, // allow user to create new directory - ImGuiFileBrowserFlags_MultipleSelection = 1 << 7, // allow user to select multiple files. this will hide ImGuiFileBrowserFlags_EnterNewFilename +enum ImGuiFileBrowserFlags_ { + ImGuiFileBrowserFlags_SelectDirectory + = 1 << 0, // select directory instead of regular file + ImGuiFileBrowserFlags_EnterNewFilename + = 1 << 1, // allow user to enter new filename when selecting regular file + ImGuiFileBrowserFlags_NoModal + = 1 + << 2, // file browsing window is modal by default. specify this to use a popup window + ImGuiFileBrowserFlags_NoTitleBar = 1 << 3, // hide window title bar + ImGuiFileBrowserFlags_NoStatusBar + = 1 << 4, // hide status bar at the bottom of browsing window + ImGuiFileBrowserFlags_CloseOnEsc + = 1 << 5, // close file browser when pressing 'ESC' + ImGuiFileBrowserFlags_CreateNewDir + = 1 << 6, // allow user to create new directory + ImGuiFileBrowserFlags_MultipleSelection + = 1 + << 7, // allow user to select multiple files. this will hide ImGuiFileBrowserFlags_EnterNewFilename + ImGuiFileBrowserFlags_NoMove = 1 << 8, }; -namespace ImGui -{ - class FileBrowser - { - public: +namespace ImGui { + class FileBrowser { + public: + // pwd is set to current working directory by default + explicit FileBrowser(ImGuiFileBrowserFlags flags = 0); - // pwd is set to current working directory by default - explicit FileBrowser(ImGuiFileBrowserFlags flags = 0); + FileBrowser(const FileBrowser ©From); - FileBrowser(const FileBrowser ©From); + FileBrowser &operator=(const FileBrowser ©From); - FileBrowser &operator=(const FileBrowser ©From); + // set the window size (in pixels) + // default is (700, 450) + void SetWindowSize(int width, int height) noexcept, + SetWindowPosition(const ImVec2 &position) noexcept; - // set the window size (in pixels) - // default is (700, 450) - void SetWindowSize(int width, int height) noexcept; + ImVec2 GetWindowSize(), GetWindowPosition(); - // set the window title text - void SetTitle(std::string title); + // set the window title text + void SetTitle(std::string title); - // open the browsing window - void Open(); + // open the browsing window + void Open(); - // close the browsing window - void Close(); + // close the browsing window + void Close(); - // the browsing window is opened or not - bool IsOpened() const noexcept; + // the browsing window is opened or not + bool IsOpened() const noexcept; - // display the browsing window if opened - void Display(); + // display the browsing window if opened + void Display(); - // returns true when there is a selected filename and the "ok" button was clicked - bool HasSelected() const noexcept; + // returns true when there is a selected filename and the "ok" button was clicked + bool HasSelected() const noexcept; - // set current browsing directory - bool SetPwd(const std::filesystem::path &pwd = - std::filesystem::current_path()); + // set current browsing directory + bool SetPwd(const std::filesystem::path &pwd + = std::filesystem::current_path()); - // get current browsing directory - const std::filesystem::path &GetPwd() const noexcept; + // get current browsing directory + const std::filesystem::path &GetPwd() const noexcept; - // returns selected filename. make sense only when HasSelected returns true - // when ImGuiFileBrowserFlags_MultipleSelection is enabled, only one of - // selected filename will be returned - std::filesystem::path GetSelected() const; + // returns selected filename. make sense only when HasSelected returns true + // when ImGuiFileBrowserFlags_MultipleSelection is enabled, only one of + // selected filename will be returned + std::filesystem::path GetSelected() const; - // returns all selected filenames. - // when ImGuiFileBrowserFlags_MultipleSelection is enabled, use this - // instead of GetSelected - std::vector GetMultiSelected() const; + // returns all selected filenames. + // when ImGuiFileBrowserFlags_MultipleSelection is enabled, use this + // instead of GetSelected + std::vector GetMultiSelected() const; - // set selected filename to empty - void ClearSelected(); + // set selected filename to empty + void ClearSelected(); - // (optional) set file type filters. eg. { ".h", ".cpp", ".hpp" } - // ".*" matches any file types - void SetTypeFilters(const std::vector &typeFilters); + // (optional) set file type filters. eg. { ".h", ".cpp", ".hpp" } + // ".*" matches any file types + void SetTypeFilters(const std::vector &typeFilters); - // set currently applied type filter - // default value is 0 (the first type filter) - void SetCurrentTypeFilterIndex(int index); + // set currently applied type filter + // default value is 0 (the first type filter) + void SetCurrentTypeFilterIndex(int index); - private: - - template - struct ScopeGuard - { - ScopeGuard(Functor&& t) : func(std::move(t)) { } + private: + template + struct ScopeGuard { + ScopeGuard(Functor &&t) : func(std::move(t)) {} - ~ScopeGuard() - { - func(); - } + ~ScopeGuard() { func(); } - private: + private: + Functor func; + }; - Functor func; - }; + static std::string ToLower(const std::string &s); - static std::string ToLower(const std::string &s); + void SetPwdUncatched(const std::filesystem::path &pwd); - void SetPwdUncatched(const std::filesystem::path &pwd); - - bool IsExtensionMatched(const std::filesystem::path &extension) const; + bool IsExtensionMatched(const std::filesystem::path &extension) const; #ifdef _WIN32 - static std::uint32_t GetDrivesBitMask(); + static std::uint32_t GetDrivesBitMask(); #endif - // for c++17 compatibility + // for c++17 compatibility #if defined(__cpp_lib_char8_t) - static std::string u8StrToStr(std::u8string s); + static std::string u8StrToStr(std::u8string s); #endif - static std::string u8StrToStr(std::string s); + static std::string u8StrToStr(std::string s); + + int width_; + int height_; - int width_; - int height_; - ImGuiFileBrowserFlags flags_; + ImVec2 position; - std::string title_; - std::string openLabel_; + ImGuiFileBrowserFlags flags_; - bool openFlag_; - bool closeFlag_; - bool isOpened_; - bool ok_; + std::string title_; + std::string openLabel_; - std::string statusStr_; + bool openFlag_; + bool closeFlag_; + bool isOpened_; + bool ok_; + bool position_; - std::vector typeFilters_; - int typeFilterIndex_; - bool hasAllFilter_; + std::string statusStr_; - std::filesystem::path pwd_; - std::set selectedFilenames_; + std::vector typeFilters_; + int typeFilterIndex_; + bool hasAllFilter_; - struct FileRecord - { - bool isDir = false; - std::filesystem::path name; - std::string showName; - std::filesystem::path extension; - }; - std::vector fileRecords_; + std::filesystem::path pwd_; + std::set selectedFilenames_; - // IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1 - static constexpr size_t INPUT_NAME_BUF_SIZE = 512; - std::unique_ptr> inputNameBuf_; + struct FileRecord { + bool isDir = false; + std::filesystem::path name; + std::string showName; + std::filesystem::path extension; + }; + std::vector fileRecords_; - std::string openNewDirLabel_; - std::unique_ptr> newDirNameBuf_; + // IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1 + static constexpr size_t INPUT_NAME_BUF_SIZE = 512; + std::unique_ptr> inputNameBuf_; + + std::string openNewDirLabel_; + std::unique_ptr> newDirNameBuf_; #ifdef _WIN32 - uint32_t drives_; + uint32_t drives_; #endif - }; -} // namespace ImGui - -inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags) - : width_(700), height_(450), flags_(flags), - openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false), - inputNameBuf_(std::make_unique>()) -{ - if(flags_ & ImGuiFileBrowserFlags_CreateNewDir) - newDirNameBuf_ = std::make_unique< - std::array>(); - - inputNameBuf_->front() = '\0'; - inputNameBuf_->back() = '\0'; - SetTitle("file browser"); - SetPwd(std::filesystem::current_path()); - - typeFilters_.clear(); - typeFilterIndex_ = 0; - hasAllFilter_ = false; + }; +} // namespace ImGui + +inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags) : + width_(700), + height_(450), + flags_(flags), + openFlag_(false), + closeFlag_(false), + isOpened_(false), + ok_(false), + position_(false), + position({-1, -1}), + inputNameBuf_(std::make_unique>()) { + if (flags_ & ImGuiFileBrowserFlags_CreateNewDir) + newDirNameBuf_ + = std::make_unique>(); + + inputNameBuf_->front() = '\0'; + inputNameBuf_->back() = '\0'; + SetTitle("file browser"); + SetPwd(std::filesystem::current_path()); + + typeFilters_.clear(); + typeFilterIndex_ = 0; + hasAllFilter_ = false; #ifdef _WIN32 - drives_ = GetDrivesBitMask(); + drives_ = GetDrivesBitMask(); #endif } -inline ImGui::FileBrowser::FileBrowser(const FileBrowser ©From) - : FileBrowser() -{ - *this = copyFrom; +inline ImGui::FileBrowser::FileBrowser(const FileBrowser ©From) : + FileBrowser() { + *this = copyFrom; } inline ImGui::FileBrowser &ImGui::FileBrowser::operator=( - const FileBrowser ©From) -{ - width_ = copyFrom.width_; - height_ = copyFrom.height_; + const FileBrowser ©From) { + width_ = copyFrom.width_; + height_ = copyFrom.height_; + + flags_ = copyFrom.flags_; + SetTitle(copyFrom.title_); - flags_ = copyFrom.flags_; - SetTitle(copyFrom.title_); + openFlag_ = copyFrom.openFlag_; + closeFlag_ = copyFrom.closeFlag_; + isOpened_ = copyFrom.isOpened_; + ok_ = copyFrom.ok_; - openFlag_ = copyFrom.openFlag_; - closeFlag_ = copyFrom.closeFlag_; - isOpened_ = copyFrom.isOpened_; - ok_ = copyFrom.ok_; - - statusStr_ = ""; + statusStr_ = ""; - typeFilters_ = copyFrom.typeFilters_; - typeFilterIndex_ = copyFrom.typeFilterIndex_; - hasAllFilter_ = copyFrom.hasAllFilter_; + typeFilters_ = copyFrom.typeFilters_; + typeFilterIndex_ = copyFrom.typeFilterIndex_; + hasAllFilter_ = copyFrom.hasAllFilter_; - pwd_ = copyFrom.pwd_; - selectedFilenames_ = copyFrom.selectedFilenames_; + pwd_ = copyFrom.pwd_; + selectedFilenames_ = copyFrom.selectedFilenames_; - fileRecords_ = copyFrom.fileRecords_; + fileRecords_ = copyFrom.fileRecords_; - *inputNameBuf_ = *copyFrom.inputNameBuf_; + *inputNameBuf_ = *copyFrom.inputNameBuf_; - if(flags_ & ImGuiFileBrowserFlags_CreateNewDir) - { - newDirNameBuf_ = std::make_unique< - std::array>(); - *newDirNameBuf_ = *copyFrom.newDirNameBuf_; - } + if (flags_ & ImGuiFileBrowserFlags_CreateNewDir) { + newDirNameBuf_ + = std::make_unique>(); + *newDirNameBuf_ = *copyFrom.newDirNameBuf_; + } - return *this; + return *this; } -inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept -{ - assert(width > 0 && height > 0); - width_ = width; - height_ = height; +inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept { + assert(width > 0 && height > 0); + width_ = width; + height_ = height; +} + +inline void ImGui::FileBrowser::SetWindowPosition( + const ImVec2 &position) noexcept { + this->position = position; + + position_ = false; } -inline void ImGui::FileBrowser::SetTitle(std::string title) -{ - title_ = std::move(title); - openLabel_ = title_ + "##filebrowser_" + - std::to_string(reinterpret_cast(this)); - openNewDirLabel_ = "new dir##new_dir_" + - std::to_string(reinterpret_cast(this)); +inline ImVec2 ImGui::FileBrowser::GetWindowSize() { + return {static_cast(width_), static_cast(height_)}; } -inline void ImGui::FileBrowser::Open() -{ - ClearSelected(); - statusStr_ = std::string(); - openFlag_ = true; - closeFlag_ = false; +inline ImVec2 ImGui::FileBrowser::GetWindowPosition() { return position; } + +inline void ImGui::FileBrowser::SetTitle(std::string title) { + title_ = std::move(title); + openLabel_ = title_ + "##filebrowser_" + + std::to_string(reinterpret_cast(this)); + openNewDirLabel_ + = "new dir##new_dir_" + std::to_string(reinterpret_cast(this)); } -inline void ImGui::FileBrowser::Close() -{ - ClearSelected(); - statusStr_ = std::string(); - closeFlag_ = true; - openFlag_ = false; +inline void ImGui::FileBrowser::Open() { + ClearSelected(); + statusStr_ = std::string(); + openFlag_ = true; + closeFlag_ = false; } -inline bool ImGui::FileBrowser::IsOpened() const noexcept -{ - return isOpened_; +inline void ImGui::FileBrowser::Close() { + ClearSelected(); + statusStr_ = std::string(); + closeFlag_ = true; + openFlag_ = false; } -inline void ImGui::FileBrowser::Display() -{ - PushID(this); - ScopeGuard exitThis([this] - { - openFlag_ = false; - closeFlag_ = false; - PopID(); - }); - - if(openFlag_) - OpenPopup(openLabel_.c_str()); - isOpened_ = false; - - // open the popup window - - if(openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal)) - { - SetNextWindowSize( - ImVec2(static_cast(width_), static_cast(height_))); - } - else - { - SetNextWindowSize( - ImVec2(static_cast(width_), static_cast(height_)), - ImGuiCond_FirstUseEver); - } - if(flags_ & ImGuiFileBrowserFlags_NoModal) - { - if(!BeginPopup(openLabel_.c_str())) - return; - } - else if(!BeginPopupModal(openLabel_.c_str(), nullptr, - flags_ & ImGuiFileBrowserFlags_NoTitleBar ? - ImGuiWindowFlags_NoTitleBar : 0)) - { - return; - } - isOpened_ = true; - ScopeGuard endPopup([] { EndPopup(); }); - - // display elements in pwd +inline bool ImGui::FileBrowser::IsOpened() const noexcept { return isOpened_; } + +inline void ImGui::FileBrowser::Display() { + PushID(this); + ScopeGuard exitThis([this] { + openFlag_ = false; + closeFlag_ = false; + PopID(); + }); + + if (openFlag_) OpenPopup(openLabel_.c_str()); + isOpened_ = false; + + // open the popup window + + if (openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal)) { + SetNextWindowSize( + ImVec2(static_cast(width_), static_cast(height_))); + } else { + SetNextWindowSize( + ImVec2(static_cast(width_), static_cast(height_)), + ImGuiCond_FirstUseEver); + } + + if (!position_) { + if (position.x > -1 && position.y == -1) + SetNextWindowPos({position.x, ImGui::GetWindowPos().y}); + if (position.y > -1 && position.x == -1) + SetNextWindowPos({ImGui::GetWindowPos().x, position.y}); + if (position.x > -1 && position.y > -1) SetNextWindowPos(position); + + position_ = true; + } + + if (flags_ & ImGuiFileBrowserFlags_NoModal) { + if (!BeginPopup(openLabel_.c_str())) return; + } else if (!BeginPopupModal(openLabel_.c_str(), nullptr, + (flags_ & ImGuiFileBrowserFlags_NoTitleBar + ? ImGuiWindowFlags_NoTitleBar + : 0) + | (flags_ & ImGuiFileBrowserFlags_NoMove + ? ImGuiWindowFlags_NoMove + : 0))) { + return; + } + isOpened_ = true; + ScopeGuard endPopup([] { EndPopup(); }); + + // display elements in pwd #ifdef _WIN32 - char currentDrive = static_cast(pwd_.c_str()[0]); - char driveStr[] = { currentDrive, ':', '\0' }; - - PushItemWidth(4 * GetFontSize()); - if(BeginCombo("##select_drive", driveStr)) - { - ScopeGuard guard([&] { EndCombo(); }); - for(int i = 0; i < 26; ++i) - { - if(!(drives_ & (1 << i))) - continue; - char driveCh = static_cast('A' + i); - char selectableStr[] = { driveCh, ':', '\0' }; - bool selected = currentDrive == driveCh; - if(Selectable(selectableStr, selected) && !selected) - { - char newPwd[] = { driveCh, ':', '\\', '\0' }; - SetPwd(newPwd); - } - } - } - PopItemWidth(); - - SameLine(); + char currentDrive = static_cast(pwd_.c_str()[0]); + char driveStr[] = {currentDrive, ':', '\0'}; + + PushItemWidth(4 * GetFontSize()); + if (BeginCombo("##select_drive", driveStr)) { + ScopeGuard guard([&] { EndCombo(); }); + for (int i = 0; i < 26; ++i) { + if (!(drives_ & (1 << i))) continue; + char driveCh = static_cast('A' + i); + char selectableStr[] = {driveCh, ':', '\0'}; + bool selected = currentDrive == driveCh; + if (Selectable(selectableStr, selected) && !selected) { + char newPwd[] = {driveCh, ':', '\\', '\0'}; + SetPwd(newPwd); + } + } + } + PopItemWidth(); + + SameLine(); #endif - int secIdx = 0, newPwdLastSecIdx = -1; - for(auto &sec : pwd_) - { + int secIdx = 0, newPwdLastSecIdx = -1; + for (auto &sec : pwd_) { #ifdef _WIN32 - if(secIdx == 1) - { - ++secIdx; - continue; - } + if (secIdx == 1) { + ++secIdx; + continue; + } #endif - PushID(secIdx); - if(secIdx > 0) - SameLine(); - if(SmallButton(u8StrToStr(sec.u8string()).c_str())) - newPwdLastSecIdx = secIdx; - PopID(); - ++secIdx; - } - - if(newPwdLastSecIdx >= 0) - { - int i = 0; - std::filesystem::path newPwd; - for(auto &sec : pwd_) - { - if(i++ > newPwdLastSecIdx) - break; - newPwd /= sec; - } + PushID(secIdx); + if (secIdx > 0) SameLine(); + if (SmallButton(u8StrToStr(sec.u8string()).c_str())) + newPwdLastSecIdx = secIdx; + PopID(); + ++secIdx; + } + + if (newPwdLastSecIdx >= 0) { + int i = 0; + std::filesystem::path newPwd; + for (auto &sec : pwd_) { + if (i++ > newPwdLastSecIdx) break; + newPwd /= sec; + } #ifdef _WIN32 - if(newPwdLastSecIdx == 0) - newPwd /= "\\"; + if (newPwdLastSecIdx == 0) newPwd /= "\\"; #endif - SetPwd(newPwd); - } - - SameLine(); - - if(SmallButton("*")) - SetPwd(pwd_); - - if(newDirNameBuf_) - { - SameLine(); - if(SmallButton("+")) - { - OpenPopup(openNewDirLabel_.c_str()); - (*newDirNameBuf_)[0] = '\0'; - } - - if(BeginPopup(openNewDirLabel_.c_str())) - { - ScopeGuard endNewDirPopup([] { EndPopup(); }); - - InputText("name", newDirNameBuf_->data(), newDirNameBuf_->size()); - SameLine(); - - if(Button("ok") && (*newDirNameBuf_)[0] != '\0') - { - ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); }); - if(create_directory(pwd_ / newDirNameBuf_->data())) - SetPwd(pwd_); - else - { - statusStr_ = "failed to create " + - std::string(newDirNameBuf_->data()); - } - } - } - } - - // browse files in a child window - - float reserveHeight = GetFrameHeightWithSpacing(); - std::filesystem::path newPwd; bool setNewPwd = false; - if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) && - (flags_ & ImGuiFileBrowserFlags_EnterNewFilename)) - reserveHeight += GetFrameHeightWithSpacing(); - { - BeginChild("ch", ImVec2(0, -reserveHeight), true, - (flags_ & ImGuiFileBrowserFlags_NoModal) ? - ImGuiWindowFlags_AlwaysHorizontalScrollbar : 0); - ScopeGuard endChild([] { EndChild(); }); - - for(auto &rsc : fileRecords_) - { - if(!rsc.isDir && !IsExtensionMatched(rsc.extension)) - continue; - - if(!rsc.name.empty() && rsc.name.c_str()[0] == '$') - continue; - - bool selected = selectedFilenames_.find(rsc.name) - != selectedFilenames_.end(); - - if(Selectable(rsc.showName.c_str(), selected, - ImGuiSelectableFlags_DontClosePopups)) - { - const bool multiSelect = - (flags_ & ImGuiFileBrowserFlags_MultipleSelection) && - IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && - (GetIO().KeyCtrl || GetIO().KeyShift); - - if(selected) - { - if(!multiSelect) - selectedFilenames_.clear(); - else - selectedFilenames_.erase(rsc.name); - - (*inputNameBuf_)[0] = '\0'; - } - else if(rsc.name != "..") - { - if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) || - (!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory))) - { - if(!multiSelect) - selectedFilenames_.clear(); - selectedFilenames_.insert(rsc.name); - if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) - { + SetPwd(newPwd); + } + + SameLine(); + + if (SmallButton("*")) SetPwd(pwd_); + + if (newDirNameBuf_) { + SameLine(); + if (SmallButton("+")) { + OpenPopup(openNewDirLabel_.c_str()); + (*newDirNameBuf_)[0] = '\0'; + } + + if (BeginPopup(openNewDirLabel_.c_str())) { + ScopeGuard endNewDirPopup([] { EndPopup(); }); + + InputText("name", newDirNameBuf_->data(), newDirNameBuf_->size()); + SameLine(); + + if (Button("ok") && (*newDirNameBuf_)[0] != '\0') { + ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); }); + if (create_directory(pwd_ / newDirNameBuf_->data())) + SetPwd(pwd_); + else { + statusStr_ = "failed to create " + + std::string(newDirNameBuf_->data()); + } + } + } + } + + // browse files in a child window + + float reserveHeight = GetFrameHeightWithSpacing(); + std::filesystem::path newPwd; + bool setNewPwd = false; + if (!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) + && (flags_ & ImGuiFileBrowserFlags_EnterNewFilename)) + reserveHeight += GetFrameHeightWithSpacing(); + { + BeginChild("ch", ImVec2(0, -reserveHeight), true, + (flags_ & ImGuiFileBrowserFlags_NoModal) + ? ImGuiWindowFlags_AlwaysHorizontalScrollbar + : 0); + ScopeGuard endChild([] { EndChild(); }); + + for (auto &rsc : fileRecords_) { + if (!rsc.isDir && !IsExtensionMatched(rsc.extension)) continue; + + if (!rsc.name.empty() && rsc.name.c_str()[0] == '$') continue; + + bool selected + = selectedFilenames_.find(rsc.name) != selectedFilenames_.end(); + + if (Selectable(rsc.showName.c_str(), selected, + ImGuiSelectableFlags_DontClosePopups)) { + const bool multiSelect + = (flags_ & ImGuiFileBrowserFlags_MultipleSelection) + && IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) + && (GetIO().KeyCtrl || GetIO().KeyShift); + + if (selected) { + if (!multiSelect) + selectedFilenames_.clear(); + else + selectedFilenames_.erase(rsc.name); + + (*inputNameBuf_)[0] = '\0'; + } else if (rsc.name != "..") { + if ((rsc.isDir + && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) + || (!rsc.isDir + && !(flags_ + & ImGuiFileBrowserFlags_SelectDirectory))) { + if (!multiSelect) selectedFilenames_.clear(); + selectedFilenames_.insert(rsc.name); + if (!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) { #ifdef _MSC_VER - strcpy_s( - inputNameBuf_->data(), inputNameBuf_->size(), - u8StrToStr(rsc.name.u8string()).c_str()); + strcpy_s(inputNameBuf_->data(), + inputNameBuf_->size(), + u8StrToStr(rsc.name.u8string()).c_str()); #else - std::strncpy(inputNameBuf_->data(), - u8StrToStr(rsc.name.u8string()).c_str(), - inputNameBuf_->size() - 1); + std::strncpy( + inputNameBuf_->data(), + u8StrToStr(rsc.name.u8string()).c_str(), + inputNameBuf_->size() - 1); #endif - } - } - } - else - { - if(!multiSelect) - selectedFilenames_.clear(); - } - } - - if(IsItemClicked(0) && IsMouseDoubleClicked(0)) - { - if(rsc.isDir) - { - setNewPwd = true; - newPwd = (rsc.name != "..") ? (pwd_ / rsc.name) : - pwd_.parent_path(); - } - else if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) - { - selectedFilenames_ = { rsc.name }; - ok_ = true; - CloseCurrentPopup(); - } - } - } - } - - if(setNewPwd) - SetPwd(newPwd); - - if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) && - (flags_ & ImGuiFileBrowserFlags_EnterNewFilename)) - { - PushID(this); - ScopeGuard popTextID([] { PopID(); }); - - PushItemWidth(-1); - if(InputText("", inputNameBuf_->data(), inputNameBuf_->size()) && - inputNameBuf_->at(0) != '\0') - { - selectedFilenames_ = { inputNameBuf_->data() }; - } - PopItemWidth(); - } - - if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) - { - if(Button(" ok ") && !selectedFilenames_.empty()) - { - ok_ = true; - CloseCurrentPopup(); - } - } - else - { - if(Button(" ok ")) - { - ok_ = true; - CloseCurrentPopup(); - } - } - - SameLine(); - - int escIdx = GetIO().KeyMap[ImGuiKey_Escape]; - if(Button("cancel") || closeFlag_ || - ((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) && - IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && - escIdx >= 0 && IsKeyPressed(escIdx))) - CloseCurrentPopup(); - - if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar)) - { - SameLine(); - Text("%s", statusStr_.c_str()); - } - - if(!typeFilters_.empty()) - { - SameLine(); - PushItemWidth(8 * GetFontSize()); - if(BeginCombo( - "##type_filters", typeFilters_[typeFilterIndex_].c_str())) - { - ScopeGuard guard([&] { EndCombo(); }); - - for(size_t i = 0; i < typeFilters_.size(); ++i) - { - bool selected = static_cast(i) == typeFilterIndex_; - if(Selectable(typeFilters_[i].c_str(), selected) && !selected) - typeFilterIndex_ = static_cast(i); - } - } - PopItemWidth(); - } + } + } + } else { + if (!multiSelect) selectedFilenames_.clear(); + } + } + + if (IsItemClicked(0) && IsMouseDoubleClicked(0)) { + if (rsc.isDir) { + setNewPwd = true; + newPwd = (rsc.name != "..") ? (pwd_ / rsc.name) + : pwd_.parent_path(); + } else if (!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) { + selectedFilenames_ = {rsc.name}; + ok_ = true; + CloseCurrentPopup(); + } + } + } + } + + if (setNewPwd) SetPwd(newPwd); + + if (!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) + && (flags_ & ImGuiFileBrowserFlags_EnterNewFilename)) { + PushID(this); + ScopeGuard popTextID([] { PopID(); }); + + PushItemWidth(-1); + if (InputText("", inputNameBuf_->data(), inputNameBuf_->size()) + && inputNameBuf_->at(0) != '\0') { + selectedFilenames_ = {inputNameBuf_->data()}; + } + PopItemWidth(); + } + + if (!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) { + if (Button(" ok ") && !selectedFilenames_.empty()) { + ok_ = true; + CloseCurrentPopup(); + } + } else { + if (Button(" ok ")) { + ok_ = true; + CloseCurrentPopup(); + } + } + + SameLine(); + + int escIdx = GetIO().KeyMap[ImGuiKey_Escape]; + if (Button("cancel") || closeFlag_ + || ((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) + && IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) + && escIdx >= 0 && IsKeyPressed(escIdx))) + CloseCurrentPopup(); + + if (!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar)) { + SameLine(); + Text("%s", statusStr_.c_str()); + } + + if (!typeFilters_.empty()) { + SameLine(); + PushItemWidth(8 * GetFontSize()); + if (BeginCombo("##type_filters", + typeFilters_[typeFilterIndex_].c_str())) { + ScopeGuard guard([&] { EndCombo(); }); + + for (size_t i = 0; i < typeFilters_.size(); ++i) { + bool selected = static_cast(i) == typeFilterIndex_; + if (Selectable(typeFilters_[i].c_str(), selected) && !selected) + typeFilterIndex_ = static_cast(i); + } + } + PopItemWidth(); + } } -inline bool ImGui::FileBrowser::HasSelected() const noexcept -{ - return ok_; -} +inline bool ImGui::FileBrowser::HasSelected() const noexcept { return ok_; } -inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd) -{ - try - { - SetPwdUncatched(pwd); - return true; - } - catch(const std::exception &err) - { - statusStr_ = std::string("last error: ") + err.what(); - } - catch(...) - { - statusStr_ = "last error: unknown"; - } - - SetPwdUncatched(std::filesystem::current_path()); - return false; +inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd) { + try { + SetPwdUncatched(pwd); + return true; + } catch (const std::exception &err) { + statusStr_ = std::string("last error: ") + err.what(); + } catch (...) { statusStr_ = "last error: unknown"; } + + SetPwdUncatched(std::filesystem::current_path()); + return false; } -inline const class std::filesystem::path &ImGui::FileBrowser::GetPwd() const noexcept -{ - return pwd_; +inline const class std::filesystem::path &ImGui::FileBrowser::GetPwd() + const noexcept { + return pwd_; } -inline std::filesystem::path ImGui::FileBrowser::GetSelected() const -{ - // when ok_ is true, selectedFilenames_ may be empty if SelectDirectory - // is enabled. return pwd in that case. - if(selectedFilenames_.empty()) - return pwd_; - return pwd_ / *selectedFilenames_.begin(); +inline std::filesystem::path ImGui::FileBrowser::GetSelected() const { + // when ok_ is true, selectedFilenames_ may be empty if SelectDirectory + // is enabled. return pwd in that case. + if (selectedFilenames_.empty()) return pwd_; + return pwd_ / *selectedFilenames_.begin(); } -inline std::vector - ImGui::FileBrowser::GetMultiSelected() const -{ - if(selectedFilenames_.empty()) - return { pwd_ }; - std::vector ret; - ret.reserve(selectedFilenames_.size()); - for(auto &s : selectedFilenames_) - ret.push_back(pwd_ / s); - return ret; +inline std::vector ImGui::FileBrowser::GetMultiSelected() + const { + if (selectedFilenames_.empty()) return {pwd_}; + std::vector ret; + ret.reserve(selectedFilenames_.size()); + for (auto &s : selectedFilenames_) ret.push_back(pwd_ / s); + return ret; } -inline void ImGui::FileBrowser::ClearSelected() -{ - selectedFilenames_.clear(); - (*inputNameBuf_)[0] = '\0'; - ok_ = false; +inline void ImGui::FileBrowser::ClearSelected() { + selectedFilenames_.clear(); + (*inputNameBuf_)[0] = '\0'; + ok_ = false; } inline void ImGui::FileBrowser::SetTypeFilters( - const std::vector &_typeFilters) -{ - typeFilters_.clear(); + const std::vector &_typeFilters) { + typeFilters_.clear(); - // remove duplicate filter names due to case unsensitivity on windows + // remove duplicate filter names due to case unsensitivity on windows #ifdef _WIN32 - std::vector typeFilters; - for(auto &rawFilter : _typeFilters) - { - std::string lowerFilter = ToLower(rawFilter); - if(std::find(typeFilters.begin(), typeFilters.end(), lowerFilter) == - typeFilters.end()) - typeFilters.push_back(std::move(lowerFilter)); - } + std::vector typeFilters; + for (auto &rawFilter : _typeFilters) { + std::string lowerFilter = ToLower(rawFilter); + if (std::find(typeFilters.begin(), typeFilters.end(), lowerFilter) + == typeFilters.end()) + typeFilters.push_back(std::move(lowerFilter)); + } #else - auto &typeFilters = _typeFilters; + auto &typeFilters = _typeFilters; #endif - // insert auto-generated filter - - if(typeFilters.size() > 1) - { - hasAllFilter_ = true; - std::string allFiltersName = std::string(); - for(size_t i = 0; i < typeFilters.size(); ++i) - { - if(typeFilters[i] == std::string_view(".*")) - { - hasAllFilter_ = false; - break; - } - - if(i > 0) - allFiltersName += ","; - allFiltersName += typeFilters[i]; - } - - if(hasAllFilter_) - typeFilters_.push_back(std::move(allFiltersName)); - } - - std::copy( - typeFilters.begin(), typeFilters.end(), - std::back_inserter(typeFilters_)); - - typeFilterIndex_ = 0; + // insert auto-generated filter + + if (typeFilters.size() > 1) { + hasAllFilter_ = true; + std::string allFiltersName = std::string(); + for (size_t i = 0; i < typeFilters.size(); ++i) { + if (typeFilters[i] == std::string_view(".*")) { + hasAllFilter_ = false; + break; + } + + if (i > 0) allFiltersName += ","; + allFiltersName += typeFilters[i]; + } + + if (hasAllFilter_) typeFilters_.push_back(std::move(allFiltersName)); + } + + std::copy(typeFilters.begin(), typeFilters.end(), + std::back_inserter(typeFilters_)); + + typeFilterIndex_ = 0; } -inline void ImGui::FileBrowser::SetCurrentTypeFilterIndex(int index) -{ - typeFilterIndex_ = index; +inline void ImGui::FileBrowser::SetCurrentTypeFilterIndex(int index) { + typeFilterIndex_ = index; } -inline std::string ImGui::FileBrowser::ToLower(const std::string &s) -{ - std::string ret = s; - for(char &c : ret) - c = static_cast(std::tolower(c)); - return ret; +inline std::string ImGui::FileBrowser::ToLower(const std::string &s) { + std::string ret = s; + for (char &c : ret) c = static_cast(std::tolower(c)); + return ret; } -inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd) -{ - fileRecords_ = { FileRecord{ true, "..", "[D] ..", "" } }; - - for(auto &p : std::filesystem::directory_iterator(pwd)) - { - FileRecord rcd; - - if(p.is_regular_file()) - rcd.isDir = false; - else if(p.is_directory()) - rcd.isDir = true; - else - continue; - - rcd.name = p.path().filename(); - if(rcd.name.empty()) - continue; - - rcd.extension = p.path().filename().extension(); - - rcd.showName = (rcd.isDir ? "[D] " : "[F] ") + - u8StrToStr(p.path().filename().u8string()); - fileRecords_.push_back(rcd); - } - - std::sort(fileRecords_.begin(), fileRecords_.end(), - [](const FileRecord &L, const FileRecord &R) - { - return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name); - }); - - pwd_ = absolute(pwd); - selectedFilenames_.clear(); - (*inputNameBuf_)[0] = '\0'; +inline void ImGui::FileBrowser::SetPwdUncatched( + const std::filesystem::path &pwd) { + fileRecords_ = {FileRecord {true, "..", "[D] ..", ""}}; + + for (auto &p : std::filesystem::directory_iterator(pwd)) { + FileRecord rcd; + + if (p.is_regular_file()) + rcd.isDir = false; + else if (p.is_directory()) + rcd.isDir = true; + else + continue; + + rcd.name = p.path().filename(); + if (rcd.name.empty()) continue; + + rcd.extension = p.path().filename().extension(); + + rcd.showName = (rcd.isDir ? "[D] " : "[F] ") + + u8StrToStr(p.path().filename().u8string()); + fileRecords_.push_back(rcd); + } + + std::sort(fileRecords_.begin(), fileRecords_.end(), + [](const FileRecord &L, const FileRecord &R) { + return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name); + }); + + pwd_ = absolute(pwd); + selectedFilenames_.clear(); + (*inputNameBuf_)[0] = '\0'; } inline bool ImGui::FileBrowser::IsExtensionMatched( - const std::filesystem::path &_extension) const -{ + const std::filesystem::path &_extension) const { #ifdef _WIN32 - std::filesystem::path extension = ToLower(_extension.string()); + std::filesystem::path extension = ToLower(_extension.string()); #else - auto &extension = _extension; + auto &extension = _extension; #endif - // no type filters - if(typeFilters_.empty()) - return true; - - // invalid type filter index - if(static_cast(typeFilterIndex_) >= typeFilters_.size()) - return true; - - // all type filters - if(hasAllFilter_ && typeFilterIndex_ == 0) - { - for(size_t i = 1; i < typeFilters_.size(); ++i) - { - if(extension == typeFilters_[i]) - return true; - } - return false; - } - - // universal filter - if(typeFilters_[typeFilterIndex_] == std::string_view(".*")) - return true; - - // regular filter - return extension == typeFilters_[typeFilterIndex_]; + // no type filters + if (typeFilters_.empty()) return true; + + // invalid type filter index + if (static_cast(typeFilterIndex_) >= typeFilters_.size()) + return true; + + // all type filters + if (hasAllFilter_ && typeFilterIndex_ == 0) { + for (size_t i = 1; i < typeFilters_.size(); ++i) { + if (extension == typeFilters_[i]) return true; + } + return false; + } + + // universal filter + if (typeFilters_[typeFilterIndex_] == std::string_view(".*")) return true; + + // regular filter + return extension == typeFilters_[typeFilterIndex_]; } #if defined(__cpp_lib_char8_t) -inline std::string ImGui::FileBrowser::u8StrToStr(std::u8string s) -{ - return std::string(s.begin(), s.end()); +inline std::string ImGui::FileBrowser::u8StrToStr(std::u8string s) { + return std::string(s.begin(), s.end()); } #endif -inline std::string ImGui::FileBrowser::u8StrToStr(std::string s) -{ - return s; -} +inline std::string ImGui::FileBrowser::u8StrToStr(std::string s) { return s; } #ifdef _WIN32 -#ifndef _INC_WINDOWS + #ifndef _INC_WINDOWS -#ifndef WIN32_LEAN_AND_MEAN + #ifndef WIN32_LEAN_AND_MEAN -#define IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN + #define IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN -#endif // #ifndef WIN32_LEAN_AND_MEAN + #endif // #ifndef WIN32_LEAN_AND_MEAN -#include + #include -#ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN -#undef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif // #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN + #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN + #undef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN + #undef WIN32_LEAN_AND_MEAN + #endif // #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN -#endif // #ifdef _INC_WINDOWS + #endif // #ifdef _INC_WINDOWS -inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask() -{ - DWORD mask = GetLogicalDrives(); - uint32_t ret = 0; - for(int i = 0; i < 26; ++i) - { - if(!(mask & (1 << i))) - continue; - char rootName[4] = { static_cast('A' + i), ':', '\\', '\0' }; - UINT type = GetDriveTypeA(rootName); - if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED) - ret |= (1 << i); - } - return ret; +inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask() { + DWORD mask = GetLogicalDrives(); + uint32_t ret = 0; + for (int i = 0; i < 26; ++i) { + if (!(mask & (1 << i))) continue; + char rootName[4] = {static_cast('A' + i), ':', '\\', '\0'}; + UINT type = GetDriveTypeA(rootName); + if (type == DRIVE_REMOVABLE || type == DRIVE_FIXED) ret |= (1 << i); + } + return ret; } #endif