Mercurial
view postdog/gui_window_file_dialog.h @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 7bd795bac997 |
| children |
line wrap: on
line source
/******************************************************************************************* * * Window File Dialog v1.2 - Modal file dialog to open/save files * * MODULE USAGE: * #define GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION * #include "gui_window_file_dialog.h" * * INIT: GuiWindowFileDialogState state = GuiInitWindowFileDialog(); * DRAW: GuiWindowFileDialog(&state); * * NOTE: This module depends on some raylib file system functions: * - LoadDirectoryFiles() * - UnloadDirectoryFiles() * - GetWorkingDirectory() * - DirectoryExists() * - FileExists() * * LICENSE: zlib/libpng * * Copyright (c) 2019-2024 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, including commercial * applications, and to alter it and redistribute it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim that you * wrote the original software. If you use this software in a product, an acknowledgment * in the product documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be misrepresented * as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * **********************************************************************************************/ #include "third_party/raylib/include/raylib.h" #ifndef GUI_WINDOW_FILE_DIALOG_H #define GUI_WINDOW_FILE_DIALOG_H // Gui file dialog context data typedef struct { // Window management variables bool windowActive; Rectangle windowBounds; Vector2 panOffset; bool dragMode; bool supportDrag; // UI variables bool dirPathEditMode; char dirPathText[1024]; int filesListScrollIndex; bool filesListEditMode; int filesListActive; bool fileNameEditMode; char fileNameText[1024]; bool SelectFilePressed; bool CancelFilePressed; int fileTypeActive; int itemFocused; // Custom state variables FilePathList dirFiles; char filterExt[256]; char dirPathTextCopy[1024]; char fileNameTextCopy[1024]; int prevFilesListActive; bool saveFileMode; } GuiWindowFileDialogState; #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- //... //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- // ... //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- //... //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- GuiWindowFileDialogState InitGuiWindowFileDialog(const char *initPath); void GuiWindowFileDialog(GuiWindowFileDialogState *state); #ifdef __cplusplus } #endif #endif // GUI_WINDOW_FILE_DIALOG_H /*********************************************************************************** * * GUI_WINDOW_FILE_DIALOG IMPLEMENTATION * ************************************************************************************/ #if defined(GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION) #include "third_party/raylib/include/raygui.h" #include <string.h> // Required for: strcpy() //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- #define MAX_DIRECTORY_FILES 2048 #define MAX_ICON_PATH_LENGTH 512 #ifdef _WIN32 #define PATH_SEPERATOR "\\" #else #define PATH_SEPERATOR "/" #endif //---------------------------------------------------------------------------------- // 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; #else // Filename only typedef char *FileInfo; // Files are just a path string #endif //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- FileInfo *dirFilesIcon = NULL; // Path string + icon (for fancy drawing) //---------------------------------------------------------------------------------- // Internal Module Functions Definition //---------------------------------------------------------------------------------- // Read files in new path static void ReloadDirectoryFiles(GuiWindowFileDialogState *state); #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 //---------------------------------------------------------------------------------- GuiWindowFileDialogState InitGuiWindowFileDialog(const char *initPath) { GuiWindowFileDialogState state = { 0 }; // Init window data state.windowBounds = (Rectangle){ GetScreenWidth()/2 - 440/2, GetScreenHeight()/2 - 310/2, 440, 310 }; state.windowActive = false; state.supportDrag = true; state.dragMode = false; state.panOffset = (Vector2){ 0, 0 }; // Init path data 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; strcpy(state.fileNameText, "\0"); // Custom variables initialization if (initPath && DirectoryExists(initPath)) { strcpy(state.dirPathText, initPath); } else if (initPath && FileExists(initPath)) { strcpy(state.dirPathText, GetDirectoryPath(initPath)); strcpy(state.fileNameText, GetFileName(initPath)); } else strcpy(state.dirPathText, GetWorkingDirectory()); // TODO: Why we keep a copy? strcpy(state.dirPathTextCopy, state.dirPathText); strcpy(state.fileNameTextCopy, state.fileNameText); state.filterExt[0] = '\0'; //strcpy(state.filterExt, "all"); state.dirFiles.count = 0; return state; } // Update and draw file dialog void GuiWindowFileDialog(GuiWindowFileDialogState *state) { if (state->windowActive) { // Update window dragging //---------------------------------------------------------------------------------------- if (state->supportDrag) { Vector2 mousePosition = GetMousePosition(); if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { // Window can be dragged from the top window bar if (CheckCollisionPointRec(mousePosition, (Rectangle){ state->windowBounds.x, state->windowBounds.y, (float)state->windowBounds.width, RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT })) { state->dragMode = true; state->panOffset.x = mousePosition.x - state->windowBounds.x; state->panOffset.y = mousePosition.y - state->windowBounds.y; } } if (state->dragMode) { state->windowBounds.x = (mousePosition.x - state->panOffset.x); state->windowBounds.y = (mousePosition.y - state->panOffset.y); // Check screen limits to avoid moving out of screen if (state->windowBounds.x < 0) state->windowBounds.x = 0; else if (state->windowBounds.x > (GetScreenWidth() - state->windowBounds.width)) state->windowBounds.x = GetScreenWidth() - state->windowBounds.width; if (state->windowBounds.y < 0) state->windowBounds.y = 0; else if (state->windowBounds.y > (GetScreenHeight() - state->windowBounds.height)) state->windowBounds.y = GetScreenHeight() - state->windowBounds.height; if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) state->dragMode = false; } } //---------------------------------------------------------------------------------------- // Load dirFilesIcon and state->dirFiles lazily on windows open // NOTE: They are automatically unloaded at fileDialog closing //---------------------------------------------------------------------------------------- if (dirFilesIcon == NULL) { dirFilesIcon = (FileInfo *)RL_CALLOC(MAX_DIRECTORY_FILES, sizeof(FileInfo)); // Max files to read for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesIcon[i] = (char *)RL_CALLOC(MAX_ICON_PATH_LENGTH, 1); // Max file name length } // Load current directory files if (state->dirFiles.paths == NULL) ReloadDirectoryFiles(state); //---------------------------------------------------------------------------------------- // Draw window and controls //---------------------------------------------------------------------------------------- state->windowActive = !GuiWindowBox(state->windowBounds, "#198# Select File Dialog"); // Draw previous directory button + logic if (GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 48, state->windowBounds.y + 24 + 12, 40, 24 }, "< ..")) { // Move dir path one level up strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText)); // Reload directory files (frees previous list) ReloadDirectoryFiles(state); state->filesListActive = -1; memset(state->fileNameText, 0, 1024); memset(state->fileNameTextCopy, 0, 1024); } // Draw current directory text box info + path editing logic if (GuiTextBox((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + 24 + 12, state->windowBounds.width - 48 - 16, 24 }, state->dirPathText, 1024, state->dirPathEditMode)) { if (state->dirPathEditMode) { // Verify if a valid path has been introduced if (DirectoryExists(state->dirPathText)) { // Reload directory files (frees previous list) ReloadDirectoryFiles(state); strcpy(state->dirPathTextCopy, state->dirPathText); } else strcpy(state->dirPathText, state->dirPathTextCopy); } state->dirPathEditMode = !state->dirPathEditMode; } // List view elements are aligned left int prevTextAlignment = GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT); int prevElementsHeight = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 24); # if defined(USE_CUSTOM_LISTVIEW_FILEINFO) 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); # else GuiListViewEx((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + 48 + 20, state->windowBounds.width - 16, state->windowBounds.height - 60 - 16 - 68 }, (const char**)dirFilesIcon, state->dirFiles.count, &state->filesListScrollIndex, &state->filesListActive, &state->itemFocused); # endif GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, prevTextAlignment); GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, prevElementsHeight); // Check if a path has been selected, if it is a directory, move to that directory (and reload paths) if ((state->filesListActive >= 0) && (state->filesListActive != state->prevFilesListActive)) //&& (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_DPAD_A))) { strcpy(state->fileNameText, GetFileName(state->dirFiles.paths[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", (strcmp(state->dirPathText, "/") == 0)? "" : state->dirPathText, state->fileNameText)); strcpy(state->dirPathTextCopy, state->dirPathText); // Reload directory files (frees previous list) ReloadDirectoryFiles(state); strcpy(state->dirPathTextCopy, state->dirPathText); state->filesListActive = -1; strcpy(state->fileNameText, "\0"); strcpy(state->fileNameTextCopy, state->fileNameText); } state->prevFilesListActive = state->filesListActive; } // Draw bottom controls //-------------------------------------------------------------------------------------- GuiLabel((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 68, 60, 24 }, "File name:"); if (GuiTextBox((Rectangle){ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 68, state->windowBounds.width - 184, 24 }, 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 (unsigned int i = 0; i < state->dirFiles.count; i++) { if (TextIsEqual(state->fileNameText, state->dirFiles.paths[i])) { state->filesListActive = i; strcpy(state->fileNameTextCopy, state->fileNameText); break; } } } else if (!state->saveFileMode) { strcpy(state->fileNameText, state->fileNameTextCopy); } } state->fileNameEditMode = !state->fileNameEditMode; } GuiLabel((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 68, 24 }, "File filter:"); GuiComboBox((Rectangle){ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 24 - 12, state->windowBounds.width - 184, 24 }, "All files", &state->fileTypeActive); state->SelectFilePressed = GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 68, 96, 24 }, "Select"); 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; //-------------------------------------------------------------------------------------- // Exit on file selected if (state->SelectFilePressed) state->windowActive = false; // File dialog has been closed, free all memory before exit if (!state->windowActive) { // Free dirFilesIcon memory for (int i = 0; i < MAX_DIRECTORY_FILES; i++) RL_FREE(dirFilesIcon[i]); RL_FREE(dirFilesIcon); dirFilesIcon = NULL; // Unload directory file paths UnloadDirectoryFiles(state->dirFiles); // Reset state variables state->dirFiles.count = 0; state->dirFiles.capacity = 0; state->dirFiles.paths = NULL; } } } // Compare two files from a directory static inline int FileCompare(const char *d1, const char *d2, const char *dir) { const bool b1 = DirectoryExists(TextFormat("%s/%s", dir, d1)); const bool b2 = DirectoryExists(TextFormat("%s/%s", dir, d2)); if (b1 && !b2) return -1; if (!b1 && b2) return 1; if (!FileExists(TextFormat("%s/%s", dir, d1))) return 1; if (!FileExists(TextFormat("%s/%s", dir, d2))) return -1; return strcmp(d1, d2); } // Read files in new path static void ReloadDirectoryFiles(GuiWindowFileDialogState *state) { UnloadDirectoryFiles(state->dirFiles); state->dirFiles = LoadDirectoryFilesEx(state->dirPathText, (state->filterExt[0] == '\0')? NULL : state->filterExt, false); state->itemFocused = 0; // Reset dirFilesIcon memory for (int i = 0; i < MAX_DIRECTORY_FILES; i++) memset(dirFilesIcon[i], 0, MAX_ICON_PATH_LENGTH); // Copy paths as icon + fileNames into dirFilesIcon for (unsigned int i = 0; i < state->dirFiles.count; i++) { if (IsPathFile(state->dirFiles.paths[i])) { // Path is a file, a file icon for convenience (for some recognized extensions) if (IsFileExtension(state->dirFiles.paths[i], ".png;.bmp;.tga;.gif;.jpg;.jpeg;.psd;.hdr;.qoi;.dds;.pkm;.ktx;.pvr;.astc")) { strcpy(dirFilesIcon[i], TextFormat("#12#%s", GetFileName(state->dirFiles.paths[i]))); } else if (IsFileExtension(state->dirFiles.paths[i], ".wav;.mp3;.ogg;.flac;.xm;.mod;.it;.wma;.aiff")) { strcpy(dirFilesIcon[i], TextFormat("#11#%s", GetFileName(state->dirFiles.paths[i]))); } else if (IsFileExtension(state->dirFiles.paths[i], ".txt;.info;.md;.nfo;.xml;.json;.c;.cpp;.cs;.lua;.py;.glsl;.vs;.fs")) { strcpy(dirFilesIcon[i], TextFormat("#10#%s", GetFileName(state->dirFiles.paths[i]))); } else if (IsFileExtension(state->dirFiles.paths[i], ".exe;.bin;.raw;.msi")) { strcpy(dirFilesIcon[i], TextFormat("#200#%s", GetFileName(state->dirFiles.paths[i]))); } else strcpy(dirFilesIcon[i], TextFormat("#218#%s", GetFileName(state->dirFiles.paths[i]))); } else { // Path is a directory, add a directory icon strcpy(dirFilesIcon[i], TextFormat("#1#%s", GetFileName(state->dirFiles.paths[i]))); } } } #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) { int result = 0; GuiState state = guiState; int itemFocused = (focus == NULL)? -1 : *focus; int itemSelected = *active; // Check if we need a scroll bar bool useScrollBar = false; if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING))*count > bounds.height) useScrollBar = true; // Define base item rectangle [0] Rectangle itemBounds = { 0 }; itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING); itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); itemBounds.height = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH); // Get items on the list int visibleItems = bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_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, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_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, LIST_ITEMS_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, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_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_WIDTH); // Save default slider size int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed GuiSetStyle(SCROLLBAR, SLIDER_WIDTH, 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_WIDTH, prevSliderSize); // Reset slider size to default } //-------------------------------------------------------------------- if (focus != NULL) *focus = itemFocused; if (scrollIndex != NULL) *scrollIndex = startIndex; *active = itemSelected; return result; } #endif // USE_CUSTOM_LISTVIEW_FILEINFO #endif // GUI_FILE_DIALOG_IMPLEMENTATION