comparison postdog/gui_window_file_dialog.h @ 116:7bd795bac997

[Postdog] Added scrollable area to inputs and history files, buttons to delete and view.
author June Park <parkjune1995@gmail.com>
date Wed, 07 Jan 2026 04:52:17 -0800
parents
children
comparison
equal deleted inserted replaced
115:96db6c3f38d6 116:7bd795bac997
1 /*******************************************************************************************
2 *
3 * Window File Dialog v1.2 - Modal file dialog to open/save files
4 *
5 * MODULE USAGE:
6 * #define GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION
7 * #include "gui_window_file_dialog.h"
8 *
9 * INIT: GuiWindowFileDialogState state = GuiInitWindowFileDialog();
10 * DRAW: GuiWindowFileDialog(&state);
11 *
12 * NOTE: This module depends on some raylib file system functions:
13 * - LoadDirectoryFiles()
14 * - UnloadDirectoryFiles()
15 * - GetWorkingDirectory()
16 * - DirectoryExists()
17 * - FileExists()
18 *
19 * LICENSE: zlib/libpng
20 *
21 * Copyright (c) 2019-2024 Ramon Santamaria (@raysan5)
22 *
23 * This software is provided "as-is", without any express or implied warranty. In no event
24 * will the authors be held liable for any damages arising from the use of this software.
25 *
26 * Permission is granted to anyone to use this software for any purpose, including commercial
27 * applications, and to alter it and redistribute it freely, subject to the following restrictions:
28 *
29 * 1. The origin of this software must not be misrepresented; you must not claim that you
30 * wrote the original software. If you use this software in a product, an acknowledgment
31 * in the product documentation would be appreciated but is not required.
32 *
33 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
34 * as being the original software.
35 *
36 * 3. This notice may not be removed or altered from any source distribution.
37 *
38 **********************************************************************************************/
39
40 #include "third_party/raylib/include/raylib.h"
41
42 #ifndef GUI_WINDOW_FILE_DIALOG_H
43 #define GUI_WINDOW_FILE_DIALOG_H
44
45 // Gui file dialog context data
46 typedef struct {
47
48 // Window management variables
49 bool windowActive;
50 Rectangle windowBounds;
51 Vector2 panOffset;
52 bool dragMode;
53 bool supportDrag;
54
55 // UI variables
56 bool dirPathEditMode;
57 char dirPathText[1024];
58
59 int filesListScrollIndex;
60 bool filesListEditMode;
61 int filesListActive;
62
63 bool fileNameEditMode;
64 char fileNameText[1024];
65 bool SelectFilePressed;
66 bool CancelFilePressed;
67 int fileTypeActive;
68 int itemFocused;
69
70 // Custom state variables
71 FilePathList dirFiles;
72 char filterExt[256];
73 char dirPathTextCopy[1024];
74 char fileNameTextCopy[1024];
75
76 int prevFilesListActive;
77
78 bool saveFileMode;
79
80 } GuiWindowFileDialogState;
81
82 #ifdef __cplusplus
83 extern "C" { // Prevents name mangling of functions
84 #endif
85
86 //----------------------------------------------------------------------------------
87 // Defines and Macros
88 //----------------------------------------------------------------------------------
89 //...
90
91 //----------------------------------------------------------------------------------
92 // Types and Structures Definition
93 //----------------------------------------------------------------------------------
94 // ...
95
96 //----------------------------------------------------------------------------------
97 // Global Variables Definition
98 //----------------------------------------------------------------------------------
99 //...
100
101 //----------------------------------------------------------------------------------
102 // Module Functions Declaration
103 //----------------------------------------------------------------------------------
104 GuiWindowFileDialogState InitGuiWindowFileDialog(const char *initPath);
105 void GuiWindowFileDialog(GuiWindowFileDialogState *state);
106
107 #ifdef __cplusplus
108 }
109 #endif
110
111 #endif // GUI_WINDOW_FILE_DIALOG_H
112
113 /***********************************************************************************
114 *
115 * GUI_WINDOW_FILE_DIALOG IMPLEMENTATION
116 *
117 ************************************************************************************/
118 #if defined(GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION)
119 #include "third_party/raylib/include/raygui.h"
120
121 #include <string.h> // Required for: strcpy()
122
123 //----------------------------------------------------------------------------------
124 // Defines and Macros
125 //----------------------------------------------------------------------------------
126 #define MAX_DIRECTORY_FILES 2048
127 #define MAX_ICON_PATH_LENGTH 512
128 #ifdef _WIN32
129 #define PATH_SEPERATOR "\\"
130 #else
131 #define PATH_SEPERATOR "/"
132 #endif
133
134 //----------------------------------------------------------------------------------
135 // Types and Structures Definition
136 //----------------------------------------------------------------------------------
137 #if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
138 // Detailed file info type
139 typedef struct FileInfo {
140 const char *name;
141 int size;
142 int modTime;
143 int type;
144 int icon;
145 } FileInfo;
146 #else
147 // Filename only
148 typedef char *FileInfo; // Files are just a path string
149 #endif
150
151 //----------------------------------------------------------------------------------
152 // Global Variables Definition
153 //----------------------------------------------------------------------------------
154 FileInfo *dirFilesIcon = NULL; // Path string + icon (for fancy drawing)
155
156 //----------------------------------------------------------------------------------
157 // Internal Module Functions Definition
158 //----------------------------------------------------------------------------------
159 // Read files in new path
160 static void ReloadDirectoryFiles(GuiWindowFileDialogState *state);
161
162 #if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
163 // List View control for files info with extended parameters
164 static int GuiListViewFiles(Rectangle bounds, FileInfo *files, int count, int *focus, int *scrollIndex, int active);
165 #endif
166
167 //----------------------------------------------------------------------------------
168 // Module Functions Definition
169 //----------------------------------------------------------------------------------
170 GuiWindowFileDialogState InitGuiWindowFileDialog(const char *initPath)
171 {
172 GuiWindowFileDialogState state = { 0 };
173
174 // Init window data
175 state.windowBounds = (Rectangle){ GetScreenWidth()/2 - 440/2, GetScreenHeight()/2 - 310/2, 440, 310 };
176 state.windowActive = false;
177 state.supportDrag = true;
178 state.dragMode = false;
179 state.panOffset = (Vector2){ 0, 0 };
180
181 // Init path data
182 state.dirPathEditMode = false;
183 state.filesListActive = -1;
184 state.prevFilesListActive = state.filesListActive;
185 state.filesListScrollIndex = 0;
186
187 state.fileNameEditMode = false;
188
189 state.SelectFilePressed = false;
190 state.CancelFilePressed = false;
191
192 state.fileTypeActive = 0;
193
194 strcpy(state.fileNameText, "\0");
195
196 // Custom variables initialization
197 if (initPath && DirectoryExists(initPath))
198 {
199 strcpy(state.dirPathText, initPath);
200 }
201 else if (initPath && FileExists(initPath))
202 {
203 strcpy(state.dirPathText, GetDirectoryPath(initPath));
204 strcpy(state.fileNameText, GetFileName(initPath));
205 }
206 else strcpy(state.dirPathText, GetWorkingDirectory());
207
208 // TODO: Why we keep a copy?
209 strcpy(state.dirPathTextCopy, state.dirPathText);
210 strcpy(state.fileNameTextCopy, state.fileNameText);
211
212 state.filterExt[0] = '\0';
213 //strcpy(state.filterExt, "all");
214
215 state.dirFiles.count = 0;
216
217 return state;
218 }
219
220 // Update and draw file dialog
221 void GuiWindowFileDialog(GuiWindowFileDialogState *state)
222 {
223 if (state->windowActive)
224 {
225 // Update window dragging
226 //----------------------------------------------------------------------------------------
227 if (state->supportDrag)
228 {
229 Vector2 mousePosition = GetMousePosition();
230
231 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
232 {
233 // Window can be dragged from the top window bar
234 if (CheckCollisionPointRec(mousePosition, (Rectangle){ state->windowBounds.x, state->windowBounds.y, (float)state->windowBounds.width, RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }))
235 {
236 state->dragMode = true;
237 state->panOffset.x = mousePosition.x - state->windowBounds.x;
238 state->panOffset.y = mousePosition.y - state->windowBounds.y;
239 }
240 }
241
242 if (state->dragMode)
243 {
244 state->windowBounds.x = (mousePosition.x - state->panOffset.x);
245 state->windowBounds.y = (mousePosition.y - state->panOffset.y);
246
247 // Check screen limits to avoid moving out of screen
248 if (state->windowBounds.x < 0) state->windowBounds.x = 0;
249 else if (state->windowBounds.x > (GetScreenWidth() - state->windowBounds.width)) state->windowBounds.x = GetScreenWidth() - state->windowBounds.width;
250
251 if (state->windowBounds.y < 0) state->windowBounds.y = 0;
252 else if (state->windowBounds.y > (GetScreenHeight() - state->windowBounds.height)) state->windowBounds.y = GetScreenHeight() - state->windowBounds.height;
253
254 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) state->dragMode = false;
255 }
256 }
257 //----------------------------------------------------------------------------------------
258
259 // Load dirFilesIcon and state->dirFiles lazily on windows open
260 // NOTE: They are automatically unloaded at fileDialog closing
261 //----------------------------------------------------------------------------------------
262 if (dirFilesIcon == NULL)
263 {
264 dirFilesIcon = (FileInfo *)RL_CALLOC(MAX_DIRECTORY_FILES, sizeof(FileInfo)); // Max files to read
265 for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesIcon[i] = (char *)RL_CALLOC(MAX_ICON_PATH_LENGTH, 1); // Max file name length
266 }
267
268 // Load current directory files
269 if (state->dirFiles.paths == NULL) ReloadDirectoryFiles(state);
270 //----------------------------------------------------------------------------------------
271
272 // Draw window and controls
273 //----------------------------------------------------------------------------------------
274 state->windowActive = !GuiWindowBox(state->windowBounds, "#198# Select File Dialog");
275
276 // Draw previous directory button + logic
277 if (GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 48, state->windowBounds.y + 24 + 12, 40, 24 }, "< .."))
278 {
279 // Move dir path one level up
280 strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText));
281
282 // Reload directory files (frees previous list)
283 ReloadDirectoryFiles(state);
284
285 state->filesListActive = -1;
286 memset(state->fileNameText, 0, 1024);
287 memset(state->fileNameTextCopy, 0, 1024);
288 }
289
290 // Draw current directory text box info + path editing logic
291 if (GuiTextBox((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + 24 + 12, state->windowBounds.width - 48 - 16, 24 }, state->dirPathText, 1024, state->dirPathEditMode))
292 {
293 if (state->dirPathEditMode)
294 {
295 // Verify if a valid path has been introduced
296 if (DirectoryExists(state->dirPathText))
297 {
298 // Reload directory files (frees previous list)
299 ReloadDirectoryFiles(state);
300
301 strcpy(state->dirPathTextCopy, state->dirPathText);
302 }
303 else strcpy(state->dirPathText, state->dirPathTextCopy);
304 }
305
306 state->dirPathEditMode = !state->dirPathEditMode;
307 }
308
309 // List view elements are aligned left
310 int prevTextAlignment = GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT);
311 int prevElementsHeight = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT);
312 GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
313 GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 24);
314 # if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
315 state->filesListActive = GuiListViewFiles((Rectangle){ state->position.x + 8, state->position.y + 48 + 20, state->windowBounds.width - 16, state->windowBounds.height - 60 - 16 - 68 }, fileInfo, state->dirFiles.count, &state->itemFocused, &state->filesListScrollIndex, state->filesListActive);
316 # else
317 GuiListViewEx((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + 48 + 20, state->windowBounds.width - 16, state->windowBounds.height - 60 - 16 - 68 },
318 (const char**)dirFilesIcon, state->dirFiles.count, &state->filesListScrollIndex, &state->filesListActive, &state->itemFocused);
319 # endif
320 GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, prevTextAlignment);
321 GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, prevElementsHeight);
322
323 // Check if a path has been selected, if it is a directory, move to that directory (and reload paths)
324 if ((state->filesListActive >= 0) && (state->filesListActive != state->prevFilesListActive))
325 //&& (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_DPAD_A)))
326 {
327 strcpy(state->fileNameText, GetFileName(state->dirFiles.paths[state->filesListActive]));
328
329 if (DirectoryExists(TextFormat("%s/%s", state->dirPathText, state->fileNameText)))
330 {
331 if (TextIsEqual(state->fileNameText, "..")) strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText));
332 else strcpy(state->dirPathText, TextFormat("%s/%s", (strcmp(state->dirPathText, "/") == 0)? "" : state->dirPathText, state->fileNameText));
333
334 strcpy(state->dirPathTextCopy, state->dirPathText);
335
336 // Reload directory files (frees previous list)
337 ReloadDirectoryFiles(state);
338
339 strcpy(state->dirPathTextCopy, state->dirPathText);
340
341 state->filesListActive = -1;
342 strcpy(state->fileNameText, "\0");
343 strcpy(state->fileNameTextCopy, state->fileNameText);
344 }
345
346 state->prevFilesListActive = state->filesListActive;
347 }
348
349 // Draw bottom controls
350 //--------------------------------------------------------------------------------------
351 GuiLabel((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 68, 60, 24 }, "File name:");
352 if (GuiTextBox((Rectangle){ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 68, state->windowBounds.width - 184, 24 }, state->fileNameText, 128, state->fileNameEditMode))
353 {
354 if (*state->fileNameText)
355 {
356 // Verify if a valid filename has been introduced
357 if (FileExists(TextFormat("%s/%s", state->dirPathText, state->fileNameText)))
358 {
359 // Select filename from list view
360 for (unsigned int i = 0; i < state->dirFiles.count; i++)
361 {
362 if (TextIsEqual(state->fileNameText, state->dirFiles.paths[i]))
363 {
364 state->filesListActive = i;
365 strcpy(state->fileNameTextCopy, state->fileNameText);
366 break;
367 }
368 }
369 }
370 else if (!state->saveFileMode)
371 {
372 strcpy(state->fileNameText, state->fileNameTextCopy);
373 }
374 }
375
376 state->fileNameEditMode = !state->fileNameEditMode;
377 }
378
379 GuiLabel((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 68, 24 }, "File filter:");
380 GuiComboBox((Rectangle){ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 24 - 12, state->windowBounds.width - 184, 24 }, "All files", &state->fileTypeActive);
381
382 state->SelectFilePressed = GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 68, 96, 24 }, "Select");
383
384 if (GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 96, 24 }, "Cancel")) state->windowActive = false;
385 //--------------------------------------------------------------------------------------
386
387 // Exit on file selected
388 if (state->SelectFilePressed) state->windowActive = false;
389
390 // File dialog has been closed, free all memory before exit
391 if (!state->windowActive)
392 {
393 // Free dirFilesIcon memory
394 for (int i = 0; i < MAX_DIRECTORY_FILES; i++) RL_FREE(dirFilesIcon[i]);
395
396 RL_FREE(dirFilesIcon);
397 dirFilesIcon = NULL;
398
399 // Unload directory file paths
400 UnloadDirectoryFiles(state->dirFiles);
401
402 // Reset state variables
403 state->dirFiles.count = 0;
404 state->dirFiles.capacity = 0;
405 state->dirFiles.paths = NULL;
406 }
407 }
408 }
409
410 // Compare two files from a directory
411 static inline int FileCompare(const char *d1, const char *d2, const char *dir)
412 {
413 const bool b1 = DirectoryExists(TextFormat("%s/%s", dir, d1));
414 const bool b2 = DirectoryExists(TextFormat("%s/%s", dir, d2));
415
416 if (b1 && !b2) return -1;
417 if (!b1 && b2) return 1;
418
419 if (!FileExists(TextFormat("%s/%s", dir, d1))) return 1;
420 if (!FileExists(TextFormat("%s/%s", dir, d2))) return -1;
421
422 return strcmp(d1, d2);
423 }
424
425 // Read files in new path
426 static void ReloadDirectoryFiles(GuiWindowFileDialogState *state)
427 {
428 UnloadDirectoryFiles(state->dirFiles);
429
430 state->dirFiles = LoadDirectoryFilesEx(state->dirPathText, (state->filterExt[0] == '\0')? NULL : state->filterExt, false);
431 state->itemFocused = 0;
432
433 // Reset dirFilesIcon memory
434 for (int i = 0; i < MAX_DIRECTORY_FILES; i++) memset(dirFilesIcon[i], 0, MAX_ICON_PATH_LENGTH);
435
436 // Copy paths as icon + fileNames into dirFilesIcon
437 for (unsigned int i = 0; i < state->dirFiles.count; i++)
438 {
439 if (IsPathFile(state->dirFiles.paths[i]))
440 {
441 // Path is a file, a file icon for convenience (for some recognized extensions)
442 if (IsFileExtension(state->dirFiles.paths[i], ".png;.bmp;.tga;.gif;.jpg;.jpeg;.psd;.hdr;.qoi;.dds;.pkm;.ktx;.pvr;.astc"))
443 {
444 strcpy(dirFilesIcon[i], TextFormat("#12#%s", GetFileName(state->dirFiles.paths[i])));
445 }
446 else if (IsFileExtension(state->dirFiles.paths[i], ".wav;.mp3;.ogg;.flac;.xm;.mod;.it;.wma;.aiff"))
447 {
448 strcpy(dirFilesIcon[i], TextFormat("#11#%s", GetFileName(state->dirFiles.paths[i])));
449 }
450 else if (IsFileExtension(state->dirFiles.paths[i], ".txt;.info;.md;.nfo;.xml;.json;.c;.cpp;.cs;.lua;.py;.glsl;.vs;.fs"))
451 {
452 strcpy(dirFilesIcon[i], TextFormat("#10#%s", GetFileName(state->dirFiles.paths[i])));
453 }
454 else if (IsFileExtension(state->dirFiles.paths[i], ".exe;.bin;.raw;.msi"))
455 {
456 strcpy(dirFilesIcon[i], TextFormat("#200#%s", GetFileName(state->dirFiles.paths[i])));
457 }
458 else strcpy(dirFilesIcon[i], TextFormat("#218#%s", GetFileName(state->dirFiles.paths[i])));
459 }
460 else
461 {
462 // Path is a directory, add a directory icon
463 strcpy(dirFilesIcon[i], TextFormat("#1#%s", GetFileName(state->dirFiles.paths[i])));
464 }
465 }
466 }
467
468 #if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
469 // List View control for files info with extended parameters
470 static int GuiListViewFiles(Rectangle bounds, FileInfo *files, int count, int *focus, int *scrollIndex, int *active)
471 {
472 int result = 0;
473 GuiState state = guiState;
474 int itemFocused = (focus == NULL)? -1 : *focus;
475 int itemSelected = *active;
476
477 // Check if we need a scroll bar
478 bool useScrollBar = false;
479 if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING))*count > bounds.height) useScrollBar = true;
480
481 // Define base item rectangle [0]
482 Rectangle itemBounds = { 0 };
483 itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING);
484 itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
485 itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
486 itemBounds.height = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT);
487 if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
488
489 // Get items on the list
490 int visibleItems = bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING));
491 if (visibleItems > count) visibleItems = count;
492
493 int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex;
494 if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0;
495 int endIndex = startIndex + visibleItems;
496
497 // Update control
498 //--------------------------------------------------------------------
499 if ((state != GUI_STATE_DISABLED) && !guiLocked)
500 {
501 Vector2 mousePoint = GetMousePosition();
502
503 // Check mouse inside list view
504 if (CheckCollisionPointRec(mousePoint, bounds))
505 {
506 state = GUI_STATE_FOCUSED;
507
508 // Check focused and selected item
509 for (int i = 0; i < visibleItems; i++)
510 {
511 if (CheckCollisionPointRec(mousePoint, itemBounds))
512 {
513 itemFocused = startIndex + i;
514 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) itemSelected = startIndex + i;
515 break;
516 }
517
518 // Update item rectangle y position for next item
519 itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING));
520 }
521
522 if (useScrollBar)
523 {
524 int wheelMove = GetMouseWheelMove();
525 startIndex -= wheelMove;
526
527 if (startIndex < 0) startIndex = 0;
528 else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems;
529
530 endIndex = startIndex + visibleItems;
531 if (endIndex > count) endIndex = count;
532 }
533 }
534 else itemFocused = -1;
535
536 // Reset item rectangle y to [0]
537 itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
538 }
539 //--------------------------------------------------------------------
540
541 // Draw control
542 //--------------------------------------------------------------------
543 DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background
544 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha));
545
546 // TODO: Draw list view header with file sections: icon+name | size | type | modTime
547
548 // Draw visible items
549 for (int i = 0; i < visibleItems; i++)
550 {
551 if (state == GUI_STATE_DISABLED)
552 {
553 if ((startIndex + i) == itemSelected)
554 {
555 DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha));
556 DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), guiAlpha));
557 }
558
559 // TODO: Draw full file info line: icon+name | size | type | modTime
560
561 GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha));
562 }
563 else
564 {
565 if ((startIndex + i) == itemSelected)
566 {
567 // Draw item selected
568 DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha));
569 DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha));
570
571 GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), guiAlpha));
572 }
573 else if ((startIndex + i) == itemFocused)
574 {
575 // Draw item focused
576 DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha));
577 DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha));
578
579 GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), guiAlpha));
580 }
581 else
582 {
583 // Draw item normal
584 GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha));
585 }
586 }
587
588 // Update item rectangle y position for next item
589 itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING));
590 }
591
592 if (useScrollBar)
593 {
594 Rectangle scrollBarBounds = {
595 bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
596 bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
597 bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
598 };
599
600 // Calculate percentage of visible items and apply same percentage to scrollbar
601 float percentVisible = (float)(endIndex - startIndex)/count;
602 float sliderSize = bounds.height*percentVisible;
603
604 int prevSliderSize = GuiGetStyle(SCROLLBAR, SLIDER_WIDTH); // Save default slider size
605 int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
606 GuiSetStyle(SCROLLBAR, SLIDER_WIDTH, sliderSize); // Change slider size
607 GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed
608
609 startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems);
610
611 GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default
612 GuiSetStyle(SCROLLBAR, SLIDER_WIDTH, prevSliderSize); // Reset slider size to default
613 }
614 //--------------------------------------------------------------------
615
616 if (focus != NULL) *focus = itemFocused;
617 if (scrollIndex != NULL) *scrollIndex = startIndex;
618
619 *active = itemSelected;
620 return result;
621 }
622 #endif // USE_CUSTOM_LISTVIEW_FILEINFO
623
624 #endif // GUI_FILE_DIALOG_IMPLEMENTATION