add file dialog

This commit is contained in:
Fabien Freling 2023-10-13 18:16:41 +02:00
parent 7f0c088b3d
commit a503e67eaf
6 changed files with 775 additions and 8 deletions

738
3rd-party/gui_window_file_dialog.h vendored Normal file
View file

@ -0,0 +1,738 @@
/*******************************************************************************************
*
* 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-2023 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 "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 "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 (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 (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

6
3rd-party/raygui.c vendored Normal file
View file

@ -0,0 +1,6 @@
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
#undef RAYGUI_IMPLEMENTATION
#define GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION
#include "gui_window_file_dialog.h"

View file

@ -21,6 +21,14 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
});
exe.linkLibrary(raylib_dep.artifact("raylib"));
const cflags = [_][]const u8{};
exe.addCSourceFile(.{
.file = .{
.path = "3rd-party/raygui.c",
},
.flags = &cflags,
});
exe.addIncludePath(.{ .path = "3rd-party/" });
exe.linkSystemLibrary("sqlite3");

View file

@ -21,4 +21,6 @@ format:
fd -e nix -X nix fmt {}
update-deps:
curl https://raw.githubusercontent.com/raysan5/raygui/master/src/raygui.h --output 3rd-party/raygui.h
# We cannot easily add header-only C library with package manager
curl https://raw.githubusercontent.com/raysan5/raygui/master/src/raygui.h --remote-name --output-dir 3rd-party/
curl https://raw.githubusercontent.com/raysan5/raygui/master/examples/custom_file_dialog/gui_window_file_dialog.h --remote-name --output-dir 3rd-party/

View file

@ -7,15 +7,28 @@ const sqlite = @cImport({
pub fn main() !void {
const screen_width = 800;
const screen_height = 450;
raylib.InitWindow(screen_width, screen_height, "raylib [core] example - basic window");
raylib.InitWindow(screen_width, screen_height, "FabApp");
raylib.SetTargetFPS(60);
var file_dialog_state = raylib.InitGuiWindowFileDialog(raylib.GetWorkingDirectory());
while (!raylib.WindowShouldClose()) {
raylib.BeginDrawing();
defer raylib.EndDrawing();
raylib.ClearBackground(raylib.RAYWHITE);
raylib.DrawText("Congrats! You created your first window!", 190, 200, 20, raylib.LIGHTGRAY);
if (raylib.GuiButton(.{ .x = 0, .y = 0, .width = 100, .height = 100 }, "MyButton") == 1) {}
{
if (file_dialog_state.windowActive) raylib.GuiLock();
defer raylib.GuiUnlock();
const button_size = 200;
if (raylib.GuiButton(.{ .x = (screen_width - button_size) / 2, .y = (screen_height - button_size) / 2, .width = 200, .height = 200 }, "Load db file") == 1) {
file_dialog_state.windowActive = true;
}
}
raylib.GuiWindowFileDialog(&file_dialog_state);
// // if (GuiButton((Rectangle){ 20, 20, 140, 30 }, GuiIconText(ICON_FILE_OPEN, "Open Image"))) fileDialogState.windowActive = true;
}
raylib.CloseWindow();
}

View file

@ -1,5 +1,5 @@
pub usingnamespace @cImport({
@cInclude("raylib.h");
@cDefine("RAYGUI_IMPLEMENTATION", "1");
@cInclude("raygui.h");
@cInclude("gui_window_file_dialog.h");
});