feat(gui): add toolbar widget

feat(gui): add grid_select, toggle, panel, status_bar, image widgets
fix(bsp): fill in exterior cells
This commit is contained in:
asrael 2026-02-27 06:50:49 -06:00
parent 5a565844dd
commit 8d491612ab
63 changed files with 3150 additions and 1686 deletions

View file

@ -4,16 +4,13 @@
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include "pxl8_cart.h"
#include "pxl8_embed.h"
#include "pxl8_io.h"
#include "pxl8_gui.h"
#include "pxl8_log.h"
#include "pxl8_macros.h"
@ -28,7 +25,7 @@ struct pxl8_script {
char last_error[PXL8_MAX_ERROR_SIZE];
char main_path[PXL8_MAX_PATH];
char watch_dir[PXL8_MAX_PATH];
time_t latest_mod_time;
f64 latest_mod_time;
int repl_env_ref;
bool repl_mode;
};
@ -407,8 +404,12 @@ static pxl8_result pxl8_script_prepare_path(pxl8_script* script, const char* fil
char script_dir[PATH_MAX];
char original_cwd[PATH_MAX];
if (realpath(filename_copy, script_dir) && getcwd(original_cwd, sizeof(original_cwd))) {
chdir(script_dir);
char* resolved = pxl8_io_get_real_path(filename_copy);
char* cwd = pxl8_io_get_cwd();
if (resolved && cwd) {
pxl8_strncpy(script_dir, resolved, sizeof(script_dir));
pxl8_strncpy(original_cwd, cwd, sizeof(original_cwd));
pxl8_io_set_cwd(script_dir);
pxl8_script_set_cart_path(script, script_dir, original_cwd);
strncpy(out_basename, last_slash + 1, basename_size - 1);
out_basename[basename_size - 1] = '\0';
@ -416,6 +417,8 @@ static pxl8_result pxl8_script_prepare_path(pxl8_script* script, const char* fil
strncpy(out_basename, filename, basename_size - 1);
out_basename[basename_size - 1] = '\0';
}
pxl8_free(resolved);
pxl8_free(cwd);
} else {
strncpy(out_basename, filename, basename_size - 1);
out_basename[basename_size - 1] = '\0';
@ -442,51 +445,38 @@ pxl8_result pxl8_script_run_file(pxl8_script* script, const char* filename) {
return result;
}
static time_t get_file_mod_time(const char* path) {
struct stat file_stat;
if (stat(path, &file_stat) == 0) {
return file_stat.st_mtime;
}
return 0;
}
typedef struct {
f64 latest;
} script_mod_ctx;
static time_t get_latest_script_mod_time(const char* dir_path) {
DIR* dir = opendir(dir_path);
if (!dir) return 0;
static f64 get_latest_script_mod_time(const char* dir_path);
time_t latest = 0;
struct dirent* entry;
static bool check_script_mod_time(void* userdata, const char* dirname, const char* name) {
script_mod_ctx* ctx = userdata;
if (name[0] == '.') return true;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s%s", dirname, name);
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
struct stat st;
if (stat(full_path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
time_t subdir_time = get_latest_script_mod_time(full_path);
if (subdir_time > latest) {
latest = subdir_time;
}
} else {
usize len = strlen(entry->d_name);
bool is_script = (len > 4 && strcmp(entry->d_name + len - 4, ".fnl") == 0) ||
(len > 4 && strcmp(entry->d_name + len - 4, ".lua") == 0);
if (is_script) {
time_t mod_time = get_file_mod_time(full_path);
if (mod_time > latest) {
latest = mod_time;
}
}
}
if (pxl8_io_is_directory(full_path)) {
f64 subdir_time = get_latest_script_mod_time(full_path);
if (subdir_time > ctx->latest) ctx->latest = subdir_time;
} else {
usize len = strlen(name);
bool is_script = (len > 4 && strcmp(name + len - 4, ".fnl") == 0) ||
(len > 4 && strcmp(name + len - 4, ".lua") == 0);
if (is_script) {
f64 mod_time = pxl8_io_get_file_modified_time(full_path);
if (mod_time > ctx->latest) ctx->latest = mod_time;
}
}
return true;
}
closedir(dir);
return latest;
static f64 get_latest_script_mod_time(const char* dir_path) {
script_mod_ctx ctx = { .latest = 0.0 };
pxl8_io_enumerate_directory(dir_path, check_script_mod_time, &ctx);
return ctx.latest;
}
void pxl8_script_set_cart_path(pxl8_script* script, const char* cart_path, const char* original_cwd) {
@ -1157,8 +1147,8 @@ bool pxl8_script_check_reload(pxl8_script* script) {
return false;
}
time_t current_mod_time = get_latest_script_mod_time(script->watch_dir);
if (current_mod_time > script->latest_mod_time && current_mod_time != 0) {
f64 current_mod_time = get_latest_script_mod_time(script->watch_dir);
if (current_mod_time > script->latest_mod_time && current_mod_time != 0.0) {
pxl8_info("Script files modified, reloading: %s", script->main_path);
script->latest_mod_time = current_mod_time;
@ -1214,12 +1204,6 @@ static pxl8_resolution parse_resolution(const char* str) {
return PXL8_RESOLUTION_640x360;
}
static pxl8_pixel_mode parse_pixel_mode(const char* str) {
if (strcmp(str, "indexed") == 0) return PXL8_PIXEL_INDEXED;
if (strcmp(str, "hicolor") == 0) return PXL8_PIXEL_HICOLOR;
return PXL8_PIXEL_INDEXED;
}
pxl8_result pxl8_script_load_cart_manifest(pxl8_script* script, pxl8_cart* cart) {
if (!script || !script->L || !cart) return PXL8_ERROR_NULL_POINTER;
@ -1267,12 +1251,6 @@ pxl8_result pxl8_script_load_cart_manifest(pxl8_script* script, pxl8_cart* cart)
}
lua_pop(script->L, 1);
lua_getfield(script->L, -1, "pixel-mode");
if (lua_isstring(script->L, -1)) {
pxl8_cart_set_pixel_mode(cart, parse_pixel_mode(lua_tostring(script->L, -1)));
}
lua_pop(script->L, 1);
lua_getfield(script->L, -1, "window-size");
if (lua_istable(script->L, -1)) {
lua_rawgeti(script->L, -1, 1);