46 std::string selectedFile =
"";
47 std::string actionFile =
"";
49 std::filesystem::path relative = m_currentPath.lexically_relative(m_rootPath);
51 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.0f, 4.0f));
53 if (ImGui::Button(
"Assets")) {
54 m_currentPath = m_rootPath;
57 if (ImGui::BeginDragDropTarget()) {
58 if (
const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(
"ASSET_ITEM")) {
59 std::string sourcePathStr = (
const char*)payload->Data;
60 std::filesystem::path sourcePath(sourcePathStr);
61 std::filesystem::path destPath = m_rootPath / sourcePath.filename();
63 if (sourcePath != destPath) {
64 std::filesystem::rename(sourcePath, destPath);
66 }
catch (
const std::filesystem::filesystem_error& e) {
67 vex::log(
"Error moving file to root: %s", e.what());
70 ImGui::EndDragDropTarget();
73 std::filesystem::path accumPath = m_rootPath;
74 for (
auto it = relative.begin(); it != relative.end() && relative !=
"."; ++it) {
79 if (ImGui::Button(it->string().c_str())) {
80 m_currentPath = accumPath;
82 if (ImGui::BeginDragDropTarget()) {
83 if (
const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(
"ASSET_ITEM")) {
84 std::string sourcePathStr = (
const char*)payload->Data;
85 std::filesystem::path sourcePath(sourcePathStr);
86 std::filesystem::path destPath = accumPath / sourcePath.filename();
88 if (sourcePath != destPath) {
89 std::filesystem::rename(sourcePath, destPath);
91 }
catch (
const std::filesystem::filesystem_error& e) {
92 vex::log(
"Error moving file to breadcrumb: %s", e.what());
95 ImGui::EndDragDropTarget();
100 ImGui::SameLine(ImGui::GetWindowWidth() - 200.0f);
101 ImGui::SetNextItemWidth(180.0f);
102 ImGui::InputTextWithHint(
"##Search",
"Search...", m_searchBuffer,
sizeof(m_searchBuffer));
106 if (ImGui::BeginPopupContextWindow(
"AssetBrowserBgContext", ImGuiMouseButton_Right | ImGuiPopupFlags_NoOpenOverItems)) {
107 if (ImGui::MenuItem(
"Paste",
nullptr,
false, !m_copiedPath.empty())) {
108 if (std::filesystem::exists(m_copiedPath)) {
110 std::filesystem::path dest = m_currentPath / m_copiedPath.filename();
112 std::string stem = m_copiedPath.stem().string();
113 std::string ext = m_copiedPath.extension().string();
114 while (std::filesystem::exists(dest)) {
115 dest = m_currentPath / (stem +
" (" + std::to_string(i++) +
")" + ext);
118 std::filesystem::copy(m_copiedPath, dest, std::filesystem::copy_options::recursive);
121 std::filesystem::remove_all(m_copiedPath);
122 m_copiedPath.clear();
125 }
catch (
const std::filesystem::filesystem_error& e) {
133 bool isDialog =
false;
134 if (ImGuiWindow* window = ImGui::GetCurrentWindow()) {
135 isDialog = (strcmp(window->Name,
"Assets") != 0);
137 if (isDialog) showBottomBar =
false;
139 float bottomBarHeight = showBottomBar ? (ImGui::GetFrameHeightWithSpacing() + 5.0f) : 0.0f;
140 float contentHeight = height;
141 if (contentHeight <= 0.0f) {
142 contentHeight = isDialog ? (ImGui::GetContentRegionAvail().y - 120.0f) : -bottomBarHeight;
143 if (contentHeight < 100.0f && isDialog) contentHeight = 100.0f;
146 ImGui::BeginChild(
"BrowserContent", ImVec2(0, contentHeight),
false, ImGuiWindowFlags_HorizontalScrollbar);
148 float cellSize = thumbnailSize + m_padding;
149 float panelWidth = ImGui::GetContentRegionAvail().x;
150 int columnCount = (int)(panelWidth / cellSize);
151 if (columnCount < 1) columnCount = 1;
153 if (ImGui::BeginTable(
"BrowserGrid", columnCount)) {
154 std::vector<std::filesystem::directory_entry> folders;
155 std::vector<std::filesystem::directory_entry> files;
158 for (
const auto& entry : std::filesystem::directory_iterator(m_currentPath)) {
159 if (entry.is_directory()) {
160 folders.push_back(entry);
162 files.push_back(entry);
165 }
catch (
const std::filesystem::filesystem_error& e) {
169 auto sortAlpha = [](
const std::filesystem::directory_entry& a,
const std::filesystem::directory_entry& b) {
170 return a.path().filename().string() < b.path().filename().string();
172 std::sort(folders.begin(), folders.end(), sortAlpha);
173 std::sort(files.begin(), files.end(), sortAlpha);
175 auto renderEntry = [&](
const std::filesystem::directory_entry& entry) {
176 std::string filename = entry.path().filename().string();
177 if (m_searchBuffer[0] !=
'\0') {
178 std::string lowerFilename = filename;
179 std::string lowerSearch = m_searchBuffer;
180 std::transform(lowerFilename.begin(), lowerFilename.end(), lowerFilename.begin(), [](
unsigned char c){ return std::tolower(c); });
181 std::transform(lowerSearch.begin(), lowerSearch.end(), lowerSearch.begin(), [](
unsigned char c){ return std::tolower(c); });
182 if (lowerFilename.find(lowerSearch) == std::string::npos) {
187 ImGui::TableNextColumn();
188 ImGui::PushID(filename.c_str());
190 float columnWidth = ImGui::GetContentRegionAvail().x;
192 ImTextureID iconID =
GetIcon(entry, icons);
193 if (iconID == 0) iconID = icons.file;
195 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
197 ImGui::ImageButton(
"##Icon", iconID, { thumbnailSize, thumbnailSize });
199 if (ImGui::BeginDragDropSource()) {
200 std::string fullPath = entry.path().string();
202 ImGui::SetDragDropPayload(
"ASSET_ITEM", fullPath.c_str(), fullPath.size() + 1);
204 ImGui::Text(
"Move %s", entry.path().filename().string().c_str());
205 ImGui::Image(iconID, { thumbnailSize/2, thumbnailSize/2 });
207 ImGui::EndDragDropSource();
210 if (entry.is_directory()) {
211 if (ImGui::BeginDragDropTarget()) {
212 if (
const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(
"ASSET_ITEM")) {
213 std::string sourcePathStr = (
const char*)payload->Data;
214 std::filesystem::path sourcePath(sourcePathStr);
215 std::filesystem::path targetFolder = entry.path();
216 std::filesystem::path destPath = targetFolder / sourcePath.filename();
219 bool isSubFolder = (targetFolder.string().find(sourcePath.string()) == 0);
221 if (sourcePath != destPath && !isSubFolder) {
222 std::filesystem::rename(sourcePath, destPath);
224 }
catch (
const std::filesystem::filesystem_error& e) {
225 vex::log(
"Error moving file: %s", e.what());
228 ImGui::EndDragDropTarget();
232 if (ImGui::IsItemHovered()) {
233 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
234 if (entry.is_directory()) {
235 m_currentPath /= entry.path().filename();
237 actionFile = entry.path().string();
239 }
else if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
240 selectedFile = entry.path().string();
244 ImGui::PopStyleColor();
246 if (ImGui::BeginPopupContextItem()) {
247 if (ImGui::MenuItem(
"Open")) {
248 if (entry.is_directory()) m_currentPath /= entry.path().filename();
249 else actionFile = entry.path().string();
252 if (ImGui::MenuItem(
"Copy")) {
253 m_copiedPath = entry.path();
256 if (ImGui::MenuItem(
"Cut")) {
257 m_copiedPath = entry.path();
260 if (ImGui::MenuItem(
"Duplicate")) {
262 std::filesystem::path src = entry.path();
263 std::filesystem::path dest = src;
265 while(std::filesystem::exists(dest)) {
266 dest = src.parent_path() / (src.stem().
string() +
"_Copy" + std::to_string(i++) + src.extension().string());
268 std::filesystem::copy(src, dest, std::filesystem::copy_options::recursive);
269 }
catch(
const std::filesystem::filesystem_error& e) {
273 if (ImGui::MenuItem(
"Rename")) {
275 m_renamePath = entry.path();
276 strncpy(m_renameBuffer, filename.c_str(),
sizeof(m_renameBuffer));
279 if (ImGui::MenuItem(
"Delete")) {
280 try { std::filesystem::remove_all(entry.path()); }
catch(...) {}
285 if (m_renaming && m_renamePath == entry.path()) {
286 ImGui::SetNextItemWidth(cellSize);
287 if (ImGui::InputText(
"##Rename", m_renameBuffer,
sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
288 std::filesystem::path newPath = entry.path().parent_path() / m_renameBuffer;
290 std::filesystem::rename(entry.path(), newPath);
291 }
catch (
const std::filesystem::filesystem_error& e) {
296 if (!ImGui::IsItemActive() && (ImGui::IsMouseClicked(0) || ImGui::IsMouseClicked(1))) {
301 float columnWidth = ImGui::GetContentRegionAvail().x;
302 float textWidth = ImGui::CalcTextSize(filename.c_str()).x;
304 if (textWidth < (columnWidth * 1.1f)) {
305 float textOffset = (columnWidth - textWidth) * 0.5f;
306 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + textOffset);
307 ImGui::Text(
"%s", filename.c_str());
309 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + columnWidth);
310 ImGui::TextWrapped(
"%s", filename.c_str());
311 ImGui::PopTextWrapPos();
318 for (
const auto& entry : folders) { renderEntry(entry); }
319 for (
const auto& entry : files) { renderEntry(entry); }
327 ImGui::SetNextItemWidth(150.0f);
328 ImGui::SliderFloat(
"Icon Size", &thumbnailSize, 16.0f, 256.0f,
"%.0f");