texture editor wip
parent
c479667bcc
commit
a7717b88d7
|
@ -45,9 +45,9 @@ void DrawCircleEco(float centerX, float centerY, float radius, Color color)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
void DrawRectangleEco(float posX, float posY, int width, int height, Color color)
|
void DrawRectangleEco(float posX, float posY, float width, float height, Color color)
|
||||||
{
|
{
|
||||||
DrawRectangleV((Vector2){ (float)posX , (float)posY }, (Vector2){ (float)width , (float)height }, color);
|
DrawRectangleV((Vector2){ (float)posX , (float)posY }, (Vector2){ width , height }, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#define WORLD_TRACKER_UPDATE_FAST_MS 100
|
#define WORLD_TRACKER_UPDATE_FAST_MS 100
|
||||||
#define WORLD_TRACKER_UPDATE_NORMAL_MS 500
|
#define WORLD_TRACKER_UPDATE_NORMAL_MS 500
|
||||||
#define WORLD_TRACKER_UPDATE_SLOW_MS 1000
|
#define WORLD_TRACKER_UPDATE_SLOW_MS 1000
|
||||||
#define WORLD_BLOCK_SIZE 16
|
#define WORLD_BLOCK_SIZE 64
|
||||||
|
|
||||||
#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata)
|
#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata)
|
||||||
typedef WORLD_PKT_READER(world_pkt_reader_proc);
|
typedef WORLD_PKT_READER(world_pkt_reader_proc);
|
||||||
|
|
|
@ -1,104 +1,440 @@
|
||||||
|
#define ZPL_NO_WINDOWS_H
|
||||||
|
#include "zpl.h"
|
||||||
|
|
||||||
#include "editors/texed.h"
|
#include "editors/texed.h"
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
#include "utils/raylib_helpers.h"
|
||||||
|
#include "cwpack/cwpack.h"
|
||||||
|
|
||||||
#define RAYGUI_IMPLEMENTATION
|
#define RAYGUI_IMPLEMENTATION
|
||||||
#define RAYGUI_SUPPORT_ICONS
|
#define RAYGUI_SUPPORT_ICONS
|
||||||
#include "raygui.h"
|
#include "raygui.h"
|
||||||
|
|
||||||
|
#define GUI_FILE_DIALOG_IMPLEMENTATION
|
||||||
|
#include "gui_file_dialog.h"
|
||||||
|
|
||||||
static uint16_t screenWidth = 1280;
|
static uint16_t screenWidth = 1280;
|
||||||
static uint16_t screenHeight = 720;
|
static uint16_t screenHeight = 720;
|
||||||
static void DrawStyleEditControls(void);
|
static float zoom = 4.0f;
|
||||||
|
|
||||||
|
#define TD_DEFAULT_IMG_WIDTH 64
|
||||||
|
#define TD_DEFAULT_IMG_HEIGHT 64
|
||||||
|
#define TD_UI_PADDING 5.0f
|
||||||
|
#define TD_UI_PREVIEW_BORDER 4.0f
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TPARAM_FLOAT,
|
||||||
|
TPARAM_INT,
|
||||||
|
TPARAM_COLOR,
|
||||||
|
TPARAM_STRING,
|
||||||
|
|
||||||
|
TPARAM_FORCE_UINT8 = UINT8_MAX
|
||||||
|
} td_param_kind;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
td_param_kind kind;
|
||||||
|
union {
|
||||||
|
float number;
|
||||||
|
int pix;
|
||||||
|
char *str;
|
||||||
|
Color color;
|
||||||
|
};
|
||||||
|
} td_param;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TOP_CLEAR,
|
||||||
|
TOP_DRAW_RECT,
|
||||||
|
TOP_DRAW_LINE,
|
||||||
|
|
||||||
|
TOP_FORCE_UINT8 = UINT8_MAX
|
||||||
|
} td_op_kind;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
td_op_kind kind;
|
||||||
|
char const *name;
|
||||||
|
bool is_hidden;
|
||||||
|
|
||||||
|
uint8_t num_params;
|
||||||
|
td_param *params;
|
||||||
|
} td_op;
|
||||||
|
|
||||||
|
#define OP(n) .kind = n, .name = #n
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *filepath;
|
||||||
|
Image img;
|
||||||
|
Texture2D tex;
|
||||||
|
GuiFileDialogState fileDialog;
|
||||||
|
|
||||||
|
td_op *ops; //< zpl_array
|
||||||
|
} td_ctx;
|
||||||
|
|
||||||
|
static td_ctx ctx = {0};
|
||||||
|
|
||||||
|
static char filename[200];
|
||||||
|
|
||||||
|
static td_op default_ops[] = {
|
||||||
|
{
|
||||||
|
OP(TOP_CLEAR),
|
||||||
|
.num_params = 1,
|
||||||
|
.params = (td_param[]) {
|
||||||
|
{
|
||||||
|
.kind = TPARAM_COLOR,
|
||||||
|
.color = WHITE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OP(TOP_DRAW_RECT),
|
||||||
|
.num_params = 5,
|
||||||
|
.params = (td_param[]) {
|
||||||
|
{
|
||||||
|
.kind = TPARAM_INT,
|
||||||
|
.pix = 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.kind = TPARAM_INT,
|
||||||
|
.pix = 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.kind = TPARAM_INT,
|
||||||
|
.pix = 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.kind = TPARAM_INT,
|
||||||
|
.pix = 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.kind = TPARAM_COLOR,
|
||||||
|
.color = RED
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE(zaklaus): IMPORTANT !! keep these in sync
|
||||||
|
static char const *add_op_list = "CLEAR SOLID;DRAW RECTANGLE;PLOT LINE;DITHER";
|
||||||
|
|
||||||
|
#define DEF_OPS_LEN (int)(sizeof(default_ops) / (sizeof(default_ops[0])))
|
||||||
|
|
||||||
|
void texed_new(int32_t w, int32_t h);
|
||||||
|
void texed_destroy(void);
|
||||||
|
void texed_load(void);
|
||||||
|
void texed_save(void);
|
||||||
|
void texed_repaint_preview(void);
|
||||||
|
void texed_process_ops(void);
|
||||||
|
void texed_add_op(int idx);
|
||||||
|
void texed_rem_op(int idx);
|
||||||
|
void texed_swp_op(int idx, int idx2);
|
||||||
|
|
||||||
|
void texed_draw_oplist_pane(zpl_aabb2 r);
|
||||||
|
void texed_draw_topbar(zpl_aabb2 r);
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void DrawAABB(zpl_aabb2 rect, Color color) {
|
||||||
|
DrawRectangleEco(rect.min.x, rect.min.y,
|
||||||
|
rect.max.x-rect.min.x,
|
||||||
|
rect.max.y-rect.min.y,
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Rectangle aabb2_ray(zpl_aabb2 r);
|
||||||
|
|
||||||
void texed_run(void) {
|
void texed_run(void) {
|
||||||
InitWindow(screenWidth, screenHeight, "eco2d - texture editor");
|
InitWindow(screenWidth, screenHeight, "eco2d - texture editor");
|
||||||
SetWindowState(FLAG_WINDOW_RESIZABLE);
|
|
||||||
SetTargetFPS(60);
|
SetTargetFPS(60);
|
||||||
|
|
||||||
|
texed_new(TD_DEFAULT_IMG_WIDTH, TD_DEFAULT_IMG_HEIGHT);
|
||||||
|
|
||||||
Rectangle panelRec = { 20, 40, 200, 150 };
|
zpl_aabb2 screen = {
|
||||||
Rectangle panelContentRec = {0, 0, 340, 340 };
|
.min = (zpl_vec2) {.x = 0.0f, .y = 0.0f},
|
||||||
Vector2 panelScroll = { 99, -20 };
|
.max = (zpl_vec2) {.x = screenWidth, .y = screenHeight},
|
||||||
|
};
|
||||||
|
|
||||||
bool showContentArea = true;
|
// TODO(zaklaus): TEMP
|
||||||
|
texed_add_op(0);
|
||||||
|
texed_add_op(1);
|
||||||
|
|
||||||
|
zpl_aabb2 topbar = zpl_aabb2_cut_top(&screen, 25.0f);
|
||||||
|
zpl_aabb2 oplist_pane = zpl_aabb2_cut_right(&screen, screenWidth / 2.0f);
|
||||||
|
zpl_aabb2 preview_window = zpl_aabb2_cut_top(&screen, (screen.max.y-screen.min.y) / 2.0f);
|
||||||
|
zpl_aabb2 property_pane = screen;
|
||||||
|
|
||||||
|
// NOTE(zaklaus): contract all panes for a clean UI separation
|
||||||
|
oplist_pane = zpl_aabb2_contract(&oplist_pane, TD_UI_PADDING);
|
||||||
|
preview_window = zpl_aabb2_contract(&preview_window, TD_UI_PADDING);
|
||||||
|
property_pane = zpl_aabb2_contract(&property_pane, TD_UI_PADDING);
|
||||||
|
|
||||||
|
Rectangle preview_rect = aabb2_ray(preview_window);
|
||||||
|
Image checkerboard = GenImageChecked(preview_rect.width, preview_rect.height, 16, 16, BLACK, ColorAlpha(GRAY, 0.2f));
|
||||||
|
Texture2D checker_tex = LoadTextureFromImage(checkerboard);
|
||||||
|
UnloadImage(checkerboard);
|
||||||
|
|
||||||
while (!WindowShouldClose()) {
|
while (!WindowShouldClose()) {
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
ClearBackground(RAYWHITE);
|
ClearBackground(GetColor(0x222034));
|
||||||
{
|
{
|
||||||
DrawText(TextFormat("[%f, %f]", panelScroll.x, panelScroll.y), 4, 4, 20, RED);
|
if (ctx.fileDialog.fileDialogActive) GuiLock();
|
||||||
|
DrawTextureEx(checker_tex, (Vector2){ preview_window.min.x, preview_window.min.y}, 0.0f, 1.0f, WHITE);
|
||||||
|
DrawTextureEx(ctx.tex, (Vector2){ preview_window.min.x, preview_window.min.y}, 0.0f, zoom, WHITE);
|
||||||
|
|
||||||
Rectangle view = GuiScrollPanel(panelRec, panelContentRec, &panelScroll);
|
DrawAABB(topbar, RAYWHITE);
|
||||||
|
DrawAABB(property_pane, GetColor(0x422060));
|
||||||
|
DrawAABB(oplist_pane, GetColor(0x425060));
|
||||||
|
|
||||||
BeginScissorMode(view.x, view.y, view.width, view.height);
|
texed_draw_topbar(topbar);
|
||||||
GuiGrid((Rectangle){panelRec.x + panelScroll.x, panelRec.y + panelScroll.y, panelContentRec.width, panelContentRec.height}, 16, 3);
|
texed_draw_oplist_pane(oplist_pane);
|
||||||
EndScissorMode();
|
|
||||||
|
|
||||||
if (showContentArea) DrawRectangle(panelRec.x + panelScroll.x, panelRec.y + panelScroll.y, panelContentRec.width, panelContentRec.height, Fade(RED, 0.1));
|
|
||||||
|
|
||||||
DrawStyleEditControls();
|
|
||||||
|
|
||||||
showContentArea = GuiCheckBox((Rectangle){ 565, 80, 20, 20 }, "SHOW CONTENT AREA", showContentArea);
|
|
||||||
|
|
||||||
panelContentRec.width = GuiSliderBar((Rectangle){ 590, 385, 145, 15}, "WIDTH", TextFormat("%i", (int)panelContentRec.width), panelContentRec.width, 1, 600);
|
|
||||||
panelContentRec.height = GuiSliderBar((Rectangle){ 590, 410, 145, 15 }, "HEIGHT", TextFormat("%i", (int)panelContentRec.height), panelContentRec.height, 1, 400);
|
|
||||||
|
|
||||||
|
if (ctx.fileDialog.fileDialogActive) GuiUnlock();
|
||||||
|
GuiFileDialog(&ctx.fileDialog);
|
||||||
}
|
}
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnloadTexture(checker_tex);
|
||||||
|
texed_destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw and process scroll bar style edition controls
|
void texed_new(int32_t w, int32_t h) {
|
||||||
static void DrawStyleEditControls(void)
|
ctx.img = GenImageColor(w, h, WHITE);
|
||||||
{
|
ctx.filepath = NULL;
|
||||||
// ScrollPanel style controls
|
zpl_array_init(ctx.ops, zpl_heap());
|
||||||
//----------------------------------------------------------
|
texed_repaint_preview();
|
||||||
GuiGroupBox((Rectangle){ 550, 170, 220, 205 }, "SCROLLBAR STYLE");
|
|
||||||
|
|
||||||
int style = GuiGetStyle(SCROLLBAR, BORDER_WIDTH);
|
ctx.fileDialog = InitGuiFileDialog(420, 310, zpl_bprintf("%s/art", GetWorkingDirectory()), false);
|
||||||
GuiLabel((Rectangle){ 555, 195, 110, 10 }, "BORDER_WIDTH");
|
//zpl_strcpy(ctx.fileDialog.filterExt, ".ecotex");
|
||||||
GuiSpinner((Rectangle){ 670, 190, 90, 20 }, NULL, &style, 0, 6, false);
|
}
|
||||||
GuiSetStyle(SCROLLBAR, BORDER_WIDTH, style);
|
|
||||||
|
void texed_destroy(void) {
|
||||||
style = GuiGetStyle(SCROLLBAR, ARROWS_SIZE);
|
UnloadTexture(ctx.tex);
|
||||||
GuiLabel((Rectangle){ 555, 220, 110, 10 }, "ARROWS_SIZE");
|
UnloadImage(ctx.img);
|
||||||
GuiSpinner((Rectangle){ 670, 215, 90, 20 }, NULL, &style, 4, 14, false);
|
zpl_array_free(ctx.ops);
|
||||||
GuiSetStyle(SCROLLBAR, ARROWS_SIZE, style);
|
}
|
||||||
|
|
||||||
style = GuiGetStyle(SCROLLBAR, SLIDER_PADDING);
|
void texed_repaint_preview(void) {
|
||||||
GuiLabel((Rectangle){ 555, 245, 110, 10 }, "SLIDER_PADDING");
|
UnloadTexture(ctx.tex);
|
||||||
GuiSpinner((Rectangle){ 670, 240, 90, 20 }, NULL, &style, 0, 14, false);
|
ImageClearBackground(&ctx.img, ColorAlpha(BLACK, 0.0f));
|
||||||
GuiSetStyle(SCROLLBAR, SLIDER_PADDING, style);
|
texed_process_ops();
|
||||||
|
ctx.tex = LoadTextureFromImage(ctx.img);
|
||||||
style = GuiCheckBox((Rectangle){ 565, 280, 20, 20 }, "ARROWS_VISIBLE", GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE));
|
}
|
||||||
GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, style);
|
|
||||||
|
void texed_add_op(int idx) {
|
||||||
style = GuiGetStyle(SCROLLBAR, SLIDER_PADDING);
|
assert(idx >= 0 && idx < DEF_OPS_LEN);
|
||||||
GuiLabel((Rectangle){ 555, 325, 110, 10 }, "SLIDER_PADDING");
|
td_op *dop = &default_ops[idx];
|
||||||
GuiSpinner((Rectangle){ 670, 320, 90, 20 }, NULL, &style, 0, 14, false);
|
|
||||||
GuiSetStyle(SCROLLBAR, SLIDER_PADDING, style);
|
td_op op = {
|
||||||
|
.kind = dop->kind,
|
||||||
style = GuiGetStyle(SCROLLBAR, SLIDER_WIDTH);
|
.name = dop->name,
|
||||||
GuiLabel((Rectangle){ 555, 350, 110, 10 }, "SLIDER_WIDTH");
|
.num_params = dop->num_params,
|
||||||
GuiSpinner((Rectangle){ 670, 345, 90, 20 }, NULL, &style, 2, 100, false);
|
.params = (td_param*)zpl_malloc(sizeof(td_param)*dop->num_params)
|
||||||
GuiSetStyle(SCROLLBAR, SLIDER_WIDTH, style);
|
};
|
||||||
|
|
||||||
const char *text = GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE? "SCROLLBAR: LEFT" : "SCROLLBAR: RIGHT";
|
zpl_memcopy(op.params, dop->params, sizeof(td_param)*dop->num_params);
|
||||||
style = GuiToggle((Rectangle){ 560, 110, 200, 35 }, text, GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE));
|
|
||||||
GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, style);
|
zpl_array_append(ctx.ops, op);
|
||||||
//----------------------------------------------------------
|
|
||||||
|
texed_repaint_preview();
|
||||||
// ScrollBar style controls
|
}
|
||||||
//----------------------------------------------------------
|
|
||||||
GuiGroupBox((Rectangle){ 550, 20, 220, 135 }, "SCROLLPANEL STYLE");
|
void texed_rem_op(int idx) {
|
||||||
|
assert(idx >= 0 && idx < (int)zpl_array_count(ctx.ops));
|
||||||
style = GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
|
zpl_mfree(ctx.ops[idx].params);
|
||||||
GuiLabel((Rectangle){ 555, 35, 110, 10 }, "SCROLLBAR_WIDTH");
|
zpl_array_remove_at(ctx.ops, idx);
|
||||||
GuiSpinner((Rectangle){ 670, 30, 90, 20 }, NULL, &style, 6, 30, false);
|
texed_repaint_preview();
|
||||||
GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, style);
|
}
|
||||||
|
|
||||||
style = GuiGetStyle(DEFAULT, BORDER_WIDTH);
|
static bool is_add_op_dropbox_open = false;
|
||||||
GuiLabel((Rectangle){ 555, 60, 110, 10 }, "BORDER_WIDTH");
|
static int add_op_dropbox_selected = 0;
|
||||||
GuiSpinner((Rectangle){ 670, 55, 90, 20 }, NULL, &style, 0, 20, false);
|
|
||||||
GuiSetStyle(DEFAULT, BORDER_WIDTH, style);
|
void texed_draw_oplist_pane(zpl_aabb2 r) {
|
||||||
//----------------------------------------------------------
|
zpl_aabb2 oplist_header = zpl_aabb2_cut_top(&r, 40.0f);
|
||||||
|
|
||||||
|
zpl_aabb2 add_op_r = zpl_aabb2_cut_left(&oplist_header, 120.0f);
|
||||||
|
|
||||||
|
if (!is_add_op_dropbox_open && GuiButton(aabb2_ray(add_op_r), "ADD OPERATION")) {
|
||||||
|
is_add_op_dropbox_open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GuiSetState(ctx.filepath ? GUI_STATE_NORMAL : GUI_STATE_DISABLED);
|
||||||
|
|
||||||
|
zpl_aabb2 export_code_r = zpl_aabb2_cut_left(&oplist_header, 120.0f);
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(export_code_r), "BUILD TEXTURE")) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
zpl_aabb2 export_img_r = zpl_aabb2_cut_left(&oplist_header, 120.0f);
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(export_img_r), "EXPORT AS IMAGE")) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiSetState(GUI_STATE_NORMAL);
|
||||||
|
|
||||||
|
// NOTE(zaklaus): operator list
|
||||||
|
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
||||||
|
zpl_aabb2 op_item_r = zpl_aabb2_cut_top(&r, 45.0f);
|
||||||
|
zpl_aabb2_cut_top(&op_item_r, 2.5f);
|
||||||
|
zpl_aabb2_cut_bottom(&op_item_r, 2.5f);
|
||||||
|
Rectangle list_item = aabb2_ray(op_item_r);
|
||||||
|
DrawRectangleRec(list_item, ColorAlpha(RED, 0.4f));
|
||||||
|
|
||||||
|
zpl_aabb2 swap_r = zpl_aabb2_cut_left(&op_item_r, 50.0f);
|
||||||
|
Rectangle list_text = aabb2_ray(op_item_r);
|
||||||
|
|
||||||
|
zpl_aabb2_cut_right(&swap_r, 5.0f);
|
||||||
|
zpl_aabb2 swap_top = zpl_aabb2_cut_top(&swap_r, 20.0f);
|
||||||
|
zpl_aabb2 swap_bottom = swap_r;
|
||||||
|
|
||||||
|
if (i > 0 && GuiButton(aabb2_ray(swap_top), "UP")) {
|
||||||
|
texed_swp_op(i, i-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i+1 < zpl_array_count(ctx.ops) && GuiButton(aabb2_ray(swap_bottom), "DOWN")) {
|
||||||
|
texed_swp_op(i, i+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
zpl_aabb2 remove_r = zpl_aabb2_cut_right(&op_item_r, 60.0f);
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(remove_r), "REMOVE")) {
|
||||||
|
texed_rem_op(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
zpl_aabb2 hidden_r = zpl_aabb2_cut_right(&op_item_r, 60.0f);
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(hidden_r), ctx.ops[i].is_hidden ? "SHOW" : "HIDE")) {
|
||||||
|
ctx.ops[i].is_hidden = !ctx.ops[i].is_hidden;
|
||||||
|
texed_repaint_preview();
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiDrawText(ctx.ops[i].name, GetTextBounds(LABEL, list_text), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(RAYWHITE, guiAlpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_add_op_dropbox_open && GuiDropdownBox(aabb2_ray(add_op_r), add_op_list, &add_op_dropbox_selected, true)) {
|
||||||
|
is_add_op_dropbox_open = false;
|
||||||
|
texed_add_op(add_op_dropbox_selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static inline
|
||||||
|
Rectangle aabb2_ray(zpl_aabb2 r) {
|
||||||
|
return (Rectangle) {
|
||||||
|
.x = r.min.x,
|
||||||
|
.y = r.min.y,
|
||||||
|
.width = r.max.x-r.min.x,
|
||||||
|
.height = r.max.y-r.min.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void texed_process_ops(void) {
|
||||||
|
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
||||||
|
td_op *op = &ctx.ops[i];
|
||||||
|
if (op->is_hidden) continue;
|
||||||
|
zpl_printf("processing op: %s ... \n", op->name);
|
||||||
|
|
||||||
|
switch (op->kind) {
|
||||||
|
case TOP_CLEAR: {
|
||||||
|
ImageClearBackground(&ctx.img, op->params[0].color);
|
||||||
|
}break;
|
||||||
|
case TOP_DRAW_RECT: {
|
||||||
|
ImageDrawRectangle(&ctx.img,
|
||||||
|
op->params[0].pix,
|
||||||
|
op->params[1].pix,
|
||||||
|
op->params[2].pix,
|
||||||
|
op->params[3].pix,
|
||||||
|
op->params[4].color);
|
||||||
|
}break;
|
||||||
|
default: {
|
||||||
|
zpl_printf("%s\n", "unsupported op!");
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void texed_draw_topbar(zpl_aabb2 r) {
|
||||||
|
zpl_aabb2 zoom_ctrl_r = zpl_aabb2_cut_left(&r, 150.0f);
|
||||||
|
|
||||||
|
zoom = GuiSlider(aabb2_ray(zoom_ctrl_r), "zoom: ", zpl_bprintf("%.02f x", zoom), zoom, 1.0f, 16.0f);
|
||||||
|
|
||||||
|
zpl_aabb2_cut_left(&r, 100.0f);
|
||||||
|
|
||||||
|
zpl_aabb2 new_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(new_prj_r), "NEW")) {
|
||||||
|
texed_destroy();
|
||||||
|
texed_new(TD_DEFAULT_IMG_WIDTH, TD_DEFAULT_IMG_HEIGHT); // TODO(zaklaus): show res panel
|
||||||
|
}
|
||||||
|
|
||||||
|
zpl_aabb2 load_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||||
|
static bool load_pending = false;
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(load_prj_r), "LOAD")) {
|
||||||
|
load_pending = true;
|
||||||
|
ctx.fileDialog.fileDialogActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.fileDialog.SelectFilePressed && load_pending) {
|
||||||
|
ctx.fileDialog.SelectFilePressed = false;
|
||||||
|
if (IsFileExtension(ctx.fileDialog.fileNameText, ".ecotex")) {
|
||||||
|
zpl_strcpy(filename, ctx.fileDialog.fileNameText);
|
||||||
|
ctx.filepath = filename;
|
||||||
|
load_pending = false;
|
||||||
|
} else {
|
||||||
|
ctx.fileDialog.fileDialogActive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zpl_aabb2 save_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||||
|
static bool save_as_pending = false;
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(save_prj_r), "SAVE")) {
|
||||||
|
if (ctx.filepath == NULL) {
|
||||||
|
save_as_pending = true;
|
||||||
|
ctx.fileDialog.fileDialogActive = true;
|
||||||
|
} else {
|
||||||
|
texed_save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zpl_aabb2 save_as_prj_r = zpl_aabb2_cut_left(&r, 60.0f);
|
||||||
|
|
||||||
|
if (GuiButton(aabb2_ray(save_as_prj_r), "SAVE AS")) {
|
||||||
|
save_as_pending = true;
|
||||||
|
ctx.fileDialog.fileDialogActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.fileDialog.SelectFilePressed && save_as_pending) {
|
||||||
|
ctx.fileDialog.SelectFilePressed = false;
|
||||||
|
zpl_strcpy(ctx.fileDialog.fileNameText, zpl_bprintf("%s.ecotex", ctx.fileDialog.fileNameText));
|
||||||
|
zpl_strcpy(filename, ctx.fileDialog.fileNameText);
|
||||||
|
ctx.filepath = filename;
|
||||||
|
save_as_pending = false;
|
||||||
|
texed_save();
|
||||||
|
}
|
||||||
|
|
||||||
|
zpl_aabb2 prj_name_r = zpl_aabb2_cut_right(&r, 200.0f);
|
||||||
|
GuiDrawText(zpl_bprintf("Project: %s", ctx.filepath ? ctx.filepath : "(unnamed)"), GetTextBounds(LABEL, aabb2_ray(prj_name_r)), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(BLACK, guiAlpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
void texed_swp_op(int idx, int idx2) {
|
||||||
|
assert(idx >= 0 && idx < (int)zpl_array_count(ctx.ops));
|
||||||
|
assert(idx2 >= 0 && idx2 < (int)zpl_array_count(ctx.ops));
|
||||||
|
|
||||||
|
td_op tmp = ctx.ops[idx2];
|
||||||
|
ctx.ops[idx2] = ctx.ops[idx];
|
||||||
|
ctx.ops[idx] = tmp;
|
||||||
|
|
||||||
|
texed_repaint_preview();
|
||||||
|
}
|
||||||
|
|
||||||
|
void texed_load(void) {
|
||||||
|
// TODO(zaklaus):
|
||||||
|
}
|
||||||
|
|
||||||
|
void texed_save(void) {
|
||||||
|
assert(ctx.filepath);
|
||||||
|
|
||||||
|
// TODO(zaklaus):
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
static uint16_t screenWidth = 1600;
|
static uint16_t screenWidth = 1600;
|
||||||
static uint16_t screenHeight = 900;
|
static uint16_t screenHeight = 900;
|
||||||
static float target_zoom = 4.0f;
|
static float target_zoom = 1.5f;
|
||||||
static float zoom_overlay_tran = 0.0f;
|
static float zoom_overlay_tran = 0.0f;
|
||||||
static bool request_shutdown;
|
static bool request_shutdown;
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ void platform_init() {
|
||||||
render_camera.target = (Vector2){0.0f,0.0f};
|
render_camera.target = (Vector2){0.0f,0.0f};
|
||||||
render_camera.offset = (Vector2){screenWidth/2.0f, screenHeight/2.0f};
|
render_camera.offset = (Vector2){screenWidth/2.0f, screenHeight/2.0f};
|
||||||
render_camera.rotation = 0.0f;
|
render_camera.rotation = 0.0f;
|
||||||
render_camera.zoom = 4.0f;
|
render_camera.zoom = 1.5f;
|
||||||
|
|
||||||
// NOTE(zaklaus): Paint the screen before we load the game
|
// NOTE(zaklaus): Paint the screen before we load the game
|
||||||
// TODO(zaklaus): Render a cool loading screen background maybe? :wink: :wink:
|
// TODO(zaklaus): Render a cool loading screen background maybe? :wink: :wink:
|
||||||
|
@ -65,7 +65,7 @@ void platform_input() {
|
||||||
float mouse_z = (GetMouseWheelMove()*0.5f);
|
float mouse_z = (GetMouseWheelMove()*0.5f);
|
||||||
|
|
||||||
if (mouse_z != 0.0f) {
|
if (mouse_z != 0.0f) {
|
||||||
target_zoom = zpl_clamp(target_zoom+mouse_z, 0.3f, 10.0f);
|
target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 10.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(zaklaus): keystate handling
|
// NOTE(zaklaus): keystate handling
|
||||||
|
@ -198,7 +198,7 @@ void DEBUG_draw_ground(uint64_t key, entity_view * data) {
|
||||||
static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; }
|
static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; }
|
||||||
|
|
||||||
void DEBUG_draw_entities(uint64_t key, entity_view * data) {
|
void DEBUG_draw_entities(uint64_t key, entity_view * data) {
|
||||||
uint16_t size = 4;
|
uint16_t size = 16;
|
||||||
uint16_t font_size = (uint16_t)lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom);
|
uint16_t font_size = (uint16_t)lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom);
|
||||||
float font_spacing = 1.1f;
|
float font_spacing = 1.1f;
|
||||||
float title_bg_offset = 4;
|
float title_bg_offset = 4;
|
||||||
|
|
|
@ -0,0 +1,663 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* FileDialog v1.1 - Modal file dialog to open/save files
|
||||||
|
*
|
||||||
|
* MODULE USAGE:
|
||||||
|
* #define GUI_FILE_DIALOG_IMPLEMENTATION
|
||||||
|
* #include "gui_file_dialog.h"
|
||||||
|
*
|
||||||
|
* INIT: GuiFileDialogState state = InitGuiFileDialog();
|
||||||
|
* DRAW: GuiFileDialog(&state);
|
||||||
|
*
|
||||||
|
* NOTE: This module depends on some raylib file system functions:
|
||||||
|
* - GetDirectoryFiles()
|
||||||
|
* - ClearDirectoryFiles()
|
||||||
|
* - GetWorkingDirectory()
|
||||||
|
* - DirectoryExists()
|
||||||
|
* - FileExists()
|
||||||
|
*
|
||||||
|
* LICENSE: zlib/libpng
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019-2020 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"
|
||||||
|
|
||||||
|
// WARNING: raygui implementation is expected to be defined before including this header
|
||||||
|
#undef RAYGUI_IMPLEMENTATION
|
||||||
|
#include "raygui.h"
|
||||||
|
|
||||||
|
#ifndef GUI_FILE_DIALOG_H
|
||||||
|
#define GUI_FILE_DIALOG_H
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vector2 position;
|
||||||
|
Vector2 size;
|
||||||
|
|
||||||
|
bool fileDialogActive;
|
||||||
|
|
||||||
|
bool dirPathEditMode;
|
||||||
|
char dirPathText[256];
|
||||||
|
|
||||||
|
int filesListScrollIndex;
|
||||||
|
bool filesListEditMode;
|
||||||
|
int filesListActive;
|
||||||
|
|
||||||
|
bool fileNameEditMode;
|
||||||
|
char fileNameText[256];
|
||||||
|
bool SelectFilePressed;
|
||||||
|
bool CancelFilePressed;
|
||||||
|
int fileTypeActive;
|
||||||
|
int itemFocused;
|
||||||
|
|
||||||
|
// Custom state variables (depend on development software)
|
||||||
|
// NOTE: This variables should be added manually if required
|
||||||
|
char **dirFiles;
|
||||||
|
int dirFilesCount;
|
||||||
|
|
||||||
|
char filterExt[256];
|
||||||
|
|
||||||
|
char dirPathTextCopy[256];
|
||||||
|
char fileNameTextCopy[256];
|
||||||
|
|
||||||
|
int prevFilesListActive;
|
||||||
|
|
||||||
|
} GuiFileDialogState;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" { // Prevents name mangling of functions
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Defines and Macros
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
//...
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Types and Structures Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// ...
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Global Variables Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
//...
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Module Functions Declaration
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
GuiFileDialogState InitGuiFileDialog(int width, int height, const char *initPath, bool active);
|
||||||
|
void GuiFileDialog(GuiFileDialogState *state);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // GUI_FILE_DIALOG_H
|
||||||
|
|
||||||
|
/***********************************************************************************
|
||||||
|
*
|
||||||
|
* GUI_FILE_DIALOG IMPLEMENTATION
|
||||||
|
*
|
||||||
|
************************************************************************************/
|
||||||
|
#if defined(GUI_FILE_DIALOG_IMPLEMENTATION)
|
||||||
|
|
||||||
|
#include "raygui.h"
|
||||||
|
|
||||||
|
#include <string.h> // Required for: strcpy()
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Defines and Macros
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
#define MAX_DIRECTORY_FILES 1024
|
||||||
|
#define MAX_DIR_PATH_LENGTH 1024
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Types and Structures Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
|
||||||
|
// Detailed file info type
|
||||||
|
typedef struct FileInfo {
|
||||||
|
const char *name;
|
||||||
|
int size;
|
||||||
|
int modTime;
|
||||||
|
int type;
|
||||||
|
int icon;
|
||||||
|
} FileInfo;
|
||||||
|
#else
|
||||||
|
// Filename only
|
||||||
|
typedef char *FileInfo;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Global Variables Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
FileInfo *dirFilesIcon = NULL;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Internal Module Functions Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Read all filenames from directory (supported file types)
|
||||||
|
static char **ReadDirectoryFiles(const char *dir, int *filesCount, char *filterExt);
|
||||||
|
|
||||||
|
#if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
|
||||||
|
// List View control for files info with extended parameters
|
||||||
|
static int GuiListViewFiles(Rectangle bounds, FileInfo *files, int count, int *focus, int *scrollIndex, int active);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Module Functions Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
GuiFileDialogState InitGuiFileDialog(int width, int height, const char *initPath, bool active)
|
||||||
|
{
|
||||||
|
GuiFileDialogState state = { 0 };
|
||||||
|
|
||||||
|
// Default dialog size is 440x310
|
||||||
|
state.size.x = width == -1 ? 440 : width;
|
||||||
|
state.size.y = height == -1 ? 310 : height;
|
||||||
|
state.position = (Vector2){ GetScreenWidth()/2 - state.size.x/2, GetScreenHeight()/2 - state.size.y/2 };
|
||||||
|
|
||||||
|
state.fileDialogActive = active;
|
||||||
|
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());
|
||||||
|
|
||||||
|
strcpy(state.dirPathTextCopy, state.dirPathText);
|
||||||
|
strcpy(state.fileNameTextCopy, state.fileNameText);
|
||||||
|
|
||||||
|
strcpy(state.filterExt, "all");
|
||||||
|
|
||||||
|
state.dirFilesCount = 0;
|
||||||
|
state.dirFiles = NULL; // NOTE: Loaded lazily on window active
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read files in new path
|
||||||
|
static void FD_RELOAD_DIRPATH(GuiFileDialogState *state)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < state->dirFilesCount; i++) RL_FREE(state->dirFiles[i]);
|
||||||
|
RL_FREE(state->dirFiles);
|
||||||
|
|
||||||
|
state->dirFiles = ReadDirectoryFiles(state->dirPathText, &state->dirFilesCount, state->filterExt);
|
||||||
|
state->itemFocused = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update and draw file dialog
|
||||||
|
void GuiFileDialog(GuiFileDialogState *state)
|
||||||
|
{
|
||||||
|
if (state->fileDialogActive)
|
||||||
|
{
|
||||||
|
const int winWidth = state->size.x;
|
||||||
|
const int winHeight = state->size.y;
|
||||||
|
|
||||||
|
// Load dirFilesIcon and state->dirFiles lazily on windows open
|
||||||
|
// NOTE: they are automatically unloaded at fileDialog closing
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
if (dirFilesIcon == NULL)
|
||||||
|
{
|
||||||
|
dirFilesIcon = (FileInfo *)RL_MALLOC(MAX_DIRECTORY_FILES*sizeof(FileInfo)); // Max files to read
|
||||||
|
for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesIcon[i] = (char *)calloc(MAX_DIR_PATH_LENGTH, 1); // Max file name length
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->dirFiles == NULL)
|
||||||
|
{
|
||||||
|
state->dirFiles = ReadDirectoryFiles(state->dirPathText, &state->dirFilesCount, state->filterExt);
|
||||||
|
|
||||||
|
for(int f = 0; f < state->dirFilesCount; f++)
|
||||||
|
{
|
||||||
|
if (strcmp(state->fileNameText, state->dirFiles[f]) == 0)
|
||||||
|
{
|
||||||
|
if (state->filesListActive != f) state->filesListScrollIndex = state->filesListActive = f; // Make it active and visible only on first call
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), 0.85f));
|
||||||
|
state->fileDialogActive = !GuiWindowBox((Rectangle){ state->position.x + 0, state->position.y + 0, winWidth, winHeight }, "#198#eco2d file dialog");
|
||||||
|
|
||||||
|
if (GuiButton((Rectangle){ state->position.x + winWidth - 50, state->position.y + 35, 40, 25 }, "< ..")) // || IsKeyReleased(KEY_DPAD_Y))
|
||||||
|
{
|
||||||
|
// Move dir path one level up
|
||||||
|
strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText));
|
||||||
|
|
||||||
|
// RL_FREE previous dirFiles (reloaded by ReadDirectoryFiles())
|
||||||
|
FD_RELOAD_DIRPATH(state);
|
||||||
|
|
||||||
|
state->filesListActive = -1;
|
||||||
|
strcpy(state->fileNameText, "\0");
|
||||||
|
strcpy(state->fileNameTextCopy, state->fileNameText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GuiTextBox((Rectangle){ state->position.x + 10, state->position.y + 35, winWidth - 65, 25 }, state->dirPathText, 256, state->dirPathEditMode))
|
||||||
|
{
|
||||||
|
if (state->dirPathEditMode)
|
||||||
|
{
|
||||||
|
// Verify if a valid path has been introduced
|
||||||
|
if (DirectoryExists(state->dirPathText))
|
||||||
|
{
|
||||||
|
// RL_FREE previous dirFiles (reloaded by ReadDirectoryFiles())
|
||||||
|
FD_RELOAD_DIRPATH(state);
|
||||||
|
|
||||||
|
strcpy(state->dirPathTextCopy, state->dirPathText);
|
||||||
|
}
|
||||||
|
else strcpy(state->dirPathText, state->dirPathTextCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->dirPathEditMode = !state->dirPathEditMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prevTextAlignment = GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT);
|
||||||
|
int prevElementsHeight = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT);
|
||||||
|
GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
|
||||||
|
GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 24);
|
||||||
|
|
||||||
|
// TODO: ListViewElements should be aligned left
|
||||||
|
# if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
|
||||||
|
FileInfo fileInfo;
|
||||||
|
state->filesListActive = GuiListViewFiles((Rectangle){ state->position.x + 10, state->position.y + 70, winWidth - 20, winHeight - 135 }, fileInfo, state->dirFilesCount, &state->itemFocused, &state->filesListScrollIndex, state->filesListActive);
|
||||||
|
# else
|
||||||
|
state->filesListActive = GuiListViewEx((Rectangle){ state->position.x + 10, state->position.y + 70, winWidth - 20, winHeight - 135 }, (const char**)dirFilesIcon, state->dirFilesCount, &state->itemFocused, &state->filesListScrollIndex, state->filesListActive);
|
||||||
|
# endif
|
||||||
|
GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, prevTextAlignment);
|
||||||
|
GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, prevElementsHeight);
|
||||||
|
|
||||||
|
if ((state->filesListActive >= 0) && (state->filesListActive != state->prevFilesListActive))
|
||||||
|
//&& (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_DPAD_A)))
|
||||||
|
{
|
||||||
|
strcpy(state->fileNameText, state->dirFiles[state->filesListActive]);
|
||||||
|
|
||||||
|
if (DirectoryExists(TextFormat("%s/%s", state->dirPathText, state->fileNameText)))
|
||||||
|
{
|
||||||
|
if (TextIsEqual(state->fileNameText, "..")) strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText));
|
||||||
|
else strcpy(state->dirPathText, TextFormat("%s/%s", strcmp(state->dirPathText, "/")==0 ? "" : state->dirPathText, state->fileNameText));
|
||||||
|
|
||||||
|
strcpy(state->dirPathTextCopy, state->dirPathText);
|
||||||
|
|
||||||
|
// RL_FREE previous dirFiles (reloaded by ReadDirectoryFiles())
|
||||||
|
FD_RELOAD_DIRPATH(state);
|
||||||
|
|
||||||
|
strcpy(state->dirPathTextCopy, state->dirPathText);
|
||||||
|
|
||||||
|
state->filesListActive = -1;
|
||||||
|
strcpy(state->fileNameText, "\0");
|
||||||
|
strcpy(state->fileNameTextCopy, state->fileNameText);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->prevFilesListActive = state->filesListActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiLabel((Rectangle){ state->position.x + 10, state->position.y + winHeight - 60, 68, 25 }, "File name:");
|
||||||
|
|
||||||
|
if (GuiTextBox((Rectangle){ state->position.x + 75, state->position.y + winHeight - 60, winWidth - 200, 25 }, state->fileNameText, 128, state->fileNameEditMode))
|
||||||
|
{
|
||||||
|
if (*state->fileNameText)
|
||||||
|
{
|
||||||
|
// Verify if a valid filename has been introduced
|
||||||
|
//if (FileExists(TextFormat("%s/%s", state->dirPathText, state->fileNameText)))
|
||||||
|
{
|
||||||
|
// Select filename from list view
|
||||||
|
for (int i = 0; i < state->dirFilesCount; i++)
|
||||||
|
{
|
||||||
|
if (TextIsEqual(state->fileNameText, state->dirFiles[i]))
|
||||||
|
{
|
||||||
|
state->filesListActive = i;
|
||||||
|
strcpy(state->fileNameTextCopy, state->fileNameText);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(state->fileNameText, state->fileNameTextCopy);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
state->fileNameEditMode = !state->fileNameEditMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->fileTypeActive = GuiComboBox((Rectangle){ state->position.x + 75, state->position.y + winHeight - 30, winWidth - 200, 25 }, "ecotex files", state->fileTypeActive);
|
||||||
|
GuiLabel((Rectangle){ state->position.x + 10, state->position.y + winHeight - 30, 68, 25 }, "File filter:");
|
||||||
|
|
||||||
|
state->SelectFilePressed = GuiButton((Rectangle){ state->position.x + winWidth - 120, state->position.y + winHeight - 60, 110,
|
||||||
|
#ifdef PLATFORM_DESKTOP
|
||||||
|
25
|
||||||
|
#else
|
||||||
|
25 + 30
|
||||||
|
#endif
|
||||||
|
}, "Select") || IsKeyPressed(KEY_ENTER);
|
||||||
|
|
||||||
|
if (state->SelectFilePressed) state->fileDialogActive = false;
|
||||||
|
|
||||||
|
#ifdef PLATFORM_DESKTOP
|
||||||
|
if (GuiButton((Rectangle){ state->position.x + winWidth - 120, state->position.y + winHeight - 30, 110, 25 }, "Cancel")) state->fileDialogActive = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// File dialog has been closed!
|
||||||
|
if (!state->fileDialogActive)
|
||||||
|
{
|
||||||
|
// RL_FREE dirFiles memory
|
||||||
|
for (int i = 0; i < state->dirFilesCount; i++)
|
||||||
|
{
|
||||||
|
RL_FREE(state->dirFiles[i]);
|
||||||
|
RL_FREE(dirFilesIcon[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
RL_FREE(state->dirFiles);
|
||||||
|
RL_FREE(dirFilesIcon);
|
||||||
|
|
||||||
|
dirFilesIcon = NULL;
|
||||||
|
state->dirFiles = 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 all filenames from directory (supported file types)
|
||||||
|
static char **ReadDirectoryFiles(const char *dir, int *filesCount, char *filterExt)
|
||||||
|
{
|
||||||
|
int validFilesCount = 0;
|
||||||
|
char **validFiles = (char **)RL_MALLOC(MAX_DIRECTORY_FILES*sizeof(char *)); // Max files to read
|
||||||
|
for (int i = 0; i < MAX_DIRECTORY_FILES; i++) validFiles[i] = (char *)RL_MALLOC(MAX_DIR_PATH_LENGTH); // Max file name length
|
||||||
|
|
||||||
|
int filterExtCount = 0;
|
||||||
|
const char **extensions = GuiTextSplit(filterExt, &filterExtCount, NULL);
|
||||||
|
bool filterExtensions = true;
|
||||||
|
|
||||||
|
int dirFilesCount = 0;
|
||||||
|
char **files = GetDirectoryFiles(dir, &dirFilesCount);
|
||||||
|
|
||||||
|
// Sort files and directories: dir by name + files by name
|
||||||
|
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Quicksort#C
|
||||||
|
if (dirFilesCount > 1)
|
||||||
|
{
|
||||||
|
const int MAX = 64;
|
||||||
|
unsigned int left = 0, stack[64], pos = 0, seed = rand(), len = dirFilesCount;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
for (; left + 1 < len; len++) // Sort left to len - 1
|
||||||
|
{
|
||||||
|
if (pos == MAX) len = stack[pos = 0]; // Stack overflow, reset
|
||||||
|
char *pivot = files[left + seed%(len - left)]; // Pick random pivot
|
||||||
|
seed = seed*69069 + 1; // Next pseudo-random number
|
||||||
|
stack[pos++] = len; // Sort right part later
|
||||||
|
|
||||||
|
for (unsigned int right = left - 1;;) // Inner loop: partitioning
|
||||||
|
{
|
||||||
|
while (FileCompare(files[++right], pivot, dir) < 0); // Look for greater element
|
||||||
|
while (FileCompare(pivot, files[--len], dir) < 0); // Look for smaller element
|
||||||
|
if (right >= len) break; // Partition point found?
|
||||||
|
char *temp = files[right];
|
||||||
|
files[right] = files[len]; // The only swap
|
||||||
|
files[len] = temp;
|
||||||
|
} // Partitioned, continue left part
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == 0) break; // Stack empty?
|
||||||
|
left = len; // Left to right is sorted
|
||||||
|
len = stack[--pos]; // Get next range to sort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextIsEqual(extensions[0], "all")) filterExtensions = false;
|
||||||
|
|
||||||
|
for (int i = 0; (i < dirFilesCount) && (validFilesCount < MAX_DIRECTORY_FILES); i++)
|
||||||
|
{
|
||||||
|
if (TextIsEqual(files[i], ".")) continue;
|
||||||
|
|
||||||
|
if (!filterExtensions)
|
||||||
|
{
|
||||||
|
strncpy(validFiles[validFilesCount], files[i], MAX_DIR_PATH_LENGTH);
|
||||||
|
|
||||||
|
// Only filter files by extensions, directories should be available
|
||||||
|
if (DirectoryExists(TextFormat("%s/%s", dir, files[i]))) strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 1, files[i]));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Assign custom filetype icons depending on file extension (image, audio, text, video, models...)
|
||||||
|
|
||||||
|
if (IsFileExtension(files[i], ".png")) strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 12, files[i]));
|
||||||
|
else strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 10, files[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
validFilesCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int j = 0; j < filterExtCount; j++)
|
||||||
|
{
|
||||||
|
// Check file type extensions supported
|
||||||
|
// NOTE: We just store valid files list
|
||||||
|
if (IsFileExtension(files[i], extensions[j]))
|
||||||
|
{
|
||||||
|
// TODO: Assign custom filetype icons depending on file extension (image, audio, text, video, models...)
|
||||||
|
|
||||||
|
if (IsFileExtension(files[i], ".png")) strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 12, files[i]));
|
||||||
|
else strcpy(dirFilesIcon[validFilesCount], TextFormat("#%i#%s", 10, files[i]));
|
||||||
|
|
||||||
|
validFilesCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearDirectoryFiles();
|
||||||
|
|
||||||
|
*filesCount = validFilesCount;
|
||||||
|
return validFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(USE_CUSTOM_LISTVIEW_FILEINFO)
|
||||||
|
// List View control for files info with extended parameters
|
||||||
|
static int GuiListViewFiles(Rectangle bounds, FileInfo *files, int count, int *focus, int *scrollIndex, int active)
|
||||||
|
{
|
||||||
|
GuiControlState state = guiState;
|
||||||
|
int itemFocused = (focus == NULL)? -1 : *focus;
|
||||||
|
int itemSelected = active;
|
||||||
|
|
||||||
|
// Check if we need a scroll bar
|
||||||
|
bool useScrollBar = false;
|
||||||
|
if ((GuiGetStyle(LISTVIEW, 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;
|
||||||
|
|
||||||
|
return itemSelected;
|
||||||
|
}
|
||||||
|
#endif // USE_CUSTOM_LISTVIEW_FILEINFO
|
||||||
|
|
||||||
|
#endif // GUI_FILE_DIALOG_IMPLEMENTATION
|
|
@ -31,6 +31,8 @@ GitHub:
|
||||||
https://github.com/zpl-c/zpl
|
https://github.com/zpl-c/zpl
|
||||||
|
|
||||||
Version History:
|
Version History:
|
||||||
|
14.1.4 - Fix win32 missing CRITICAL_SECTION definition if
|
||||||
|
- ZPL_NO_WINDOWS_H is defined
|
||||||
14.1.0 - add hashtable map_mut method
|
14.1.0 - add hashtable map_mut method
|
||||||
14.0.1 - fix zpl_array_remove_at boundary bug
|
14.0.1 - fix zpl_array_remove_at boundary bug
|
||||||
14.0.0 - heap memory allocator analysis
|
14.0.0 - heap memory allocator analysis
|
||||||
|
@ -361,7 +363,7 @@ License:
|
||||||
|
|
||||||
#define ZPL_VERSION_MAJOR 14
|
#define ZPL_VERSION_MAJOR 14
|
||||||
#define ZPL_VERSION_MINOR 1
|
#define ZPL_VERSION_MINOR 1
|
||||||
#define ZPL_VERSION_PATCH 2
|
#define ZPL_VERSION_PATCH 4
|
||||||
#define ZPL_VERSION_PRE ""
|
#define ZPL_VERSION_PRE ""
|
||||||
|
|
||||||
// file: zpl_hedley.h
|
// file: zpl_hedley.h
|
||||||
|
@ -7464,7 +7466,7 @@ License:
|
||||||
|
|
||||||
typedef struct zpl_mutex {
|
typedef struct zpl_mutex {
|
||||||
#if defined(ZPL_SYSTEM_WINDOWS)
|
#if defined(ZPL_SYSTEM_WINDOWS)
|
||||||
CRITICAL_SECTION win32_critical_section;
|
zpl_u64 win32_critical_section[sizeof(zpl_usize) / 2 + 1];
|
||||||
#else
|
#else
|
||||||
pthread_mutex_t pthread_mutex;
|
pthread_mutex_t pthread_mutex;
|
||||||
#endif
|
#endif
|
||||||
|
@ -16186,7 +16188,7 @@ License:
|
||||||
|
|
||||||
void zpl_mutex_init(zpl_mutex *m) {
|
void zpl_mutex_init(zpl_mutex *m) {
|
||||||
# if defined(ZPL_SYSTEM_WINDOWS)
|
# if defined(ZPL_SYSTEM_WINDOWS)
|
||||||
InitializeCriticalSection(&m->win32_critical_section);
|
InitializeCriticalSection((CRITICAL_SECTION*)m->win32_critical_section);
|
||||||
# else
|
# else
|
||||||
pthread_mutex_init(&m->pthread_mutex, NULL);
|
pthread_mutex_init(&m->pthread_mutex, NULL);
|
||||||
# endif
|
# endif
|
||||||
|
@ -16194,7 +16196,7 @@ License:
|
||||||
|
|
||||||
void zpl_mutex_destroy(zpl_mutex *m) {
|
void zpl_mutex_destroy(zpl_mutex *m) {
|
||||||
# if defined(ZPL_SYSTEM_WINDOWS)
|
# if defined(ZPL_SYSTEM_WINDOWS)
|
||||||
DeleteCriticalSection(&m->win32_critical_section);
|
DeleteCriticalSection((CRITICAL_SECTION*)m->win32_critical_section);
|
||||||
# else
|
# else
|
||||||
pthread_mutex_destroy(&m->pthread_mutex);
|
pthread_mutex_destroy(&m->pthread_mutex);
|
||||||
# endif
|
# endif
|
||||||
|
@ -16202,7 +16204,7 @@ License:
|
||||||
|
|
||||||
void zpl_mutex_lock(zpl_mutex *m) {
|
void zpl_mutex_lock(zpl_mutex *m) {
|
||||||
# if defined(ZPL_SYSTEM_WINDOWS)
|
# if defined(ZPL_SYSTEM_WINDOWS)
|
||||||
EnterCriticalSection(&m->win32_critical_section);
|
EnterCriticalSection((CRITICAL_SECTION*)m->win32_critical_section);
|
||||||
# else
|
# else
|
||||||
pthread_mutex_lock(&m->pthread_mutex);
|
pthread_mutex_lock(&m->pthread_mutex);
|
||||||
# endif
|
# endif
|
||||||
|
@ -16210,7 +16212,7 @@ License:
|
||||||
|
|
||||||
zpl_b32 zpl_mutex_try_lock(zpl_mutex *m) {
|
zpl_b32 zpl_mutex_try_lock(zpl_mutex *m) {
|
||||||
# if defined(ZPL_SYSTEM_WINDOWS)
|
# if defined(ZPL_SYSTEM_WINDOWS)
|
||||||
return TryEnterCriticalSection(&m->win32_critical_section);
|
return TryEnterCriticalSection((CRITICAL_SECTION*)m->win32_critical_section);
|
||||||
# else
|
# else
|
||||||
return pthread_mutex_trylock(&m->pthread_mutex);
|
return pthread_mutex_trylock(&m->pthread_mutex);
|
||||||
# endif
|
# endif
|
||||||
|
@ -16218,7 +16220,7 @@ License:
|
||||||
|
|
||||||
void zpl_mutex_unlock(zpl_mutex *m) {
|
void zpl_mutex_unlock(zpl_mutex *m) {
|
||||||
# if defined(ZPL_SYSTEM_WINDOWS)
|
# if defined(ZPL_SYSTEM_WINDOWS)
|
||||||
LeaveCriticalSection(&m->win32_critical_section);
|
LeaveCriticalSection((CRITICAL_SECTION*)m->win32_critical_section);
|
||||||
# else
|
# else
|
||||||
pthread_mutex_unlock(&m->pthread_mutex);
|
pthread_mutex_unlock(&m->pthread_mutex);
|
||||||
# endif
|
# endif
|
||||||
|
|
Loading…
Reference in New Issue