Bläddra i källkod

new example: custom_file_dialog

raysan5 6 år sedan
förälder
incheckning
d2d370ef41

BIN
examples/custom_file_dialog/cat.png


+ 113 - 0
examples/custom_file_dialog/custom_file_dialog.c

@@ -0,0 +1,113 @@
+/*******************************************************************************************
+*
+*   raygui - custom file dialog to load image
+*
+*   DEPENDENCIES:
+*       raylib 2.6-dev  - Windowing/input management and drawing.
+*       raygui 2.6-dev  - Immediate-mode GUI controls.
+*
+*   COMPILATION (Windows - MinGW):
+*       gcc -o $(NAME_PART).exe $(FILE_NAME) -I../../src -lraylib -lopengl32 -lgdi32 -std=c99
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2019 raylib technologies (@raylibtech)
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#define RAYGUI_SUPPORT_RICONS
+#include "../../src/raygui.h"
+
+#undef RAYGUI_IMPLEMENTATION            // Avoid including raygui implementation again
+
+#define GUI_FILE_DIALOG_IMPLEMENTATION
+#include "gui_file_dialog.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main()
+{
+    // Initialization
+    //---------------------------------------------------------------------------------------
+    int screenWidth = 800;
+    int screenHeight = 560;
+    
+    InitWindow(screenWidth, screenHeight, "raygui - custom modal dialog");
+    SetExitKey(0);
+
+    // Custom file dialog
+    GuiFileDialogState fileDialogState = InitGuiFileDialog();
+
+    bool exitWindow = false;
+
+    char fileNameToLoad[512] = { 0 };
+    
+    Texture texture = { 0 };
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!exitWindow)    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        exitWindow = WindowShouldClose();
+        
+        if (fileDialogState.SelectFilePressed)
+        {
+            // Load image file (if supported extension)
+            if (IsFileExtension(fileDialogState.fileNameText, ".png"))
+            {
+                strcpy(fileNameToLoad, TextFormat("%s/%s", fileDialogState.dirPathText, fileDialogState.fileNameText));
+                UnloadTexture(texture);
+                texture = LoadTexture(fileNameToLoad);
+            }
+            
+            fileDialogState.SelectFilePressed = false;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));
+            
+            DrawTexture(texture, GetScreenWidth()/2 - texture.width/2, GetScreenHeight()/2 - texture.height/2 - 5, WHITE);
+            DrawRectangleLines(GetScreenWidth()/2 - texture.width/2, GetScreenHeight()/2 - texture.height/2 - 5, texture.width, texture.height, BLACK);
+ 
+            DrawText(fileNameToLoad, 208, GetScreenHeight() - 20, 10, GRAY);
+ 
+            // raygui: controls drawing
+            //----------------------------------------------------------------------------------
+            if (fileDialogState.fileDialogActive) GuiLock();
+
+            if (GuiButton((Rectangle){ 20, 20, 140, 30 }, GuiIconText(RICON_FILE_OPEN, "Open Image"))) fileDialogState.fileDialogActive = true;
+            
+            GuiUnlock();
+            
+            // GUI: Dialog Window
+            //--------------------------------------------------------------------------------
+            GuiFileDialog(&fileDialogState);
+            //--------------------------------------------------------------------------------
+
+            //----------------------------------------------------------------------------------
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texture);     // Unload texture
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}

+ 548 - 0
examples/custom_file_dialog/gui_file_dialog.h

@@ -0,0 +1,548 @@
+/*******************************************************************************************
+*
+*   FileDialog v1.0.0 - Modal file dialog to open/save files
+*
+*   MODULE USAGE:
+*       #define GUI_FILE_DIALOG_IMPLEMENTATION
+*       #include "gui_file_dialog.h"
+*
+*       INIT: GuiFileDialogState state = InitGuiFileDialog();
+*       DRAW: GuiFileDialog(&state);
+*
+*   LICENSE: Propietary License
+*
+*   Copyright (c) 2019 raylib technologies (@raylibtech). All Rights Reserved.
+*
+*   Unauthorized copying of this file, via any medium is strictly prohibited
+*   This project is proprietary and confidential unless the owner allows
+*   usage in any other form by expresely written permission.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+
+// WARNING: raygui implementation is expected to be defined before including this header
+#undef RAYGUI_IMPLEMENTATION
+#include "../../src/raygui.h"
+
+#ifndef GUI_FILE_DIALOG_H
+#define GUI_FILE_DIALOG_H
+
+typedef struct {
+    Vector2 position;
+    
+    bool fileDialogActive;
+    
+    bool dirBackPressed;
+    bool dirPathEditMode;
+    char dirPathText[256];
+    
+    int filesListScrollIndex;
+    bool filesListEditMode;
+    int filesListActive;
+    
+    bool fileNameEditMode;
+    char fileNameText[256];
+    bool SelectFilePressed;
+    bool CancelFilePressed;
+    int fileTypeActive;
+
+    // Custom state variables (depend on development software)
+    // NOTE: This variables should be added manually if required
+    char **dirFiles;
+    int dirFilesCount;
+
+    char filterExt[256];
+    
+    char dirPathTextCopy[256];
+    char fileNameTextCopy[256];
+    
+    int prevFilesListActive;
+
+} GuiFileDialogState;
+
+#ifdef __cplusplus
+extern "C" {            // Prevents name mangling of functions
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+//...
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+//...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+GuiFileDialogState InitGuiFileDialog(void);
+void GuiFileDialog(GuiFileDialogState *state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GUI_FILE_DIALOG_H
+
+/***********************************************************************************
+*
+*   GUI_FILE_DIALOG IMPLEMENTATION
+*
+************************************************************************************/
+#if defined(GUI_FILE_DIALOG_IMPLEMENTATION)
+
+#include "raygui.h"
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define MAX_DIRECTORY_FILES    1024
+#define MAX_DIR_PATH_LENGTH    1024
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+#if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
+// Detailed file info type
+typedef struct FileInfo {
+    const char *name;
+    int size;
+    int modTime;
+    int type;
+    int icon;
+} FileInfo;
+#endif
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+char **dirFilesIcon = NULL;
+
+//----------------------------------------------------------------------------------
+// Internal Module Functions Definition
+//----------------------------------------------------------------------------------
+// Read all filenames from directory (supported file types)
+static char **ReadDirectoryFiles(const char *dir, int *filesCount, char *filterExt);
+
+#if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
+// List View control for files info with extended parameters
+static int GuiListViewFiles(Rectangle bounds, FileInfo *files, int count, int *focus, int *scrollIndex, int active)
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+GuiFileDialogState InitGuiFileDialog(void)
+{
+    GuiFileDialogState state = { 0 };
+
+    state.position = (Vector2){ GetScreenWidth()/2 - 480/2, GetScreenHeight()/2 - 305/2 };
+    
+    state.fileDialogActive = false;
+    state.dirBackPressed = false;
+    state.dirPathEditMode = false;
+    
+    state.filesListActive = -1;
+    state.prevFilesListActive = state.filesListActive;
+    state.filesListScrollIndex = 0;
+    
+    state.fileNameEditMode = false;
+
+    state.SelectFilePressed = false;
+    state.CancelFilePressed = false;
+
+    state.fileTypeActive = 0;
+
+    // Custom variables initialization
+    strcpy(state.dirPathText, GetWorkingDirectory());
+    strcpy(state.dirPathTextCopy, state.dirPathText);
+    
+    strcpy(state.filterExt, "all");
+
+    state.dirFilesCount = 0;
+    state.dirFiles = NULL;      // NOTE: Loaded lazily on window active
+    
+    strcpy(state.fileNameText, "\0");
+    strcpy(state.fileNameTextCopy, state.fileNameText);
+
+    return state;
+}
+
+void GuiFileDialog(GuiFileDialogState *state)
+{   
+    if (state->fileDialogActive)
+    {
+        // Load dirFilesIcon and state->dirFiles lazily on windows open
+        // NOTE: they are automatically unloaded at fileDialog closing
+        //------------------------------------------------------------------------------------
+        if (dirFilesIcon == NULL)
+        {
+            dirFilesIcon = (char **)malloc(MAX_DIRECTORY_FILES*sizeof(char *));    // Max files to read
+            for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesIcon[i] = (char *)calloc(MAX_DIR_PATH_LENGTH, 1);    // Max file name length
+        }
+    
+        if (state->dirFiles == NULL) state->dirFiles = ReadDirectoryFiles(state->dirPathText, &state->dirFilesCount, state->filterExt);
+        //------------------------------------------------------------------------------------
+
+        DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), 0.85f));
+        state->fileDialogActive = !GuiWindowBox((Rectangle){ state->position.x + 0, state->position.y + 0, 480, 310 }, "#198#Select File Dialog");
+        
+        if (GuiButton((Rectangle){ state->position.x + 430, state->position.y + 35, 40, 25 }, "< .."))
+        {
+            // Move dir path one level up
+            strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText));
+            
+            // Free previous dirFiles (reloaded by ReadDirectoryFiles())
+            for (int i = 0; i < state->dirFilesCount; i++) free(state->dirFiles[i]);
+            free(state->dirFiles);
+            
+            // Read files in the new path
+            state->dirFiles = ReadDirectoryFiles(state->dirPathText, &state->dirFilesCount, state->filterExt);
+            
+            state->filesListActive = -1;
+            strcpy(state->fileNameText, "\0");
+            strcpy(state->fileNameTextCopy, state->fileNameText);
+        }
+        
+        if (GuiTextBox((Rectangle){ state->position.x + 10, state->position.y + 35, 410, 25 }, state->dirPathText, 256, state->dirPathEditMode)) 
+        {
+            if (state->dirPathEditMode)
+            {
+                // Verify if a valid path has been introduced
+                if (DirectoryExists(state->dirPathText))
+                {
+                    // Free previous dirFiles (reloaded by ReadDirectoryFiles())
+                    for (int i = 0; i < state->dirFilesCount; i++) free(state->dirFiles[i]);
+                    free(state->dirFiles);
+                    
+                    // Read files in new path
+                    state->dirFiles = ReadDirectoryFiles(state->dirPathText, &state->dirFilesCount, state->filterExt);
+                    
+                    strcpy(state->dirPathTextCopy, state->dirPathText);
+                }
+                else strcpy(state->dirPathText, state->dirPathTextCopy);
+            }
+            
+            state->dirPathEditMode = !state->dirPathEditMode;
+        }
+        
+        int prevTextAlignment = GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT);
+        int prevElementsHeight = GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT);
+        GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
+        GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, 24);
+        
+        // TODO: ListViewElements should be aligned left
+        state->filesListActive = GuiListViewEx((Rectangle){ state->position.x + 10, state->position.y + 70, 460, 164 }, dirFilesIcon, state->dirFilesCount, NULL, &state->filesListScrollIndex, state->filesListActive);
+        
+        GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, prevTextAlignment);
+        GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, prevElementsHeight);
+        
+        if ((state->filesListActive >= 0) && (state->filesListActive != state->prevFilesListActive))
+        {
+            strcpy(state->fileNameText, state->dirFiles[state->filesListActive]);
+
+            if (DirectoryExists(TextFormat("%s\\%s", state->dirPathText, state->fileNameText)))
+            {
+                if (TextIsEqual(state->fileNameText, "..")) strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText));
+                else strcpy(state->dirPathText, TextFormat("%s\\%s", state->dirPathText, state->fileNameText));
+                
+                strcpy(state->dirPathTextCopy, state->dirPathText);
+                
+                // Free previous dirFiles (reloaded by ReadDirectoryFiles())
+                for (int i = 0; i < state->dirFilesCount; i++) free(state->dirFiles[i]);
+                free(state->dirFiles);
+                
+                // Read files in new path
+                state->dirFiles = ReadDirectoryFiles(state->dirPathText, &state->dirFilesCount, state->filterExt);
+                
+                strcpy(state->dirPathTextCopy, state->dirPathText);
+                
+                state->filesListActive = -1;
+                strcpy(state->fileNameText, "\0");
+                strcpy(state->fileNameTextCopy, state->fileNameText);
+            }
+
+            state->prevFilesListActive = state->filesListActive;
+        }
+        
+        GuiLabel((Rectangle){ state->position.x + 10, state->position.y + 245, 68, 25 }, "File name:");
+        
+        if (GuiTextBox((Rectangle){ state->position.x + 75, state->position.y + 245, 275, 25 }, state->fileNameText, 128, state->fileNameEditMode)) 
+        {
+            if (state->fileNameText)
+            {
+                // Verify if a valid filename has been introduced
+                if (FileExists(TextFormat("%s/%s", state->dirPathText, state->fileNameText)))
+                {
+                    // Select filename from list view
+                    for (int i = 0; i < state->dirFilesCount; i++)
+                    {
+                        if (TextIsEqual(state->fileNameText, state->dirFiles[i]))
+                        {
+                            state->filesListActive = i;
+                            strcpy(state->fileNameTextCopy, state->fileNameText);
+                            break;
+                        }
+                    }
+                }
+                else strcpy(state->fileNameText, state->fileNameTextCopy);
+            }
+            
+            state->fileNameEditMode = !state->fileNameEditMode;
+        }
+
+        state->fileTypeActive = GuiComboBox((Rectangle){ state->position.x + 75, state->position.y + 275, 275, 25 }, "All files", state->fileTypeActive);
+        GuiLabel((Rectangle){ state->position.x + 10, state->position.y + 275, 68, 25 }, "File filter:");
+        
+        state->SelectFilePressed = GuiButton((Rectangle){ state->position.x + 360, state->position.y + 245, 110, 25 }, "Select");
+        
+        if (state->SelectFilePressed) state->fileDialogActive = false;
+        
+        if (GuiButton((Rectangle){ state->position.x + 360, state->position.y + 275, 110, 25 }, "Cancel")) state->fileDialogActive = false;
+
+        // File dialog has been closed!
+        if (!state->fileDialogActive)
+        {
+            // Free dirFiles memory
+            for (int i = 0; i < state->dirFilesCount; i++) 
+            {
+                free(state->dirFiles[i]);
+                free(dirFilesIcon[i]);
+            }
+            
+            free(state->dirFiles);
+            free(dirFilesIcon);
+            
+            dirFilesIcon = NULL;
+            state->dirFiles = NULL;
+        }
+    }
+}
+
+// Read all filenames from directory (supported file types)
+static char **ReadDirectoryFiles(const char *dir, int *filesCount, char *filterExt)
+{
+    int validFilesCount = 0;
+    char **validFiles = (char **)malloc(MAX_DIRECTORY_FILES*sizeof(char *));    // Max files to read
+    for (int i = 0; i < MAX_DIRECTORY_FILES; i++) validFiles[i] = (char *)malloc(MAX_DIR_PATH_LENGTH);    // Max file name length
+    
+    int filterExtCount = 0;
+    const char **extensions = GuiTextSplit(filterExt, &filterExtCount, NULL);
+    bool filterExtensions = true;
+    
+    int dirFilesCount = 0;
+    char **files = GetDirectoryFiles(dir, &dirFilesCount);
+    
+    if (TextIsEqual(extensions[0], "all")) filterExtensions = false;
+
+    for (int i = 0; (i < dirFilesCount) && (validFilesCount < MAX_DIRECTORY_FILES); i++)
+    {
+        if (TextIsEqual(files[i], ".")) continue;
+        
+        if (!filterExtensions)
+        {
+            strcpy(validFiles[validFilesCount], files[i]);
+            
+            // Only filter files by extensions, directories should be available
+            if (DirectoryExists(TextFormat("%s\\%s", dir, files[i]))) strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 1, files[i]));
+            else 
+            {
+                // TODO: Assign custom filetype icons depending on file extension (image, audio, text, video, models...)
+                
+                if (IsFileExtension(files[i], ".png")) strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 12, files[i]));
+                else strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 10, files[i]));
+            }
+
+            validFilesCount++;
+        }
+        else
+        {
+            for (int j = 0; j < filterExtCount; j++)
+            {
+                // Check file type extensions supported
+                // NOTE: We just store valid files list
+                if (IsFileExtension(files[i], extensions[j]))
+                {
+                    // TODO: Assign custom filetype icons depending on file extension (image, audio, text, video, models...)
+
+                    if (IsFileExtension(files[i], ".png")) strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 12, files[i]));
+                    else strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 10, files[i]));
+                    
+                    validFilesCount++;
+                }
+            }
+        }
+    }
+    
+    // TODO: Sort files and directories: dir by name + files by name
+
+    ClearDirectoryFiles();
+    
+    *filesCount = validFilesCount;
+    return validFiles;
+}
+
+#if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
+// List View control for files info with extended parameters
+static int GuiListViewFiles(Rectangle bounds, FileInfo *files, int count, int *focus, int *scrollIndex, int active)
+{
+    GuiControlState state = guiState;
+    int itemFocused = (focus == NULL)? -1 : *focus;
+    int itemSelected = active;
+    
+    // Check if we need a scroll bar
+    bool useScrollBar = false;
+    if ((GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING))*count > bounds.height) useScrollBar = true;
+    
+    // Define base item rectangle [0]
+    Rectangle itemBounds = { 0 };
+    itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING);
+    itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.height = GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT);
+    if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
+    
+    // Get items on the list
+    int visibleItems = bounds.height/(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING));
+    if (visibleItems > count) visibleItems = count;
+    
+    int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex;
+    if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0;
+    int endIndex = startIndex + visibleItems;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != GUI_STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+        
+        // Check mouse inside list view
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            state = GUI_STATE_FOCUSED;
+            
+            // Check focused and selected item
+            for (int i = 0; i < visibleItems; i++)
+            {
+                if (CheckCollisionPointRec(mousePoint, itemBounds))
+                {
+                    itemFocused = startIndex + i;
+                    if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) itemSelected = startIndex + i;
+                    break;
+                }
+                
+                // Update item rectangle y position for next item
+                itemBounds.y += (GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING));
+            }
+            
+            if (useScrollBar)
+            {
+                int wheelMove = GetMouseWheelMove();
+                startIndex -= wheelMove;
+                    
+                if (startIndex < 0) startIndex = 0;
+                else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems;
+
+                endIndex = startIndex + visibleItems;
+                if (endIndex > count) endIndex = count;
+            }
+        }
+        else itemFocused = -1;
+        
+        // Reset item rectangle y to [0]
+        itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    }
+    //--------------------------------------------------------------------
+    
+    // Draw control
+    //--------------------------------------------------------------------
+    DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));     // Draw background
+    DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha));
+
+    // TODO: Draw list view header with file sections: icon+name | size | type | modTime
+    
+    // Draw visible items
+    for (int i = 0; i < visibleItems; i++)
+    {
+        if (state == GUI_STATE_DISABLED)
+        {
+            if ((startIndex + i) == itemSelected)
+            {
+                DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha));
+                DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), guiAlpha));
+            }
+            
+            // TODO: Draw full file info line: icon+name | size | type | modTime 
+            
+            GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha));
+        }
+        else
+        {
+            if ((startIndex + i) == itemSelected)
+            {
+                // Draw item selected
+                DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha));
+                DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha));
+                
+                GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), guiAlpha));
+            }
+            else if ((startIndex + i) == itemFocused)
+            {
+                // Draw item focused
+                DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha));
+                DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha));
+                
+                GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), guiAlpha));
+            }
+            else
+            {
+                // Draw item normal
+                GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha));
+            }
+        }
+
+        // Update item rectangle y position for next item
+        itemBounds.y += (GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING));
+    }
+
+    if (useScrollBar)
+    {
+        Rectangle scrollBarBounds = { 
+            bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), 
+            bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), 
+            bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) 
+        };
+
+        // Calculate percentage of visible items and apply same percentage to scrollbar
+        float percentVisible = (float)(endIndex - startIndex)/count;
+        float sliderSize = bounds.height*percentVisible;
+
+        int prevSliderSize = GuiGetStyle(SCROLLBAR, SLIDER_SIZE);   // Save default slider size
+        int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
+        GuiSetStyle(SCROLLBAR, SLIDER_SIZE, sliderSize);            // Change slider size
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed
+
+        startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems);
+
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default
+        GuiSetStyle(SCROLLBAR, SLIDER_SIZE, prevSliderSize); // Reset slider size to default
+    }
+    //--------------------------------------------------------------------
+    
+    if (focus != NULL) *focus = itemFocused;
+    if (scrollIndex != NULL) *scrollIndex = startIndex;
+    
+    return itemSelected;
+}
+#endif // USE_CUSTOM_LISTVIEW_FILEINFO
+
+#endif // GUI_FILE_DIALOG_IMPLEMENTATION