From c2dacf23cc32f0231af867e122d5fb2f52c3d748 Mon Sep 17 00:00:00 2001 From: vurtun Date: Fri, 23 Oct 2015 22:55:42 +0200 Subject: [PATCH] added linux file selector example --- example/filex/Makefile | 32 ++ example/filex/filex.c | 851 ++++++++++++++++++++++++++++++ example/filex/icons/computer.png | Bin 0 -> 620 bytes example/filex/icons/default.png | Bin 0 -> 460 bytes example/filex/icons/desktop.png | Bin 0 -> 583 bytes example/filex/icons/directory.png | Bin 0 -> 533 bytes example/filex/icons/font.png | Bin 0 -> 561 bytes example/filex/icons/home.png | Bin 0 -> 819 bytes example/filex/icons/img.png | Bin 0 -> 648 bytes example/filex/icons/movie.png | Bin 0 -> 626 bytes example/filex/icons/music.png | Bin 0 -> 610 bytes example/filex/icons/text.png | Bin 0 -> 601 bytes 12 files changed, 883 insertions(+) create mode 100644 example/filex/Makefile create mode 100644 example/filex/filex.c create mode 100644 example/filex/icons/computer.png create mode 100644 example/filex/icons/default.png create mode 100644 example/filex/icons/desktop.png create mode 100644 example/filex/icons/directory.png create mode 100644 example/filex/icons/font.png create mode 100644 example/filex/icons/home.png create mode 100644 example/filex/icons/img.png create mode 100644 example/filex/icons/movie.png create mode 100644 example/filex/icons/music.png create mode 100644 example/filex/icons/text.png diff --git a/example/filex/Makefile b/example/filex/Makefile new file mode 100644 index 0000000..91635ca --- /dev/null +++ b/example/filex/Makefile @@ -0,0 +1,32 @@ +BIN = filex + +# Compiler +CC = clang +DCC = gcc + +CFLAGS = -std=c99 -pedantic -Wdeprecated-declarations +CFLAGS += -g -Wall -Wextra -Wformat-security -Wunreachable-code +CFLAGS += -fstack-protector-strong -Winline -Wshadow -Wwrite-strings -fstrict-aliasing +CFLAGS += -Wstrict-prototypes -Wold-style-definition -Wconversion -Wfloat-equal +CFLAGS += -Wredundant-decls -Wnested-externs -Wmissing-include-dirs +CFLAGS += -Wshadow -Wcast-qual -Wcast-align -Wmissing-prototypes -Wconversion +CFLAGS += -Wswitch-default -Wundef -Wno-unused -Wstrict-overflow=5 -Wsign-conversion +CFLAGS += -Winit-self -Wstrict-aliasing -ftrapv -Wpointer-arith +CFLAGS += -Wswitch-enum -Winvalid-pch -Wbad-function-cast -fno-omit-frame-pointer + +SRC = filex.c ../../zahnrad.c +OBJ = $(SRC:.c=.o) + +# Modes +.PHONY: clang +clang: CC = clang +clang: $(BIN) + +.PHONY: gcc +gcc: CC = gcc +gcc: $(BIN) + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -D_POSIX_C_SOURCE=200809L -o bin/$(BIN) -lSDL2 -lGL -lm -lGLU -lGLEW diff --git a/example/filex/filex.c b/example/filex/filex.c new file mode 100644 index 0000000..b123baf --- /dev/null +++ b/example/filex/filex.c @@ -0,0 +1,851 @@ +/* + Copyright (c) 2015 Micha Mettke + + 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 +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#error "windows is not supported" +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define NANOVG_GLES2_IMPLEMENTATION +#include "../../demo/nanovg/dep/nanovg.h" +#include "../../demo/nanovg/dep/nanovg_gl.h" +#include "../../demo/nanovg/dep/nanovg_gl_utils.h" +#include "../../demo/nanovg/dep/nanovg.c" + +/* macros */ +#define MAX_BUFFER 64 +#define MAX_MEMORY (16 * 1024) +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define CLAMP(i,v,x) (MAX(MIN(v,x), i)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) +#define UNUSED(a) ((void)(a)) + +#include "../../zahnrad.h" + +#define MAX_PATH_LEN 512 +#define MAX_WINDOWS 32 +#define MAX_COMMAND_MEMORY (64 * 1024) + +/* ================================================================= + * + * UTIL + * + * ================================================================= */ +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static size_t +file_size(const char *path) +{ + int result; + struct stat statbuf; + result = stat(path, &statbuf); + if (result != 0) + die("[Platform]: failed to find file: %s\n", path); + return (size_t)statbuf.st_size; +} + +static size_t +file_load(const char *path, void *memory, size_t size) +{ + int fd; + ssize_t res; + assert(path && size); + fd = open(path, O_RDONLY); + if (fd == -1) + die("Failed to open file: %s (%s)\n", path, strerror(errno)); + res = read(fd, memory, size); + if (res < 0) die("Failed to call read: %s",strerror(errno)); + close(fd); + return (size_t)(res); +} + +static void +dir_free_list(char **list, size_t size) +{ + size_t i; + for (i = 0; i < size; ++i) + free(list[i]); + free(list); +} + +static char** +dir_list(const char *dir, int return_subdirs, size_t *count) +{ + size_t n = 0; + char buffer[MAX_PATH_LEN]; + char **results = NULL; + const DIR *none = NULL; + size_t capacity = 32; + size_t size; + DIR *z; + + assert(dir); + assert(count); + strncpy(buffer, dir, MAX_PATH_LEN); + n = strlen(buffer); + + if (n > 0 && (buffer[n-1] != '/')) { + buffer[n++] = '/'; + } + + size = 0; + + z = opendir(dir); + if (z != none) { + int nonempty = 1; + struct dirent *data = readdir(z); + nonempty = (data != NULL); + if (!nonempty) return NULL; + + do { + DIR *y; + char *p; + int is_subdir; + if (data->d_name[0] == '.') + continue; + + strncpy(buffer + n, data->d_name, MAX_PATH_LEN-n); + y = opendir(buffer); + is_subdir = (y != NULL); + if (y != NULL) closedir(y); + + if ((return_subdirs && is_subdir) || (!is_subdir && !return_subdirs)){ + if (!size) { + results = calloc(sizeof(char*), capacity); + } else if (size >= capacity) { + capacity = capacity * 2; + results = realloc(results, capacity * sizeof(char*)); + } + p = strdup(data->d_name); + results[size++] = p; + } + } while ((data = readdir(z)) != NULL); + } + + if (z) closedir(z); + *count = size; + return results; +} + +static void* +xcalloc(size_t siz, size_t n) +{ + void *ptr = calloc(siz, n); + if (!ptr) die("Out of memory\n"); + return ptr; +} +/* ================================================================= + * + * MEDIA + * + * ================================================================= */ +struct icon { + int image; + struct zr_image img; +}; + +struct icons { + struct icon desktop; + struct icon home; + struct icon computer; + struct icon directory; + /* file icons */ + struct icon default_file; + struct icon text_file; + struct icon music_file; + struct icon font_file; + struct icon img_file; + struct icon movie_file; +}; + +enum file_groups { + FILE_GROUP_DEFAULT, + FILE_GROUP_TEXT, + FILE_GROUP_MUSIC, + FILE_GROUP_FONT, + FILE_GROUP_IMAGE, + FILE_GROUP_MOVIE, + FILE_GROUP_MAX +}; + +enum file_types { + FILE_DEFAULT, + FILE_TEXT, + FILE_C_SOURCE, + FILE_CPP_SOURCE, + FILE_HEADER, + FILE_MP3, + FILE_WAV, + FILE_OGG, + FILE_TTF, + FILE_BMP, + FILE_PNG, + FILE_JPEG, + FILE_PCX, + FILE_TGA, + FILE_GIF, + FILE_MAX +}; + +struct file_group { + enum file_groups group; + const char *name; + struct icon *icon; +}; + +struct file { + enum file_types type; + const char *suffix; + enum file_groups group; +}; + +struct media { + int font; + int icon_sheet; + struct icons icons; + struct file_group group[FILE_GROUP_MAX]; + struct file files[FILE_MAX]; +}; + +static struct icon +ICON(NVGcontext *vg, const char *path) +{ + struct icon i; + i.image = nvgCreateImage(vg, path, 0); + i.img = zr_image_id(i.image); + return i; +} + +static struct file_group +FILE_GROUP(enum file_groups group, const char *name, struct icon *icon) +{ + struct file_group fg; + fg.group = group; + fg.name = name; + fg.icon = icon; + return fg; +} + +static struct file +FILE_DEF(enum file_types type, const char *suffix, enum file_groups group) +{ + struct file fd; + fd.type = type; + fd.suffix = suffix; + fd.group = group; + return fd; +} + +static struct icon* +media_icon_for_file(struct media *media, const char *file) +{ + int i = 0; + const char *s = file; + char suffix[4]; + int found = 0; + memset(suffix, 0, sizeof(suffix)); + + /* extract suffix .xxx from file */ + while (*s++ != '\0') { + if (found && i < 3) + suffix[i++] = *s; + + if (*s == '.') { + if (found){ + found = 0; + break; + } + found = 1; + } + } + + /* check for all file definition of all groups for fitting suffix*/ + for (i = 0; i < FILE_MAX && found; ++i) { + struct file *d = &media->files[i]; + { + const char *f = d->suffix; + s = suffix; + while (f && *f && *s && *s == *f) { + s++; f++; + } + + /* found correct file definition so */ + if (f && *s == '\0' && *f == '\0') + return media->group[d->group].icon; + } + } + return &media->icons.default_file; +} + +static void +media_init(struct media *media, NVGcontext *vg) +{ + /* load media */ + struct icons *icons = &media->icons; + + /* icons */ + icons->home = ICON(vg, "../icons/home.png"); + icons->directory = ICON(vg, "../icons/directory.png"); + icons->computer = ICON(vg, "../icons/computer.png"); + icons->desktop = ICON(vg, "../icons/desktop.png"); + icons->default_file = ICON(vg, "../icons/default.png"); + icons->text_file = ICON(vg, "../icons/text.png"); + icons->music_file = ICON(vg, "../icons/music.png"); + icons->font_file = ICON(vg, "../icons/font.png"); + icons->img_file = ICON(vg, "../icons/img.png"); + icons->movie_file = ICON(vg, "../icons/movie.png"); + + /* file groups */ + media->group[FILE_GROUP_DEFAULT] = FILE_GROUP(FILE_GROUP_DEFAULT,"default",&icons->default_file); + media->group[FILE_GROUP_TEXT] = FILE_GROUP(FILE_GROUP_TEXT, "textual", &icons->text_file); + media->group[FILE_GROUP_MUSIC] = FILE_GROUP(FILE_GROUP_MUSIC, "music", &icons->music_file); + media->group[FILE_GROUP_FONT] = FILE_GROUP(FILE_GROUP_FONT, "font", &icons->font_file); + media->group[FILE_GROUP_IMAGE] = FILE_GROUP(FILE_GROUP_IMAGE, "image", &icons->img_file); + media->group[FILE_GROUP_MOVIE] = FILE_GROUP(FILE_GROUP_MOVIE, "movie", &icons->movie_file); + + /* files */ + media->files[FILE_DEFAULT] = FILE_DEF(FILE_DEFAULT, NULL, FILE_GROUP_DEFAULT); + media->files[FILE_TEXT] = FILE_DEF(FILE_TEXT, "txt", FILE_GROUP_TEXT); + media->files[FILE_C_SOURCE] = FILE_DEF(FILE_C_SOURCE, "c", FILE_GROUP_TEXT); + media->files[FILE_CPP_SOURCE] = FILE_DEF(FILE_CPP_SOURCE, "cpp", FILE_GROUP_TEXT); + media->files[FILE_HEADER] = FILE_DEF(FILE_HEADER, "h", FILE_GROUP_TEXT); + media->files[FILE_MP3] = FILE_DEF(FILE_MP3, "mp3", FILE_GROUP_MUSIC); + media->files[FILE_WAV] = FILE_DEF(FILE_WAV, "wav", FILE_GROUP_MUSIC); + media->files[FILE_OGG] = FILE_DEF(FILE_OGG, "ogg", FILE_GROUP_MUSIC); + media->files[FILE_TTF] = FILE_DEF(FILE_TTF, "ttf", FILE_GROUP_FONT); + media->files[FILE_BMP] = FILE_DEF(FILE_BMP, "bmp", FILE_GROUP_IMAGE); + media->files[FILE_PNG] = FILE_DEF(FILE_PNG, "png", FILE_GROUP_IMAGE); + media->files[FILE_JPEG] = FILE_DEF(FILE_JPEG, "jpg", FILE_GROUP_IMAGE); + media->files[FILE_PCX] = FILE_DEF(FILE_PCX, "pcx", FILE_GROUP_IMAGE); + media->files[FILE_TGA] = FILE_DEF(FILE_TGA, "tga", FILE_GROUP_IMAGE); + media->files[FILE_GIF] = FILE_DEF(FILE_GIF, "gif", FILE_GROUP_IMAGE); +} +/* ================================================================= + * + * FILE EXPLORER + * + * ================================================================= */ +struct file_browser { + /* path */ + char file[MAX_PATH_LEN]; + char home[MAX_PATH_LEN]; + char desktop[MAX_PATH_LEN]; + char directory[MAX_PATH_LEN]; + + /* directory content */ + char **files; + char **directories; + size_t file_count; + size_t dir_count; + + /* gui */ + void *memory; + struct media media; + struct zr_input input; + struct zr_command_queue queue; + struct zr_style config; + struct zr_user_font font; + struct zr_tiled_layout tiled; + struct zr_window dir; + struct zr_window sel; + zr_float left, center; +}; + +static void +file_browser_reload_directory_content(struct file_browser *browser, const char *path) +{ + strncpy(browser->directory, path, MAX_PATH_LEN); + dir_free_list(browser->files, browser->file_count); + dir_free_list(browser->directories, browser->dir_count); + browser->files = dir_list(path, 0, &browser->file_count); + browser->directories = dir_list(path, 1, &browser->dir_count); +} + +static void +file_browser_init(struct file_browser *browser, NVGcontext *vg, + struct zr_user_font *font) +{ + memset(browser, 0, sizeof(*browser)); + media_init(&browser->media, vg); + { + /* gui */ + browser->font = *font; + browser->memory = calloc(1, MAX_COMMAND_MEMORY); + memset(&browser->input, 0, sizeof(browser->input)); + zr_command_queue_init_fixed(&browser->queue, browser->memory, MAX_COMMAND_MEMORY); + zr_style_default(&browser->config, ZR_DEFAULT_ALL, &browser->font); + zr_window_init(&browser->dir, zr_rect(0,0,0,0), 0, &browser->queue, &browser->config, &browser->input); + zr_window_init(&browser->sel, zr_rect(0,0,0,0), 0, &browser->queue, &browser->config, &browser->input); + browser->left = 0.25; browser->center = 0.75f; + } + { + /* load files and sub-directory list */ + const char *home = getenv("HOME"); + if (!home) home = getpwuid(getuid())->pw_dir; + { + size_t l; + strncpy(browser->home, home, MAX_PATH_LEN); + l = strlen(browser->home); + strcpy(browser->home + l, "/"); + strcpy(browser->directory, browser->home); + } + { + size_t l; + strcpy(browser->desktop, browser->home); + l = strlen(browser->desktop); + strcpy(browser->desktop + l, "desktop/"); + } + browser->files = dir_list(browser->directory, 0, &browser->file_count); + browser->directories = dir_list(browser->directory, 1, &browser->dir_count); + } +} + +static void +file_browser_free(struct file_browser *browser) +{ + if (browser->files) + dir_free_list(browser->files, browser->file_count); + if (browser->directories) + dir_free_list(browser->directories, browser->dir_count); + browser->files = NULL; + browser->directories = NULL; +} + +static int +file_browser_run(struct file_browser *browser, int width, int height) +{ + struct zr_context context; + struct media *media = &browser->media; + struct icons *icons = &media->icons; + + zr_tiled_begin(&browser->tiled,ZR_DYNAMIC,zr_rect(0,0,(zr_float)width,(zr_float)height),zr_vec2(0,0)); + zr_tiled_slot(&browser->tiled, ZR_SLOT_LEFT, browser->left, ZR_SLOT_VERTICAL, 1); + zr_tiled_slot(&browser->tiled, ZR_SLOT_CENTER, browser->center, ZR_SLOT_VERTICAL, 1); + zr_tiled_end(&browser->tiled); + + zr_begin_tiled(&context, &browser->sel, &browser->tiled, ZR_SLOT_LEFT, 0); + { + struct zr_image home = icons->home.img; + struct zr_image desktop = icons->desktop.img; + struct zr_image computer = icons->computer.img; + + zr_layout_row_dynamic(&context, 40, 1); + zr_style_push_property(&browser->config, ZR_PROPERTY_ITEM_SPACING, zr_vec2(0, 1)); + if (zr_button_text_image(&context, home, "home", ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT)) + file_browser_reload_directory_content(browser, browser->home); + if (zr_button_text_image(&context,desktop,"desktop",ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT)) + file_browser_reload_directory_content(browser, browser->desktop); + if (zr_button_text_image(&context,computer,"computer",ZR_TEXT_CENTERED,ZR_BUTTON_DEFAULT)) + file_browser_reload_directory_content(browser, "/"); + zr_style_pop_property(&browser->config); + } + zr_end(&context, &browser->sel); + + zr_begin_tiled(&context, &browser->dir, &browser->tiled, ZR_SLOT_CENTER, 0); + { + { + /* output path directory selector */ + char *d = browser->directory; + char *begin = d + 1; + zr_style_push_property(&browser->config, ZR_PROPERTY_ITEM_SPACING, zr_vec2(0, 0)); + zr_layout_row_dynamic(&context, 25, 6); + while (*d++) { + if (*d == '/') { + *d = '\0'; + if (zr_button_text(&context, begin, ZR_BUTTON_DEFAULT)) { + *d++ = '/'; *d = '\0'; + file_browser_reload_directory_content(browser, browser->directory); + break; + } + *d = '/'; + begin = d + 1; + } + } + zr_style_pop_property(&browser->config); + } + { + /* output directory content */ + int index = -1; + size_t i = 0, j = 0, k = 0; + size_t rows = 0, cols = 0; + size_t count = browser->dir_count + browser->file_count; + + cols = 4; + rows = count / cols; + for (i = 0; i <= rows; i += 1) { + { + /* draw one row of icons */ + size_t n = j + cols; + zr_layout_row_dynamic(&context, 140, cols); + zr_style_push_property(&browser->config, ZR_PROPERTY_ITEM_SPACING, zr_vec2(0, 0)); + zr_style_push_color(&browser->config, ZR_COLOR_BUTTON, zr_rgb(45, 45, 45)); + zr_style_push_color(&browser->config, ZR_COLOR_BORDER, zr_rgb(45, 45, 45)); + for (; j < count && j < n; ++j) { + if (j < browser->dir_count) { + /* draw and execute directory buttons */ + if (zr_button_image(&context,icons->directory.img,ZR_BUTTON_DEFAULT)) + index = (int)j; + } else { + /* draw and execute files buttons */ + struct icon *icon; + size_t fileIndex = ((size_t)j - browser->dir_count); + icon = media_icon_for_file(media,browser->files[fileIndex]); + if (zr_button_image(&context, icon->img, ZR_BUTTON_DEFAULT)) { + strncpy(browser->file, browser->directory, MAX_PATH_LEN); + n = strlen(browser->file); + strncpy(browser->file + n, browser->files[fileIndex], MAX_PATH_LEN - n); + return 0; + } + } + } + zr_style_pop_color(&browser->config); + zr_style_pop_color(&browser->config); + zr_style_pop_property(&browser->config); + } + { + /* draw one row of labels */ + size_t n = k + cols; + zr_layout_row_dynamic(&context, 20, cols); + zr_style_push_property(&browser->config, ZR_PROPERTY_ITEM_SPACING, zr_vec2(0, 0)); + for (; k < count && k < n; k++) { + if (k < browser->dir_count) { + zr_label(&context, browser->directories[k], ZR_TEXT_CENTERED); + } else { + size_t t = k-browser->dir_count; + zr_label(&context,browser->files[t],ZR_TEXT_CENTERED); + } + } + zr_style_pop_property(&browser->config); + } + } + + if (index != -1) { + size_t n = strlen(browser->directory); + strncpy(browser->directory + n, browser->directories[index], MAX_PATH_LEN - n); + n = strlen(browser->directory); + if (n < MAX_PATH_LEN - 1) { + browser->directory[n] = '/'; + browser->directory[n+1] = '\0'; + } + file_browser_reload_directory_content(browser, browser->directory); + } + } + } + zr_end(&context, &browser->dir); + return 1; +} +/* ================================================================= + * + * APP + * + * ================================================================= */ +static zr_size +font_get_width(zr_handle handle, const zr_char *text, zr_size len) +{ + zr_size width; + float bounds[4]; + NVGcontext *ctx = (NVGcontext*)handle.ptr; + nvgTextBounds(ctx, 0, 0, text, &text[len], bounds); + width = (zr_size)(bounds[2] - bounds[0]); + return width; +} + +static void +draw(NVGcontext *nvg, struct zr_command_queue *queue, int width, int height) +{ + const struct zr_command *cmd; + glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_TEXTURE_2D); + + nvgBeginFrame(nvg, width, height, ((float)width/(float)height)); + zr_foreach_command(cmd, queue) { + switch (cmd->type) { + case ZR_COMMAND_NOP: break; + case ZR_COMMAND_SCISSOR: { + const struct zr_command_scissor *s = zr_command(scissor, cmd); + nvgScissor(nvg, s->x, s->y, s->w, s->h); + } break; + case ZR_COMMAND_LINE: { + const struct zr_command_line *l = zr_command(line, cmd); + nvgBeginPath(nvg); + nvgMoveTo(nvg, l->begin.x, l->begin.y); + nvgLineTo(nvg, l->end.x, l->end.y); + nvgFillColor(nvg, nvgRGBA(l->color.r, l->color.g, l->color.b, l->color.a)); + nvgFill(nvg); + } break; + case ZR_COMMAND_CURVE: { + const struct zr_command_curve *q = zr_command(curve, cmd); + nvgBeginPath(nvg); + nvgMoveTo(nvg, q->begin.x, q->begin.y); + nvgBezierTo(nvg, q->ctrl[0].x, q->ctrl[0].y, q->ctrl[1].x, + q->ctrl[1].y, q->end.x, q->end.y); + nvgStrokeColor(nvg, nvgRGBA(q->color.r, q->color.g, q->color.b, q->color.a)); + nvgStrokeWidth(nvg, 3); + nvgStroke(nvg); + } break; + case ZR_COMMAND_RECT: { + const struct zr_command_rect *r = zr_command(rect, cmd); + nvgBeginPath(nvg); + nvgRoundedRect(nvg, r->x, r->y, r->w, r->h, r->rounding); + nvgFillColor(nvg, nvgRGBA(r->color.r, r->color.g, r->color.b, r->color.a)); + nvgFill(nvg); + } break; + case ZR_COMMAND_CIRCLE: { + const struct zr_command_circle *c = zr_command(circle, cmd); + nvgBeginPath(nvg); + nvgCircle(nvg, c->x + (c->w/2.0f), c->y + c->w/2.0f, c->w/2.0f); + nvgFillColor(nvg, nvgRGBA(c->color.r, c->color.g, c->color.b, c->color.a)); + nvgFill(nvg); + } break; + case ZR_COMMAND_TRIANGLE: { + const struct zr_command_triangle *t = zr_command(triangle, cmd); + nvgBeginPath(nvg); + nvgMoveTo(nvg, t->a.x, t->a.y); + nvgLineTo(nvg, t->b.x, t->b.y); + nvgLineTo(nvg, t->c.x, t->c.y); + nvgLineTo(nvg, t->a.x, t->a.y); + nvgFillColor(nvg, nvgRGBA(t->color.r, t->color.g, t->color.b, t->color.a)); + nvgFill(nvg); + } break; + case ZR_COMMAND_TEXT: { + const struct zr_command_text *t = zr_command(text, cmd); + nvgBeginPath(nvg); + nvgRoundedRect(nvg, t->x, t->y, t->w, t->h, 0); + nvgFillColor(nvg, nvgRGBA(t->background.r, t->background.g, + t->background.b, t->background.a)); + nvgFill(nvg); + + nvgBeginPath(nvg); + nvgFillColor(nvg, nvgRGBA(t->foreground.r, t->foreground.g, + t->foreground.b, t->foreground.a)); + nvgTextAlign(nvg, NVG_ALIGN_MIDDLE); + nvgText(nvg, t->x, t->y + t->h * 0.5f, t->string, &t->string[t->length]); + nvgFill(nvg); + } break; + case ZR_COMMAND_IMAGE: { + int w, h; + NVGpaint imgpaint; + const struct zr_command_image *i = zr_command(image, cmd); + nvgImageSize(nvg, i->img.handle.id, &w, &h); + nvgBeginPath(nvg); + nvgRect(nvg, i->x, i->y, i->w, i->h); + nvgFillPaint(nvg, nvgImagePattern(nvg, + i->x, i->y, i->w, i->h, 0, i->img.handle.id, 1)); + nvgFill(nvg); + + } break; + case ZR_COMMAND_ARC: + default: break; + } + } + zr_command_queue_clear(queue); + + nvgResetScissor(nvg); + nvgEndFrame(nvg); + glPopAttrib(); +} + +static void +key(struct zr_input *in, SDL_Event *evt, zr_bool down) +{ + const Uint8* state = SDL_GetKeyboardState(NULL); + SDL_Keycode sym = evt->key.keysym.sym; + if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT) + zr_input_key(in, ZR_KEY_SHIFT, down); + else if (sym == SDLK_DELETE) + zr_input_key(in, ZR_KEY_DEL, down); + else if (sym == SDLK_RETURN) + zr_input_key(in, ZR_KEY_ENTER, down); + else if (sym == SDLK_BACKSPACE) + zr_input_key(in, ZR_KEY_BACKSPACE, down); + else if (sym == SDLK_LEFT) + zr_input_key(in, ZR_KEY_LEFT, down); + else if (sym == SDLK_RIGHT) + zr_input_key(in, ZR_KEY_RIGHT, down); + else if (sym == SDLK_c) + zr_input_key(in, ZR_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_v) + zr_input_key(in, ZR_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_x) + zr_input_key(in, ZR_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); +} + +static void +motion(struct zr_input *in, SDL_Event *evt) +{ + const zr_int x = evt->motion.x; + const zr_int y = evt->motion.y; + zr_input_motion(in, x, y); +} + +static void +btn(struct zr_input *in, SDL_Event *evt, zr_bool down) +{ + const zr_int x = evt->button.x; + const zr_int y = evt->button.y; + if (evt->button.button == SDL_BUTTON_LEFT) + zr_input_button(in, ZR_BUTTON_LEFT, x, y, down); + else if (evt->button.button == SDL_BUTTON_RIGHT) + zr_input_button(in, ZR_BUTTON_RIGHT, x, y, down); + else if (evt->button.button == SDL_BUTTON_MIDDLE) + zr_input_button(in, ZR_BUTTON_MIDDLE, x, y, down); +} + +static void +text(struct zr_input *in, SDL_Event *evt) +{ + zr_glyph glyph; + memcpy(glyph, evt->text.text, ZR_UTF_SIZE); + zr_input_glyph(in, glyph); +} + +static void +resize(SDL_Event *evt) +{ + if (evt->window.event != SDL_WINDOWEVENT_RESIZED) return; + glViewport(0, 0, evt->window.data1, evt->window.data2); +} + +int +main(int argc, char *argv[]) +{ + int x,y,width, height; + SDL_Window *win; + SDL_GLContext glContext; + NVGcontext *vg = NULL; + + int running = 1; + unsigned int started; + unsigned int dt; + struct zr_user_font font; + struct file_browser browser; + const char *font_path; + int icon_sheet; + + font_path = argv[1]; + if (argc < 2) + die("missing argument!: "); + + /* SDL */ + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + win = SDL_CreateWindow("File Explorer", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN); + glContext = SDL_GL_CreateContext(win); + SDL_GetWindowSize(win, &width, &height); + SDL_GetWindowPosition(win, &x, &y); + + /* OpenGL */ + glewExperimental = 1; + if (glewInit() != GLEW_OK) + die("[GLEW] failed setup\n"); + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + + /* nanovg */ + vg = nvgCreateGLES2(NVG_ANTIALIAS|NVG_DEBUG); + if (!vg) die("[NVG]: failed to init\n"); + nvgCreateFont(vg, "fixed", font_path); + nvgFontFace(vg, "fixed"); + nvgFontSize(vg, 10); + nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); + + /* GUI */ + memset(&browser, 0, sizeof browser); + font.userdata.ptr = vg; + nvgTextMetrics(vg, NULL, NULL, &font.height); + font.width = font_get_width; + file_browser_init(&browser, vg, &font); + + while (running) { + /* Input */ + SDL_Event evt; + started = SDL_GetTicks(); + zr_input_begin(&browser.input); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_WINDOWEVENT) resize(&evt); + else if (evt.type == SDL_QUIT) goto cleanup; + else if (evt.type == SDL_KEYUP) key(&browser.input, &evt, zr_false); + else if (evt.type == SDL_KEYDOWN) key(&browser.input, &evt, zr_true); + else if (evt.type == SDL_MOUSEBUTTONDOWN) btn(&browser.input, &evt, zr_true); + else if (evt.type == SDL_MOUSEBUTTONUP) btn(&browser.input, &evt, zr_false); + else if (evt.type == SDL_MOUSEMOTION) motion(&browser.input, &evt); + else if (evt.type == SDL_TEXTINPUT) text(&browser.input, &evt); + else if (evt.type == SDL_MOUSEWHEEL) zr_input_scroll(&browser.input, evt.wheel.y); + } + zr_input_end(&browser.input); + + SDL_GetWindowSize(win, &width, &height); + running = file_browser_run(&browser, width, height); + + /* Draw */ + glClearColor(0.4f, 0.4f, 0.4f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + draw(vg, &browser.queue, width, height); + SDL_GL_SwapWindow(win); + } + +cleanup: + /* Cleanup */ + free(browser.memory); + nvgDeleteGLES2(vg); + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(win); + SDL_Quit(); + return 0; +} + diff --git a/example/filex/icons/computer.png b/example/filex/icons/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..29db8fc867c57f556e011f6a830324310c1a1fad GIT binary patch literal 620 zcmV-y0+aoTP)lM$RWxRI;iK67_tSYjv0h5g$~6*A!7?23Z;}n$JV8^ zrF3verw%$eNCZKvg09*Q2}C1=J5Y;g?&#^SQ-UcH3Zb3yo!)!!K0Mw(xY7TR2E?ul zaRUz}Nos1ECZf@(kR<6Fa3648l_>q;hk&#!%c(>naeH@n7q8cgZQJyEJqm>awOVap zSymSK2#l@*K20W*uY5kAL(??;em|yZGMmj1Lf~?_5JE5>kEv8Dl*?svI2?Wk-U2^X z0l#-ToxAh-9K$d$O_S+#dI5Agod_YYZ5x2aVnM6bqEsr0!^6Ypz)Jwfa=Dgek;~;U z41+)*fTAd@t*znlcmOV701k(Pt*tHg_Vy&;!FhbS!M`laA|8)(baX_k)na2~gK#*E zVHgYsgG;??Hk-8DZFY8cF2$Wo_Gz(Lyr-(FdwY8ux7*F}@iC1?16h`d#bTVDol&V& z$mjEnMk7L@5WN3D`vs@KyH!)Z1w2tz^>HSXaYrH%`u#q-t}`4Csn_cOD2jrjD0I7B zs@3Yx$z<{f___*st~-8v?%a*R|Z-Cdp_-_|H z1mre1H|N=GR;1Hu5ex?Bz$ZYt&gDdbFTg$!zWGM~R=)umr_Iz|nP&C?0000=#!qTME2*;ezt)}4|4rfN`3gg|C*-h4-pw60?}XBdV4yZHc65^H_pJ! zpY!?r+Zbck>$S1N>tO*$qtV-HwergE;cy5k zT?nVQejifGGw4=3S5Z(D1(Z_G0<`Vj z`IOWRi=uE^RqdK!7r@f6$*Rq)(GWo6o-y;)tLNWiA0000z!p|$|%p4arm)sPW4^T8?J|JZb^5+P{eFEt)Y&XzEjWib2lR*>*~B{J(Bh24{P{~| zaWP5a^=8w)&%x$q;?$l-k13Q7+5?pV1jK?f;0|Ftx|FKg>s7?sG$Hy(lm_L&0A5k{Y9i&ezLa4#_B4!OC?_I?C|)(U9QhuM65y->@F|y z_4`j=zTV>7kKf!|SYY<-)bMdfQM6a9)kG;J|MQO|#bQySC~6I@urha(hxaECN%xn@ zfYvZynFHWW^Bp2!ElhNPR2<8p)|yY*EMdD%v)LT! z?N+PR;f^2(wi=B_$66~VLA_q@?5^ete0UFtg%xx7Cj_5?Pj$B#oXxZC|oJ85(3+Mv^qB z*MHJ%Wq9xL-tayzw2^y{_nsj)c=*)hu}BhO5d2trw>GgIkRro0|HOrvE5luCXj_z{DB(zXjMbHAeaFNNq{ys$Q(6V(DEb9J8gJ}+u)|Kk zEYC@jlqin({8KsrDQegtj*~qDZc#QVIB>Ac$e}S#OqMar{|`{o%ZcSNibRasZe*e)Oqx!_x$Tn$1SFwDX-&%Eb7XN0nx!k!fNT3xAtIlwhQ0}6Yl6y9k2d>T0z@MA8M6_9RJIE|*dT*MP5-ipS%zF~*oM49#Y!P(Txl$RR?)7?& z`Fx)KY`5DvfQ8s^*LBm2!+k`*gcq8jVKDSr+^^U;_LS5&Hc;gTVl; zHE%zEsrd1bqp&92H>vCCu~W}8q1zCQbM_|vmDXG^oX zeex85TQ{$B|K6R0-*O7HnOS{3dj`P#^t{%gDmoOAN{3c;?kY1>O659wS(P$WN}-g( zbzL0Cxim1>|4yf4tgE`w$ZEISu~;mG|3<$7k0ygyH7*Qe00000NkvXXu0mjfN^t?l literal 0 HcmV?d00001 diff --git a/example/filex/icons/home.png b/example/filex/icons/home.png new file mode 100644 index 0000000000000000000000000000000000000000..85606267225863537d186d63ebb2ed7014c9e4ae GIT binary patch literal 819 zcmV-31I+x1P)DW` zK~y-))s)Rk8+jPVKflb3^Frfhou&!xCQPQuB1mvSij7Ed!HanI5;P|d{ssOMUiRe4 z9`>?(2v)qgu$hZ^$jNQNlp^#cjOY>rotVTO>*TkGWkW%?EqmI1ZVx=qhv)SR|8)~z z5EC08ACE3CFHu#MSSy6(p zW?2@BqTE{H5a@XxJKNht-7epcKV)kD2^T@YZ;dxJn@tuM7ZF0BCk zcso1HJR&h5HZE^m{$NYW3;$dg!{2 zuIs3(O1s@=VqyYSRdHSSx#Ku^o+oYqGuZ-<9)AbG-Me?_cKiHs;j>?_p=lb2hliY; zoM78FD=RAi1VO<1`ufeYky3ImnLwqDN}F!r6NTrD*>})&o&EiN`u#rTa+xp;sn_dl zZf@fH{_U@qN~I*g=;$bhVQ_waPCA`N2*KRk9Hml;g@px7)1+7|GB!3wCX*rl)6e{9 z=!9V?Zu{lY+1c6P^z;@B7qhH423S&1MtN^T_3L)a&)Hf%jjkHo06b_u95?DwPV2MuWY* xJv2?@`1m*j`2SYsikVDCX0urdOz^+9KLMDz1_tX?-dO+u002ovPDHLkV1nUi5sZRGrF2`gX~#oqZIdO5p8Ue_nBnoA@63DeE9i&piJhJ} z5YlHafn$IL=)e}RIV!2}AvWT-PNUjbfT+pQ2*1=&Miy5#UoMlac9UlBX*x0Gyec;(9)h9S#u= zS^KKM(haWNxl5^3qTOx_zXDj3lan%+%TcLR4sJXPuy0T@zZ2Lgo6U{@KSb~RAL6(WSdwr%WCi1_$8N-3nSV;GXX z9T2->{|{krceuiXrhTxy%p>_9|hmOcMGwxu{|2$Z&!Kv=r;d~!}f*aILgma i2BFjG9I>zeAHM*|L6h|(d1^HP0000mI)V literal 0 HcmV?d00001 diff --git a/example/filex/icons/movie.png b/example/filex/icons/movie.png new file mode 100644 index 0000000000000000000000000000000000000000..5227883e6c4d28773043acce6ededc1fe34ded22 GIT binary patch literal 626 zcmV-&0*(ENP)T{#D-;S&%1S2+M3;h&CNB|?Xn2J@MjBouU8Fn!Jt9)lf)J5N z?utl}k|tUMo6Xu+Tv+j+hrS}K(a z#u$plBA(}=l%m`1(r7feyu9S}^mGGIe)<@gODU<wdVf*o?frV$;k=h@i^_p z^*%gwQ&PauXoT-rtCIF5HdU;n550eUjt_y-pF9RL6T M07*qoM6N<$f_*?A&j0`b literal 0 HcmV?d00001 diff --git a/example/filex/icons/music.png b/example/filex/icons/music.png new file mode 100644 index 0000000000000000000000000000000000000000..0f1415c8756311c4852151f1a0f4055e08b26b97 GIT binary patch literal 610 zcmV-o0-gPdP)I`orN2EPQ-(&Md=rc&3|7-y16j#sR2} zVjD0z1Y|idTPZd6Jg*!C0T^RR0odwM6q(6nV&%aL04{URr@dZ}h=_dOr^RAHT5DRZ zR;0D2<#L%S&+{k?iR~nSlWw=`4u?YkK)>Hdr_+J!x+s-OP)b2c2`MFn5Rg*lTE)OZ zqtUoJ8jX^V<#IU%5JF)1=4pCnjO79W0Q31AzVBl&7{GO1)a!NFww*G2^!Q;4aEVoO zfd)Xx7(=VoLc86DloG{a5su@)w(SH60B0Y9#JX0i?KJ!y>&I7BN~H{i5CD*zv)9cU z{yi82$8nOOXBPorCoF4d7-Jx!1dv@w4BQ3VT1srFlu8UgoqWXc``0-AJjT~=X9s`1 zLf#XIZC<>6h99#jlvJ4h{EpKvV|Z7tT^txEwtIQ}9KV8daK|W zK~y-))s(%;;y@IJ-x-}5R{Vij7(}d0Bgk4g^9{M~N1e=Zga0synw49#W}hG8I|&qD|S&N(>eV2put{~5KOeT|;&qjq`D;A4z z90x)OWHK2DA>N`(5&{5%AV4mc!(cGLdcD3Q3(u8G1>^DfC7~vSKx+-oIr{zn-9*Fu zi0s*>AP52oA>cR;R;yKv?CS%6hcLR#7{hQlyrZL({_`$JhP_@7p6A8Lo>4tjsZ>(? z{a$Z28+|w&w3JdSrSvP6%Vm|^-}pjP8kP90Oxw0$+xGW_rfI71XW;w3en&~dSPy;Q nm$h2$$JYUUr`zo&@n7i!nhp9}Dig&U00000NkvXXu0mjfhOhtA literal 0 HcmV?d00001