abbandoning Lua... Error in runtime

This commit is contained in:
noffie 2025-01-10 14:21:27 +01:00
parent cafced2917
commit 54703852e7
20 changed files with 1119 additions and 164 deletions

View file

@ -7,10 +7,9 @@ private:
std::chrono::duration<float> timer{ 0 }; std::chrono::duration<float> timer{ 0 };
std::chrono::duration<float> tickRate; std::chrono::duration<float> tickRate;
std::function<void()> callback;
public: public:
TickSystem(std::chrono::duration<float> tickRateSeconds, std::function<void()> func) TickSystem(std::chrono::duration<float> tickRateSeconds)
: tickRate(tickRateSeconds), callback(func) { : tickRate(tickRateSeconds) {
} }
bool update(std::chrono::duration<float> deltaTime) { bool update(std::chrono::duration<float> deltaTime) {
@ -23,6 +22,4 @@ public:
return false; return false;
} }
void call() const { callback(); }
}; };

View file

@ -122,7 +122,7 @@ struct linked_list
next->first = this; next->first = this;
return *next; return *next;
} }
next->value = val; next->put(val);
if (first) if (first)
next->first = first; next->first = first;
else else

View file

@ -0,0 +1,626 @@
#pragma once
/*******************************************************************************************
*
* 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 <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 <raylibs/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 = { static_cast<float>(GetScreenWidth()) / 2 - 440 / 2, static_cast<float>(GetScreenHeight()) / 2 - 310 / 2, 440, 310 };
state.windowActive = false;
state.supportDrag = true;
state.dragMode = false;
state.panOffset = { 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, { 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({ 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({ 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({ 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({ 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({ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 68, 60, 24 }, "File name:");
if (GuiTextBox({ 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({ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 68, 24 }, "File filter:");
GuiComboBox({ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 24 - 12, state->windowBounds.width - 184, 24 }, "All files", &state->fileTypeActive);
state->SelectFilePressed = GuiButton({ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 68, 96, 24 }, "Select");
if (GuiButton({ 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

View file

@ -0,0 +1,25 @@
local bubble_sort = Future:new()
function bubble_sort:poll(array)
local n = array:size()
self.state[n] = self.state[n] or n
if n == 0 then
return false
end
local swapped = false
for i = 1, n - 1 do
if array.at(i-1) > array.at(i) then
array.swap(i-1, i)
swapped = true
end
end
self.state.n = self.state.n - 1
return not swapped
end
function make_available(sorter_list)
table.insert(sorter_list, "bubble_sort")
end

59
res/lua/quick_sortiva.lua Normal file
View file

@ -0,0 +1,59 @@
local quick_sort = Future:new()
function quick_sort:sort(A, lo, hi, i)
if lo >= 0 and lo < hi then
lt, gt = partition(A, lo, hi) -- Multiple return values
if #self.state["lt"] >= i and #self.state["gt"] >= i then
self:sort(A, lo, self.state["lt"][i] - 1, i+1)
self:sort(A, self.state["gt"][i] + 1, hi, i+1)
else
table.insert(self.state["lt"], lt)
table.insert(self.state["gt"], gt)
return false
end
end
return true
end
function quick_sort:poll(array)
return self:sort(array, 0, array:size(),0)
end
-- Divides array into three partitions
function partition(A, lo, hi)
-- Pivot value
local pivot = A.at((lo + hi) / 2) -- Choose the middle element as the pivot (integer division)
-- Lesser, equal and greater index
local lt = lo
local eq = lo
local gt = hi
-- Iterate and compare all elements with the pivot
while eq <= gt do
if A[eq] < pivot then
-- Swap the elements at the equal and lesser indices
A:swap(eq, lt)
-- Increase lesser index
lt = lt + 1
-- Increase equal index
eq = eq + 1
elseif A[eq] > pivot then
-- Swap the elements at the equal and greater indices
A:swap(eq, gt)
-- Decrease greater index
gt = gt - 1
else -- if A[eq] = pivot then
-- Increase equal index
eq = eq + 1
end
end
-- Return lesser and greater indices
return lt, gt
end
function make_available(sorter_list)
table.insert(sorter_list, "quick_sort")
end

33
src/LuaSortList.hpp Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include <vector>
class LuaSortList
{
public:
std::vector<uint16_t> list;
void swap(size_t i1, size_t i2)
{
std::swap(list[i1], list[i2]);
}
size_t size() const
{
return list.size();
}
uint16_t at(size_t i) const
{
return list[i];
}
bool sorted() const
{
for (int i = 1; i < list.size() + 1; ++i)
{
if (list[i - 1] > list[i]) return false;
}
return true;
}
};

View file

@ -1,2 +1,7 @@
#define RAYGUI_IMPLEMENTATION #define RAYGUI_IMPLEMENTATION
#include <raylibs/raygui.h> #include <raylibs/raygui.h>
#undef RAYGUI_IMPLEMENTATION
#define GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION
#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24
#include <raylibs/modules/gui_window_file_dialog.hpp>

16
src/lua/Future.lua Normal file
View file

@ -0,0 +1,16 @@
Future = {
state = {}
}
function Future:new(state)
local f = {}
setmetatable(f, self)
self.__index = self
f["state"] = state or {}
return f
end
function Future:pull()
return true
end

35
src/lua/FutureLua.hpp Normal file
View file

@ -0,0 +1,35 @@
#pragma once
/*
static inline const char lua_future_class[] = R"(
Future = {
state = {}
}
function Future:new(state)
local f = {}
setmetatable(f, self)
self.__index = self
f["state"] = state or {}
return f
end
function Future:poll()
return true
end
function Future.get(t, ...)
return t:poll(unpack(arg))
end
)";
*/
template<typename T>
class Future
{
public:
virtual ~Future() = default;
virtual bool poll() = 0;
virtual T& get() = 0;
};

View file

@ -1,6 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include "sortiva.hpp" #include "sortiva/sortiva.hpp"
#undef _DEBUG
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
printf("Darling, I'm Home!\n"); printf("Darling, I'm Home!\n");

View file

@ -1,11 +0,0 @@
#include "sortiva.hpp"
void Sortiva::update(double dt) {
for (auto& ticker : m_TickSystems)
{
if (ticker.update(std::chrono::milliseconds(static_cast<long long>(dt))))
{
ticker.call();
}
}
}

View file

@ -1,79 +0,0 @@
#include "sortiva.hpp"
#include <cstdlib>
#include <stdio.h>
#include <cstdint>
#include <memory>
namespace rl
{
#include <raylib.h>
}
Sortiva::Sortiva()
{
rl::InitWindow(1280, 720, "Sortiva");
if (!rl::IsWindowReady()) exit(2);
m_Width = rl::GetRenderWidth();
m_Height = rl::GetRenderHeight();
rl::SetWindowMinSize(static_cast<int>(1280 * 0.25f), static_cast<int>(720 * 0.25f));
rl::SetWindowState(rl::ConfigFlags::FLAG_WINDOW_RESIZABLE | rl::ConfigFlags::FLAG_VSYNC_HINT);
rl::SetTargetFPS(60);
}
Sortiva::~Sortiva()
{
rl::CloseWindow();
}
void Sortiva::run()
{
setup();
while (m_Running)
{
if (rl::WindowShouldClose()) m_Running = false;
if (rl::IsWindowResized())
{
m_Width = rl::GetScreenWidth();
m_Height = rl::GetScreenHeight();
}
rl::ClearBackground({ 25, 25, 25, 255 });
rl::BeginDrawing();
update(rl::GetFrameTime());
draw();
rl::EndDrawing();
}
}
void Sortiva::setup()
{
m_Steps.reset();
m_Steps = std::make_unique<val_step_diag>();
m_Steps->emplace_back();
m_Steps->emplace_back();
m_Steps->emplace_back();
linked_list<uint16_t>& list1 = m_Steps->at(0);
list1.value = 1;
list1.put(1).put(3).put(1).put(1);
//
linked_list<uint16_t>& list2 = m_Steps->at(1);
list2.value = 3;
list2.put(2).put(1).put(3).put(2);
//
linked_list<uint16_t>& list3 = m_Steps->at(2);
list3.value = 2;
list3.put(3).put(2).put(2).put(3);
}

View file

@ -1,33 +0,0 @@
#pragma once
#include <cstdint>
#include <memory>
#include "collections.hpp"
#include "TickSystem.hpp"
class Sortiva final
{
public:
Sortiva();
~Sortiva();
void run();
private:
int m_Width;
int m_Height;
bool m_Running;
void draw();
void update(double deltatime);
void setup();
std::vector<TickSystem> m_TickSystems;
// std::unique_ptr
// -> tree
// -> linked_list
// -> uint16_t
using val_step_diag = std::vector<linked_list<uint16_t>>;
std::unique_ptr<val_step_diag> m_Steps;
};

View file

@ -0,0 +1,45 @@
#include "GuiFileDialog.hpp"
#include <raylibs/raygui.h>
#include <raylibs/modules/gui_window_file_dialog.hpp>
Gui_WindowFileDialog::Gui_WindowFileDialog()
{
fileDialogState = new GuiWindowFileDialogState(InitGuiWindowFileDialog(GetWorkingDirectory()));
fileDialogState->windowBounds = { 10, 10, fileDialogState->windowBounds.height, fileDialogState->windowBounds.height };
}
bool Gui_WindowFileDialog::update()
{
bool file_opened = false;
if (fileDialogState->SelectFilePressed)
{
// Load image file (if supported extension)
if (IsFileExtension(fileDialogState->fileNameText, ".lua"))
{
strcpy(fileNameToLoad, TextFormat("%s/%s", fileDialogState->dirPathText, fileDialogState->fileNameText));
openedFile = fileNameToLoad;
file_opened = true;
}
fileDialogState->SelectFilePressed = false;
}
return file_opened;
}
void Gui_WindowFileDialog::draw()
{
// raygui: controls drawing
//----------------------------------------------------------------------------------
if (fileDialogState->windowActive) GuiLock();
if (GuiButton({ 20, 20, 140, 30 }, GuiIconText(GuiIconName::ICON_FILE_OPEN, "Open Image"))) fileDialogState->windowActive = true;
GuiUnlock();
// GUI: Dialog Window
//--------------------------------------------------------------------------------
GuiWindowFileDialog(fileDialogState);
//--------------------------------------------------------------------------------
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <raylibs/modules/gui_window_file_dialog.hpp>
#include <string>
class Gui_WindowFileDialog
{
GuiWindowFileDialogState* fileDialogState;
char fileNameToLoad[512] = { 0 };
public:
Gui_WindowFileDialog();
std::string openedFile;
bool update();
void draw();
};

View file

@ -1,13 +1,11 @@
#include "sortiva.hpp" #include "sortiva.hpp"
namespace rl
{
#include <raylib.h> #include <raylib.h>
}
#include <cmath> #include <cmath>
constexpr rl::Color sorter_colors[] = { constexpr Color sorter_colors[] = {
{ 253, 249, 0, 255 }, // Yellow { 253, 249, 0, 255 }, // Yellow
{ 255, 203, 0, 255 }, // Gold { 255, 203, 0, 255 }, // Gold
{ 255, 161, 0, 255 }, // Orange { 255, 161, 0, 255 }, // Orange
@ -29,23 +27,23 @@ constexpr rl::Color sorter_colors[] = {
{ 76, 63, 47, 255 }, // Dark Brown { 76, 63, 47, 255 }, // Dark Brown
}; };
constexpr int sorter_colors_size = sizeof(sorter_colors) / sizeof(rl::Color); constexpr int sorter_colors_size = sizeof(sorter_colors) / sizeof(Color);
constexpr rl::Color sorter_block_colors[] = { constexpr Color sorter_block_colors[] = {
{ 200, 200, 200, 255 }, // Light Gray { 200, 200, 200, 255 }, // Light Gray
{ 80, 80, 80, 255 }, // Dark Gray { 80, 80, 80, 255 }, // Dark Gray
}; };
constexpr int sorter_block_colors_size = sizeof(sorter_block_colors) / sizeof(rl::Color); constexpr int sorter_block_colors_size = sizeof(sorter_block_colors) / sizeof(Color);
void Sortiva::draw() void Sortiva::draw()
{ {
rl::Vector2 margin = { Vector2 margin = {
.x = m_Width * 0.01f, .x = m_Width * 0.01f,
.y = m_Height * 0.01f .y = m_Height * 0.01f
}; };
rl::DrawRectangleV(margin, { m_Width - 2 * margin.x, m_Height - 2 * margin.y }, { 60, 60, 60, 255 }); DrawRectangleV(margin, { m_Width - 2 * margin.x, m_Height - 2 * margin.y }, { 60, 60, 60, 255 });
margin = { margin = {
@ -53,6 +51,21 @@ void Sortiva::draw()
.y = m_Height * 0.02f .y = m_Height * 0.02f
}; };
Rectangle plane = {
.x = margin.x,
.y = margin.y,
.width = m_Width - margin.x * 2,
.height = m_Height - margin.y * 2
};
if (m_Steps->empty())
{
static const char* msg = "Select and run a Sorting algorithm";
static int width = MeasureText(msg, 20);
DrawText(msg, plane.x + plane.width / 2 - width / 2, plane.y + plane.height / 2 - 10, 20, { 245, 245, 245, 255 });
return;
}
// +2 for start points and end points; will be removed later in the function // +2 for start points and end points; will be removed later in the function
size_t cvals = m_Steps->size(); size_t cvals = m_Steps->size();
@ -64,13 +77,6 @@ void Sortiva::draw()
steps += 1; steps += 1;
rl::Rectangle plane = {
.x = margin.x,
.y = margin.y,
.width = m_Width - margin.x * 2,
.height = m_Height - margin.y * 2
};
// ui distance values // ui distance values
int w = static_cast<int>(plane.width / static_cast<float>(steps)); int w = static_cast<int>(plane.width / static_cast<float>(steps));
int g = plane.height / static_cast<float>(cvals + 1); int g = plane.height / static_cast<float>(cvals + 1);
@ -85,7 +91,7 @@ void Sortiva::draw()
for (size_t i = 0; i < steps; ++i) for (size_t i = 0; i < steps; ++i)
{ {
rl::DrawRectangle( DrawRectangle(
static_cast<int>(i) * w + static_cast<int>(plane.x), static_cast<int>(i) * w + static_cast<int>(plane.x),
plane.y, plane.y,
w, w,
@ -99,19 +105,18 @@ void Sortiva::draw()
int colid = sorter_colors_size / cvals; int colid = sorter_colors_size / cvals;
// font settings // font settings
constexpr rl::Color textColor = { 0, 0, 0, 255 }; constexpr Color textColor = { 0, 0, 0, 255 };
int l3 = static_cast<int>(l * 3); int l3 = static_cast<int>(l * 3);
#ifdef _DEBUG #ifdef _DEBUG
rl::DrawFPS(5, 5); DrawFPS(5, 5);
#endif #endif
for (int v = 0; v < cvals; ++v) for (int v = 0; v < cvals; ++v)
{ {
linked_list<uint16_t>* list = &m_Steps->at(v); linked_list<uint16_t>* list = &m_Steps->at(v);
uint16_t value = list->value; uint16_t value = list->value;
rl::Color col = sorter_colors[(v * colid) % sorter_colors_size]; Color col = sorter_colors[(v * colid) % sorter_colors_size];
float wf = static_cast<float>(w); float wf = static_cast<float>(w);
@ -127,7 +132,7 @@ void Sortiva::draw()
float yt = plane.y + h + g * (nvalue - 1); float yt = plane.y + h + g * (nvalue - 1);
rl::DrawSplineSegmentBezierCubic( DrawSplineSegmentBezierCubic(
{ (sf + 1.f) * wf + plane.x - 1, y }, { (sf + 1.f) * wf + plane.x - 1, y },
{ (sf + 2.f) * wf + plane.x, y }, { (sf + 2.f) * wf + plane.x, y },
{ (sf + 1.f) * wf + plane.x, yt }, { (sf + 1.f) * wf + plane.x, yt },
@ -140,8 +145,8 @@ void Sortiva::draw()
float font_size = l * 1.2f; float font_size = l * 1.2f;
int yd = y - yt < 0 ? -font_size : +font_size; int yd = y - yt < 0 ? -font_size : +font_size;
yd *= 2; yd *= 2;
rl::DrawText( DrawText(
rl::TextFormat("%d -> %d", value, nvalue), TextFormat("%d -> %d", value, nvalue),
(sf + 1.f) * wf + plane.x + 10, (sf + 1.f) * wf + plane.x + 10,
y + yd, y + yd,
font_size, font_size,
@ -152,18 +157,18 @@ void Sortiva::draw()
value = list->value; value = list->value;
rl::Vector2 pos = { wf * steps + e + plane.x, h + (value - 1) * g + plane.y }; Vector2 pos = { wf * steps + e + plane.x, h + (value - 1) * g + plane.y };
rl::DrawCircleV(pos, l3, col); DrawCircleV(pos, l3, col);
rl::DrawLineEx( DrawLineEx(
{ pos.x - e - 1, pos.y }, { pos.x - e - 1, pos.y },
pos, pos,
l, l,
col col
); );
const char* strv = rl::TextFormat("%d", v + 1); const char* strv = TextFormat("%d", v + 1);
rl::DrawText( DrawText(
strv, strv,
static_cast<int>(pos.x) + e / 2, static_cast<int>(pos.x) + e / 2,
static_cast<int>(pos.y) - l3 / 2, static_cast<int>(pos.y) - l3 / 2,
@ -171,11 +176,12 @@ void Sortiva::draw()
textColor textColor
); );
list = list->first; if (list->first)
list = list->first->next;
value = list->value; value = list->value;
pos = { wf - e + plane.x, h + (value - 1) * g + plane.y }; pos = { wf - e + plane.x, h + (value - 1) * g + plane.y };
rl::DrawText( DrawText(
strv, strv,
static_cast<int>(pos.x) - e / 2 - l3 / 2, static_cast<int>(pos.x) - e / 2 - l3 / 2,
static_cast<int>(pos.y) - l3 / 2, static_cast<int>(pos.y) - l3 / 2,
@ -183,7 +189,7 @@ void Sortiva::draw()
textColor textColor
); );
rl::DrawRing( DrawRing(
pos, pos,
l * 2, l * 2,
l3, l3,
@ -193,11 +199,12 @@ void Sortiva::draw()
col col
); );
rl::DrawLineEx( DrawLineEx(
pos, pos,
{ pos.x + e, pos.y }, { pos.x + e, pos.y },
l, l,
col col
); );
} }
} }

View file

@ -0,0 +1,28 @@
#include "sortiva.hpp"
#include <raylibs/raygui.h>
void Sortiva::draw_overlay()
{
//static bool edit_mode = false;
//
//GuiUnlock();
//
//static int active = 0;
//if (GuiDropdownBox({ 250,20,200,30 }, m_Lua.SorterListStr.c_str(), &active, edit_mode)) edit_mode = !edit_mode;
//
//if (edit_mode) GuiLock();
if (GuiButton({ 450, 20, 100, 40 }, "Run"))
{
setup();
m_SortingFinished = false;
}
if (GuiButton({ 200, 20, 100, 40 }, "Pause"))
{
if (m_SortingFinished && m_Steps->empty()) return;
m_SortingFinished = !m_SortingFinished;
}
}

View file

@ -0,0 +1,22 @@
#include "sortiva.hpp"
void Sortiva::update(double dt) {
if (m_Ticker.update(std::chrono::duration<float>(dt)))
{
if (!m_SortingFinished) {
if (m_Sorter.poll())
{
m_SortingFinished = true;
}
else
{
LuaSortList& list = m_Sorter.get();
for (uint16_t i = 0; i < list.size(); ++i)
{
m_Steps->at(list.at(i) - 1).put(i + 1);
}
}
}
}
}

78
src/sortiva/sortiva.cpp Normal file
View file

@ -0,0 +1,78 @@
#include "sortiva.hpp"
#include <cstdlib>
#include <memory>
#include <random>
#include <raylib.h>
Sortiva::Sortiva() : m_Ticker(std::chrono::seconds(1))
{
m_Sorter.set(m_List);
m_Steps = std::make_unique<val_step_diag>();
InitWindow(1280, 720, "Sortiva");
if (!IsWindowReady()) exit(2);
m_Width = GetRenderWidth();
m_Height = GetRenderHeight();
SetWindowMinSize(static_cast<int>(1280 * 0.25f), static_cast<int>(720 * 0.25f));
SetWindowState(ConfigFlags::FLAG_WINDOW_RESIZABLE | ConfigFlags::FLAG_VSYNC_HINT);
SetTargetFPS(60);
}
Sortiva::~Sortiva()
{
CloseWindow();
}
void Sortiva::run()
{
while (m_Running)
{
if (WindowShouldClose()) m_Running = false;
if (IsWindowResized())
{
m_Width = GetScreenWidth();
m_Height = GetScreenHeight();
}
ClearBackground({ 25, 25, 25, 255 });
BeginDrawing();
update(GetFrameTime());
draw();
draw_overlay();
EndDrawing();
}
}
void Sortiva::setup()
{
m_Steps->clear();
m_List.list.clear();
uint16_t count = 5;
for (int i = 1; i <= count; ++i) // 1,2,3,4,5
{
m_List.list.push_back(i);
}
std::random_device dev;
std::mt19937 rng(dev());
std::ranges::shuffle(m_List.list, rng); // 2,3,1,5,4
for (int i = 1; i <= count; ++i)
{
m_Steps->emplace_back().value = m_List.list[i - 1];
}
}

82
src/sortiva/sortiva.hpp Normal file
View file

@ -0,0 +1,82 @@
#pragma once
#include <cstdint>
#include <memory>
#include <collections.hpp>
#include "../lua/FutureLua.hpp"
#include <TickSystem.hpp>
#include "../LuaSortList.hpp"
class Bubble_Sorter : Future<LuaSortList>
{
LuaSortList* list = nullptr;
struct STATE
{
size_t n = 0;
} state;
public:
Bubble_Sorter() = default;
Bubble_Sorter(LuaSortList& l) : list(&l) {}
bool poll() override
{
int n = list->size();
if (n <= 1) {
return true;
}
state.n = state.n + 1;
if (state.n >= n)
{
state.n = 1;
}
if (list->at(state.n - 1) > list->at(state.n)) {
list->swap(state.n - 1, state.n);
return false;
}
return list->sorted();
}
LuaSortList& get() override
{
return *list;
}
void set(LuaSortList& l)
{
list = &l;
}
};
class Sortiva final
{
public:
Sortiva();
~Sortiva();
void run();
private:
int m_Width;
int m_Height;
bool m_Running = true;
void draw();
void draw_overlay();
void update(double);
void setup();
bool m_SortingFinished = true;
TickSystem m_Ticker;
Bubble_Sorter m_Sorter;
LuaSortList m_List;
// std::unique_ptr
// -> tree
// -> linked_list
// -> uint16_t
using val_step_diag = std::vector<linked_list<uint16_t>>;
std::unique_ptr<val_step_diag> m_Steps;
};