RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
DirectoryWindow Class Reference

#include <DirectoryWindow.h>

Collaboration diagram for DirectoryWindow:

Classes

struct  ImageData
 

Public Member Functions

 DirectoryWindow (std::string directoryPath, std::string directoryName, DirectoryKind kind_)
 
std::string TruncateString (const std::string &str, size_t maxLength)
 
void generateTextureIDs ()
 
void renderDirectory ()
 
ImageData getSelectedImage () const
 
void clearSelectedImage ()
 
void startMonitoring ()
 
void stopMonitoring ()
 
 ~DirectoryWindow ()
 
ImageData getImageByPath (const std::string &file_name)
 
float getGlobalSizeSlider ()
 
void applyPendingAssetChanges ()
 

Public Attributes

std::string directoryPath
 
std::string directoryName
 

Private Member Functions

std::vector< ImageData >::iterator findImageByFilename (std::vector< ImageData > &images, const std::string &filename)
 
void monitorDirectory (const std::string &path)
 
ImageData LoadTextureFromFile (const char *path)
 

Private Attributes

ImageData selected_image = {0, glm::vec2(), ""}
 
std::vector< ImageDataimages
 
std::thread monitorThread
 
std::atomic< bool > running {true}
 
std::atomic< bool > first_scan_done {true}
 
std::shared_mutex imagesMutex
 
DirectoryKind kind_
 
float global_size_slider = 1.0f
 
float imageSize = 128.0f
 
std::mutex pendingMtx
 
std::vector< std::string > pendingRemovals
 
std::vector< std::pair< std::string, std::string > > pendingAddPaths
 

Detailed Description

Definition at line 21 of file DirectoryWindow.h.

Constructor & Destructor Documentation

◆ DirectoryWindow()

DirectoryWindow::DirectoryWindow ( std::string directoryPath,
std::string directoryName,
DirectoryKind kind_ )
inline

Definition at line 24 of file DirectoryWindow.h.

25 {
28 this->kind_ = kind_;
29 }
DirectoryKind kind_
std::string directoryPath
std::string directoryName

◆ ~DirectoryWindow()

DirectoryWindow::~DirectoryWindow ( )
inline

Definition at line 224 of file DirectoryWindow.h.

225 {
227 }
Here is the call graph for this function:

Member Function Documentation

◆ applyPendingAssetChanges()

void DirectoryWindow::applyPendingAssetChanges ( )
inline

Definition at line 257 of file DirectoryWindow.h.

258 {
259 // 1) Steal queues quickly (minimize lock time)
260 std::vector<std::string> removals;
261 std::vector<std::pair<std::string, std::string>> adds;
262 {
263 std::lock_guard<std::mutex> qlk(pendingMtx);
264 removals.swap(pendingRemovals);
265 adds.swap(pendingAddPaths);
266 }
267
268 // 2) Process removals (GL-safe thread)
269 if (!removals.empty())
270 {
271 std::unique_lock<std::shared_mutex> lk(imagesMutex);
272 for (const auto& fname : removals)
273 {
274 auto it = std::find_if(images.begin(), images.end(),
275 [&](const ImageData& im)
276 { return im.filename == fname; });
277 if (it != images.end())
278 {
279 if (it->textureID != 0)
280 glDeleteTextures(1, &it->textureID);
281 images.erase(it);
282 }
283 // else: already gone — ignore
284 }
285 }
286
287 // 3) Process additions (load textures here; GL-safe thread)
288 if (!adds.empty())
289 {
290 std::vector<ImageData> toInsert;
291 toInsert.reserve(adds.size());
292
293 for (auto& [fname, fullpath] : adds)
294 {
295 // Optional: wait briefly if file is still being copied
296 bool ready = false;
297 for (int i = 0; i < 10; ++i)
298 {
299 std::ifstream f(fullpath, std::ios::binary);
300 if (f.good())
301 {
302 ready = true;
303 break;
304 }
305 std::this_thread::sleep_for(std::chrono::milliseconds(50));
306 }
307 if (!ready)
308 continue;
309
310 // Use your existing loader (returns ImageData with texture + size)
311 ImageData img = LoadTextureFromFile(fullpath.c_str());
312 if (img.textureID != 0)
313 {
314 img.filename = fname; // store filename (not full path) for matching
315 toInsert.emplace_back(std::move(img));
316 }
317 }
318
319 if (!toInsert.empty())
320 {
321 std::unique_lock<std::shared_mutex> lk(imagesMutex);
322 for (auto& im : toInsert)
323 {
324 // Avoid duplicates
325 auto it = std::find_if(images.begin(), images.end(),
326 [&](const ImageData& x)
327 { return x.filename == im.filename; });
328 if (it == images.end())
329 images.emplace_back(std::move(im));
330 }
331 }
332 }
333 }
std::shared_mutex imagesMutex
std::mutex pendingMtx
std::vector< ImageData > images
ImageData LoadTextureFromFile(const char *path)
std::vector< std::string > pendingRemovals
std::vector< std::pair< std::string, std::string > > pendingAddPaths

◆ clearSelectedImage()

void DirectoryWindow::clearSelectedImage ( )
inline

Definition at line 205 of file DirectoryWindow.h.

206 {
207 selected_image = {0, glm::vec2(), ""}; // Limpa a seleção
208 }
ImageData selected_image

◆ findImageByFilename()

std::vector< ImageData >::iterator DirectoryWindow::findImageByFilename ( std::vector< ImageData > & images,
const std::string & filename )
inlineprivate

Definition at line 350 of file DirectoryWindow.h.

351 {
352 return std::find_if(images.begin(), images.end(), [&filename](const ImageData& image)
353 { return image.filename == filename; });
354 }

◆ generateTextureIDs()

void DirectoryWindow::generateTextureIDs ( )
inline

Definition at line 49 of file DirectoryWindow.h.

50 {
51 // Wait for the monitoring thread to finish one full loop
52 //{
53 // std::shared_lock<std::shared_mutex> lock(imagesMutex); // Shared lock: waits if the thread is still writing
54 // if (!first_scan_done) {
55 // lock.unlock();
56 // std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait and retry
57 // generateTextureIDs(); // Retry generating textures
58 // return;
59 // }
60 //}
61
62 while (true)
63 {
64 {
65 std::shared_lock<std::shared_mutex> lock(imagesMutex);
67 break;
68 }
69 std::this_thread::sleep_for(std::chrono::milliseconds(100));
70 }
71
72 // Now generate textures
73 std::unique_lock<std::shared_mutex> lock(imagesMutex); // Unique lock to write (generate textures)
74 for (auto& image : images)
75 {
76 if (image.textureID == 0)
77 {
78 std::string path_file = directoryPath + "\\" + image.filename;
79 image = LoadTextureFromFile(path_file.c_str());
80 }
81 }
82 }
std::atomic< bool > first_scan_done
Here is the call graph for this function:

◆ getGlobalSizeSlider()

float DirectoryWindow::getGlobalSizeSlider ( )
inline

Definition at line 249 of file DirectoryWindow.h.

250 {
251 return global_size_slider;
252 }

◆ getImageByPath()

ImageData DirectoryWindow::getImageByPath ( const std::string & file_name)
inline

Definition at line 229 of file DirectoryWindow.h.

230 {
231 auto it = std::find_if(images.begin(), images.end(), [&](const ImageData& image)
232 {
233 // Check if image.filename is a substring at the end of file_name
234 if (file_name.size() >= image.filename.size()) {
235 return file_name.compare(file_name.size() - image.filename.size(), image.filename.size(), image.filename) == 0;
236 }
237 return false; });
238
239 if (it != images.end())
240 {
241 return *it;
242 }
243 else
244 {
245 throw std::runtime_error("Image not found: " + file_name);
246 }
247 }

◆ getSelectedImage()

ImageData DirectoryWindow::getSelectedImage ( ) const
inline

Definition at line 199 of file DirectoryWindow.h.

200 {
201 return selected_image;
202 }

◆ LoadTextureFromFile()

ImageData DirectoryWindow::LoadTextureFromFile ( const char * path)
inlineprivate

Definition at line 411 of file DirectoryWindow.h.

412 {
413
414 // Record the start time using std::chrono::high_resolution_clock
415 //std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
416
417 int width, height, nrChannels;
418 unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 4);
419 stbi_set_flip_vertically_on_load(0); // Flip images vertically if needed
420 if (!data)
421 {
422 std::cerr << "Failed to load texture: " << path << std::endl;
423 return ImageData(0, glm::vec2(), "");
424 }
425
426 GLuint textureID[1];
427 glGenTextures(1, textureID);
428 glBindTexture(GL_TEXTURE_2D, textureID[0]);
429
430 // Set the texture data
431 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
432
433 GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
434 GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
435 GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
436 GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
437
438 stbi_image_free(data);
439
440 // Record the end time using std::chrono::high_resolution_clock
441 //std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
442
443 // Calculate the duration in milliseconds
444 //std::chrono::seconds duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
445
446 // Output the duration in milliseconds
447 //std::cout << "Operation for file "<< path <<"took " << duration.count() << " seconds" << std::endl;
448
449 return ImageData(textureID[0], glm::vec2(width, height), path);
450 }
#define GLCall(x)
Definition Renderer.h:12
Here is the caller graph for this function:

◆ monitorDirectory()

void DirectoryWindow::monitorDirectory ( const std::string & path)
inlineprivate

Definition at line 356 of file DirectoryWindow.h.

357 {
358 using namespace std::chrono_literals;
359
360 // Keep only a simple "known file list" of filenames (no GL/state here)
361 std::vector<std::string> knownFiles;
362
363 while (running.load(std::memory_order_relaxed))
364 {
365 std::vector<std::string> currentFiles;
366 try
367 {
368 for (const auto& entry : std::filesystem::directory_iterator(path))
369 {
370 if (entry.is_regular_file())
371 {
372 currentFiles.push_back(entry.path().filename().string());
373 }
374 }
375 }
376 catch (const std::filesystem::filesystem_error& e)
377 {
378 std::cerr << "[DirectoryWindow] Filesystem error: " << e.what() << std::endl;
379 }
380
381 // Diff: removed = known - current
382 for (const auto& name : knownFiles)
383 {
384 if (std::find(currentFiles.begin(), currentFiles.end(), name) == currentFiles.end())
385 {
386 std::lock_guard<std::mutex> qlk(pendingMtx);
387 pendingRemovals.emplace_back(name);
388 }
389 }
390
391 // Diff: added = current - known
392 for (const auto& name : currentFiles)
393 {
394 if (std::find(knownFiles.begin(), knownFiles.end(), name) == knownFiles.end())
395 {
396 const std::string full = (std::filesystem::path(path) / name).string();
397 std::lock_guard<std::mutex> qlk(pendingMtx);
398 pendingAddPaths.emplace_back(name, full);
399 }
400 }
401
402 knownFiles = std::move(currentFiles);
403
404 // First pass complete → allow generateTextureIDs() to proceed
405 first_scan_done = true;
406
407 std::this_thread::sleep_for(2s);
408 }
409 }
std::atomic< bool > running
Here is the caller graph for this function:

◆ renderDirectory()

void DirectoryWindow::renderDirectory ( )
inline

Definition at line 84 of file DirectoryWindow.h.

85 {
86 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoCollapse;
88 {
89 window_flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize;
90 }
91
92 ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetIO().DisplaySize.x * 0.1, ImGui::GetIO().DisplaySize.y * 0.1), ImVec2(ImGui::GetIO().DisplaySize.x - 200, ImGui::GetIO().DisplaySize.y));
93 ImGui::Begin(directoryName.c_str(), NULL, window_flags);
94 ImGui::Text("Path: %s", directoryPath.c_str());
95
96 // --- Fixed header (no scrolling) ---
98 {
99 float minScale = 0.10f, maxScale = 10.0f;
100 ImGui::Separator();
101 ImGui::TextUnformatted("Default Marker Size Scale");
102 ImGui::SameLine();
103 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
104 if (ImGui::SliderFloat("##marker_size_slider", &global_size_slider, minScale, maxScale, "x%.2f"))
105 {
106 // Clamp just in case
107 if (global_size_slider < minScale)
108 global_size_slider = minScale;
109 if (global_size_slider > maxScale)
110 global_size_slider = maxScale;
111 }
112 }
113
114 ImGui::Separator();
115
116 float minScale = 25.0f, maxScale = 550.0f;
117 ImGui::Separator();
118 ImGui::TextUnformatted("Directory Thumb Pixel Size");
119 ImGui::SameLine();
120 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
121 if (ImGui::SliderFloat("##image_size_slider", &imageSize, minScale, maxScale, "x%.2f"))
122 {
123 // Clamp just in case
124 if (imageSize < minScale)
125 imageSize = minScale;
126 if (imageSize > maxScale)
127 imageSize = maxScale;
128 }
129
130 ImGui::Separator();
131
132 ImGui::BeginChild("DirectoryScrollRegion", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
133 float window_width = ImGui::GetWindowWidth();
134 ImVec2 content_region = ImGui::GetContentRegionAvail();
135 int columns = (int)(content_region.x / imageSize);
136 if (columns <= 0)
137 columns = 1;
138 int count = 0;
139 {
140
141 std::shared_lock<std::shared_mutex> lock(imagesMutex);
142 for (auto& image : images)
143 {
144 if (count % columns != 0)
145 ImGui::SameLine();
146 std::string path_file = directoryPath + "\\" + image.filename.c_str();
147 if (image.textureID == 0)
148 {
149 image = LoadTextureFromFile(path_file.c_str());
150 }
151
152 ImGui::BeginGroup();
153 ImGui::PushID(count);
154
156 {
157 // Add drag-and-drop functionality for marker images
158 if (ImGui::ImageButton((void*)(intptr_t)image.textureID, ImVec2(imageSize, imageSize)))
159 {
160 selected_image = image;
161 std::cout << "Selected Image: " << image.filename << " | " << image.textureID << std::endl;
162 ImGui::OpenPopup("Image Popup");
163 }
164
165 // Begin the drag source for the markers
166 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
167 {
168 ImGui::SetDragDropPayload("MARKER_IMAGE", &image, sizeof(image)); // Attach the marker image payload
169 ImGui::Text("Drag Marker: %s", image.filename.c_str()); // Tooltip while dragging
170 ImGui::EndDragDropSource();
171 }
172 }
173 else
174 {
175 if (ImGui::ImageButton((void*)(intptr_t)image.textureID, ImVec2(imageSize, imageSize)))
176 {
177 selected_image = image;
178 std::cout << "Selected Image: " << image.filename << " | " << image.textureID << std::endl;
179 ImGui::OpenPopup("Image Popup");
180 }
181
182 if (ImGui::BeginPopup("Image Popup"))
183 {
184 ImGui::Text("File: %s", image.filename.c_str());
185 ImGui::EndPopup();
186 }
187 }
188 ImGui::TextWrapped("%s", TruncateString(image.filename.c_str(), 16).c_str());
189 ImGui::PopID();
190 ImGui::EndGroup();
191 count++;
192 }
193 }
194 ImGui::EndChild();
195 ImGui::End();
196 }
std::string TruncateString(const std::string &str, size_t maxLength)
Here is the call graph for this function:

◆ startMonitoring()

void DirectoryWindow::startMonitoring ( )
inline

Definition at line 210 of file DirectoryWindow.h.

211 {
213 }
std::thread monitorThread
void monitorDirectory(const std::string &path)
Here is the call graph for this function:

◆ stopMonitoring()

void DirectoryWindow::stopMonitoring ( )
inline

Definition at line 215 of file DirectoryWindow.h.

216 {
217 running.exchange(false, std::memory_order_relaxed);
218 if (monitorThread.joinable())
219 {
220 monitorThread.join();
221 }
222 }
Here is the caller graph for this function:

◆ TruncateString()

std::string DirectoryWindow::TruncateString ( const std::string & str,
size_t maxLength )
inline

Definition at line 39 of file DirectoryWindow.h.

40 {
41 if (str.length() > maxLength)
42 {
43 return str.substr(0, maxLength) + "...";
44 }
45 return str;
46 }
Here is the caller graph for this function:

Member Data Documentation

◆ directoryName

std::string DirectoryWindow::directoryName

Definition at line 255 of file DirectoryWindow.h.

◆ directoryPath

std::string DirectoryWindow::directoryPath

Definition at line 254 of file DirectoryWindow.h.

◆ first_scan_done

std::atomic<bool> DirectoryWindow::first_scan_done {true}
private

Definition at line 340 of file DirectoryWindow.h.

340{true};

◆ global_size_slider

float DirectoryWindow::global_size_slider = 1.0f
private

Definition at line 343 of file DirectoryWindow.h.

◆ images

std::vector<ImageData> DirectoryWindow::images
private

Definition at line 337 of file DirectoryWindow.h.

◆ imageSize

float DirectoryWindow::imageSize = 128.0f
private

Definition at line 344 of file DirectoryWindow.h.

◆ imagesMutex

std::shared_mutex DirectoryWindow::imagesMutex
private

Definition at line 341 of file DirectoryWindow.h.

◆ kind_

DirectoryKind DirectoryWindow::kind_
private

Definition at line 342 of file DirectoryWindow.h.

◆ monitorThread

std::thread DirectoryWindow::monitorThread
private

Definition at line 338 of file DirectoryWindow.h.

◆ pendingAddPaths

std::vector<std::pair<std::string, std::string> > DirectoryWindow::pendingAddPaths
private

Definition at line 347 of file DirectoryWindow.h.

◆ pendingMtx

std::mutex DirectoryWindow::pendingMtx
private

Definition at line 345 of file DirectoryWindow.h.

◆ pendingRemovals

std::vector<std::string> DirectoryWindow::pendingRemovals
private

Definition at line 346 of file DirectoryWindow.h.

◆ running

std::atomic<bool> DirectoryWindow::running {true}
private

Definition at line 339 of file DirectoryWindow.h.

339{true};

◆ selected_image

ImageData DirectoryWindow::selected_image = {0, glm::vec2(), ""}
private

Definition at line 336 of file DirectoryWindow.h.

336{0, glm::vec2(), ""}; // Armazena a imagem selecionada

The documentation for this class was generated from the following file: