Grab the ui framework from FWK. (Nuklear).

This commit is contained in:
Relintai 2024-09-13 09:14:42 +02:00
parent 0358ef62de
commit 8e23bf198c
6 changed files with 35606 additions and 0 deletions

30372
sfw/render_gui/3rd_nuklear.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,483 @@
// file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain)
// - rlyeh, public domain
//
// changelog:
// - ported to FWK api
// - namespaced symbols
// - diverse win32 fixes
// - adaptive cols/rows
// - removed nk_begin()/nk_end() pairs
// - dangling nk_group_begin/end() pairs
// - simplified file<->media_group concept
// - minor cosmetics
#ifdef _WIN32
#include <direct.h> // _getcwd()
#else
#include <unistd.h> // getcwd()
#include <pwd.h> // getpwuid()
#define _popen popen
#define _pclose pclose
#endif
const char** old_file_list(const char *cwd, const char *masks) {
ASSERT(strend(cwd, "/"), "Error: dirs like '%s' must end with slash", cwd);
static __thread array(char*) list = 0;
const char *arg0 = cwd; // app_path();
int larg0 = strlen(arg0);
for( int i = 0; i < array_count(list); ++i ) {
FREE(list[i]);
}
array_resize(list, 0);//array_free(list);
for each_substring(masks,";",it) {
int recurse = !!strstr(it, "**");
#if is(win32)
char *glob = va("dir %s/b/o:n \"%s\\%s\" 2> NUL", recurse ? "/s":"", cwd, it);
#else // linux, osx
char *glob = va("find %s %s -name \"%s\" | sort", cwd, !recurse ? "-maxdepth 1":"-type f", it);
#endif
for( FILE *in = _popen(glob, "r"); in; _pclose(in), in = 0) {
char buf[1024], *line = buf;
while( fgets(buf, sizeof(buf), in) ) {
// clean up
if( strstr(line, arg0) ) line = buf + larg0;
if( !memcmp(line, "./", 2) ) line += 2;
int len = strlen(line); while( len > 0 && line[len-1] < 32 ) line[--len] = 0;
if( line[0] == '\0' ) continue;
// do not insert system folders/files
for(int i = 0; i < len; ++i ) if(line[i] == '\\') line[i] = '/';
if( line[0] == '.' ) if( !strcmp(line,".git") || !strcmp(line,".vs") || !strcmp(line,".") || !strcmp(line,"..") ) continue;
if( strstr(line, "/.") ) continue;
// insert copy
#if is(win32)
char *copy = STRDUP(line); // full path already provided
#else
// while(line[0] == '/') ++line;
char *copy = STRDUP(va("%s%s", cwd, line)); // need to prepend path
#endif
array_push(list, copy);
}
}
}
array_push(list, 0); // terminator
return (const char**)list;
}
#if 1
#define BROWSER_PRINTF(...) do {} while(0)
#else
#define BROWSER_PRINTF printf
#endif
enum browser_groups {
BROWSER_FOLDER,
BROWSER_HOME,
BROWSER_DESKTOP,
BROWSER_COMPUTER,
BROWSER_PROJECT,
BROWSER_MAXFOLDERS,
BROWSER_MAXTYPES = 64,
};
struct browser_media_group {
unsigned icon;
const char *extensions;
};
struct browser_media {
int font;
int icon_sheet;
struct nk_image custom_folders[BROWSER_MAXFOLDERS];
struct nk_image custom_files[BROWSER_MAXTYPES];
struct browser_media_group group[BROWSER_MAXTYPES];
} media = {0};
void browser_config_dir(struct nk_image icon, unsigned counter) {
if( counter < BROWSER_MAXFOLDERS ) {
media.custom_folders[ counter ] = icon;
}
}
void browser_config_type(struct nk_image icon, const char *extensions) {
static int counter = 0;
if( counter < BROWSER_MAXTYPES ) {
media.custom_files[ counter ] = icon;
media.group[ counter ].icon = counter;
media.group[ counter ].extensions = extensions;
++counter;
}
}
#define BROWSER_MAX_PATH 512
struct browser {
/* path */
char file[BROWSER_MAX_PATH]; // selection
char directory[BROWSER_MAX_PATH]; // current cwd while browsing
char home[BROWSER_MAX_PATH];
char desktop[BROWSER_MAX_PATH];
char computer[BROWSER_MAX_PATH];
char project[BROWSER_MAX_PATH]; // cwd when first invoked
/* directory content */
array(char*) files;
array(char*) directories;
size_t file_count;
size_t dir_count;
/* filtered directory content */
array(char*) ffiles;
array(char*) fdirectories;
/* view mode */
bool listing;
float zooming;
};
static struct nk_image* media_icon_for_file(const char *file) {
/* extract extension .xxx from file */
char *ext = strrchr(file, '.');
if( ext && strlen(ext) < 16 ) {
char ext_dot[16+1];
snprintf(ext_dot, 16, "%s.", ext);
/* check for all file definition of all groups for fitting extension. skip first group (default) */
for (int i = 1; i < BROWSER_MAXTYPES && media.group[i].extensions; ++i) {
if( strstri(media.group[i].extensions, ext_dot) ) {
return &media.custom_files[ media.group[i].icon ];
}
}
}
// return first (default) group
return &media.custom_files[0];
}
static void browser_reload_directory_content(struct browser *browser, const char *path) {
if(path[0] == '\0') path = va("./");
if(!strend(path, "/")) path = va("%s/", path);
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_resize(browser->files, 0);
array_resize(browser->directories, 0);
BROWSER_PRINTF("searching at %s\n", path);
const char** list = old_file_list(path, "*");
for( int i = 0; list[i]; ++i ) {
char *absolute = file_pathabs(ifndef(win32, list[i], va("%s/%s", path, list[i]))); // ../dir/./file.ext -> c:/prj/dir/file.ext
BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) );
if( file_directory(absolute) ) {
// remove last '/' if present. ok to overwrite absolute var, file_*() API returns writeable strings.
char *dir = absolute; if( dir[ strlen(dir) - 1 ] == '/' ) dir[ strlen(dir) - 1 ] = '\0';
dir = file_name(dir); // /home/rlyeh/prj/fwk/art -> art
BROWSER_PRINTF("%s\n", dir);
if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->directories, STRDUP(dir));
} else {
const char *fname = file_name(absolute);
BROWSER_PRINTF("%s\n", fname);
if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip
array_push(browser->files, STRDUP(fname));
}
}
browser->file_count = array_count(browser->files);
browser->dir_count = array_count(browser->directories);
}
static void browser_chdir_and_reload_directory_content(struct browser *browser, const char *path) {
if( path != browser->directory ) strncpy(browser->directory, path, BROWSER_MAX_PATH);
browser_reload_directory_content(browser, path);
}
static void browser_init(struct browser *browser) {
memset(browser, 0, sizeof(*browser));
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
#endif
snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home);
snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home);
snprintf(browser->computer, BROWSER_MAX_PATH, "%s", ifdef(win32, va("%.*s", 3, getenv("windir")), "/"));
{
ifdef(win32, _getcwd, getcwd)(browser->project, sizeof(browser->project) - 1); // -1 == room for '/'
strcat(browser->project, "/");
}
BROWSER_PRINTF("%s\n", browser->home);
BROWSER_PRINTF("%s\n", browser->desktop);
BROWSER_PRINTF("%s\n", browser->computer);
BROWSER_PRINTF("%s\n", browser->project);
browser_chdir_and_reload_directory_content(browser, browser->project);
}
}
static void browser_free(struct browser *browser) {
for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]);
for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]);
array_free(browser->files);
array_free(browser->directories);
array_free(browser->ffiles);
array_free(browser->fdirectories);
memset(browser, 0, sizeof(*browser));
}
static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) {
int clicked = 0;
static float ratio[] = {0.25f, NK_UNDEFINED};
float spacing_x = ctx->style.window.spacing.x;
/* output path directory selector in the menubar */
ctx->style.window.spacing.x = 0;
if( windowed ) nk_menubar_begin(ctx);
{
char *d = browser->directory;
#ifdef _WIN32
char *begin = d;
#else
char *begin = d + 1;
#endif
nk_layout_row_template_begin(ctx, 25);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_push_variable(ctx, 40);
nk_layout_row_template_end(ctx);
if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) {
browser->listing ^= 1;
}
while (*d++) {
if (*d == '/') {
*d = '\0';
if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) {
*d++ = '/'; *d = '\0';
browser_chdir_and_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
}
if( windowed ) nk_menubar_end(ctx);
ctx->style.window.spacing.x = spacing_x;
if(nk_window_has_focus(ctx)) {
browser->zooming = clampf( browser->zooming + (input(KEY_LCTRL) || input(KEY_RCTRL)) * input_diff(MOUSE_W) * 0.1, 1, 3);
}
bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons
size_t cols = total_space.w / (100 * browser->zooming);
int icon_height = (67 * browser->zooming) * (tiny ? 0.33 : 1.); // icon height (96) + button padding (??). originally: 135
/**/ if( tiny ) cols = (int)cols+1.5, cols /= 2, compact = total_space.w < 500; // cols <= 2;
else cols = (int)cols+1, compact = total_space.w < 500; // cols <= 5;
if( cols < 1 ) cols=1;
/* window layout */
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio);
if( !compact )
if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) {
nk_layout_row_dynamic(ctx, 40, 1);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->home);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->desktop);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->computer);
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT))
browser_chdir_and_reload_directory_content(browser, browser->project);
nk_group_end(ctx);
}
/* output directory content window */
if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) {
array(char*) *directories = &browser->directories;
array(char*) *files = &browser->files;
if( ui_filter && ui_filter[0] ) {
array_resize(browser->fdirectories, 0);
array_resize(browser->ffiles, 0);
for each_array(browser->directories,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->fdirectories, k);
for each_array(browser->files,char*,k)
if( strstri(k, ui_filter) )
array_push(browser->ffiles, k);
directories = &browser->fdirectories;
files = &browser->ffiles;
}
int dir_count = array_count(*directories);
int file_count = array_count(*files);
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0;
size_t count = dir_count + file_count;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
if(!tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER]))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image(ctx, *icon)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
}}
if(!tiny)
{size_t n = k + cols;
nk_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
size_t t = k-dir_count;
/* draw one row of labels */
if (k < dir_count) {
nk_label(ctx, (*directories)[k], NK_TEXT_CENTERED);
} else {
nk_label(ctx, (*files)[t], NK_TEXT_CENTERED);
}
}}
if(tiny)
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, icon_height, (int)cols);
for (; j < count && j < n; ++j) {
size_t t = j-dir_count;
/* draw one row of icons */
if (j < dir_count) {
/* draw and execute directory buttons */
if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], (*directories)[j], NK_TEXT_RIGHT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - dir_count);
icon = media_icon_for_file((*files)[fileIndex]);
if (nk_button_image_label(ctx, *icon, (*files)[t],NK_TEXT_RIGHT)) {
snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]);
clicked = 1;
}
}
#if 0
bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop
bool has_popups = ui_popups(); // @fixme: move out of loop
if( !has_popups && has_focus ) {
struct nk_rect bounds = nk_widget_bounds(ctx);
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) {
char *name = j < dir_count ? (*directories)[j] : (*files)[j-dir_count];
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name);
struct stat t = {0};
if( stat( fullpath, &t ) != -1 ) {
char tooltip[256];
snprintf(tooltip, 256,
"Path: %s\n"
"Type: %lld\n" // file type and mode
"Size: %lld\n" // file size
"Owner: %lld\n" // user ID of file owner
"Modified: %s (%lld)", // last modification date
name, (int64_t)t.st_mode, (int64_t)t.st_size, (int64_t)t.st_uid, ctime(&t.st_mtime), (int64_t)t.st_mtime
);
nk_tooltip(ctx, tooltip);
}
}
}
#endif
}}
}
if (index != -1) {
BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]);
size_t n = strlen(browser->directory);
snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]);
BROWSER_PRINTF("%s\n", browser->directory);
browser_chdir_and_reload_directory_content(browser, browser->directory);
}
nk_group_end(ctx);
}
return clicked;
}
static struct nk_image icon_load(const char *filename) {
texture_t t = texture(filename, 0);
return nk_image_id((int)t.id);
}
static struct nk_image icon_load_rect(unsigned id, unsigned w, unsigned h, unsigned wcell, unsigned hcell, unsigned col, unsigned row) {
return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell });
}
/* demo:
struct browser browser = {0};
browser_init(&browser);
browser_config_dir(nk_image, BROWSER_HOME);
browser_config_dir(nk_image, BROWSER_PROJECT);
// [...]
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
browser_config_type(nk_image, ".ext1.ext2.ext3.");
// [...]
[...]
if( nk_begin(ctx, "window", ...) ) {
struct nk_rect total_space = nk_window_get_content_region(ctx);
if( browser_run(ctx, &browser, 0, total_space) ) {
puts( browser->directory );
puts( browser->file );
}
}
nk_end();
*/

View File

@ -0,0 +1,552 @@
/*
* Nuklear - 1.32.0 - public domain
* no warrenty implied; use at your own risk.
* authored from 2015-2016 by Micha Mettke
*/
/*
* ==============================================================
*
* API
*
* ===============================================================
*/
#ifndef NK_GLFW_GL3_H_
#define NK_GLFW_GL3_H_
//#include <GLFW/glfw3.h>
enum nk_glfw_init_state{
NK_GLFW3_DEFAULT=0,
NK_GLFW3_INSTALL_CALLBACKS
};
#ifndef NK_GLFW_TEXT_MAX
#define NK_GLFW_TEXT_MAX 256
#endif
struct nk_glfw_device {
struct nk_buffer cmds;
struct nk_draw_null_texture null;
GLuint vbo, vao, ebo;
GLuint prog;
GLuint vert_shdr;
GLuint frag_shdr;
GLint attrib_pos;
GLint attrib_uv;
GLint attrib_col;
GLint uniform_tex;
GLint uniform_proj;
GLuint font_tex;
};
struct nk_glfw {
GLFWwindow *win;
int width, height;
int display_width, display_height;
struct nk_glfw_device ogl;
struct nk_context ctx;
struct nk_font_atlas atlas;
struct nk_vec2 fb_scale;
unsigned int text[NK_GLFW_TEXT_MAX];
int text_len;
struct nk_vec2 scroll, scroll_bak; //< @r-lyeh, added scroll_bak
double last_button_click;
int is_double_click_down;
struct nk_vec2 double_click_pos;
};
NK_API struct nk_context* nk_glfw3_init(struct nk_glfw* glfw, GLFWwindow *win, enum nk_glfw_init_state);
NK_API void nk_glfw3_shutdown(struct nk_glfw* glfw);
NK_API void nk_glfw3_font_stash_begin(struct nk_glfw* glfw, struct nk_font_atlas **atlas);
NK_API void nk_glfw3_font_stash_end(struct nk_glfw* glfw);
NK_API void nk_glfw3_new_frame(struct nk_glfw* glfw);
NK_API void nk_glfw3_render(struct nk_glfw* glfw, enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer);
NK_API void nk_glfw3_device_destroy(struct nk_glfw* glfw);
NK_API void nk_glfw3_device_create(struct nk_glfw* glfw);
NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff);
NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *win, int button, int action, int mods);
/*
* ==============================================================
*
* IMPLEMENTATION
*
* ===============================================================
*/
#ifdef NK_GLFW_GL3_IMPLEMENTATION
#ifndef NK_GLFW_DOUBLE_CLICK_LO
#define NK_GLFW_DOUBLE_CLICK_LO 0.02
#endif
#ifndef NK_GLFW_DOUBLE_CLICK_HI
#define NK_GLFW_DOUBLE_CLICK_HI 0.2
#endif
struct nk_glfw_vertex {
float position[2];
float uv[2];
nk_byte col[4];
};
#ifdef __EMSCRIPTEN__
#define NK_SHADER_VERSION "#version 100\n"
#else
#ifdef __APPLE__
#define NK_SHADER_VERSION "#version 150\n"
#else
#define NK_SHADER_VERSION "#version 300 es\n"
#endif
#endif
NK_API void
nk_glfw3_device_create(struct nk_glfw* glfw)
{
GLint status;
static const GLchar *vertex_shader =
NK_SHADER_VERSION
"uniform mat4 ProjMtx;\n"
#ifdef __EMSCRIPTEN__
"attribute vec2 Position;\n"
"attribute vec2 TexCoord;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
#else
"in vec2 Position;\n"
"in vec2 TexCoord;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
#endif
"void main() {\n"
" Frag_UV = TexCoord;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
"}\n";
static const GLchar *fragment_shader =
NK_SHADER_VERSION
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
#ifdef __EMSCRIPTEN__
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
#else
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
#endif
"void main(){\n"
#ifdef __EMSCRIPTEN__
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV);\n"
#else
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
#endif
"}\n";
struct nk_glfw_device *dev = &glfw->ogl;
nk_buffer_init_default(&dev->cmds);
dev->prog = glCreateProgram();
dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
glCompileShader(dev->vert_shdr);
glCompileShader(dev->frag_shdr);
glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glAttachShader(dev->prog, dev->vert_shdr);
glAttachShader(dev->prog, dev->frag_shdr);
glLinkProgram(dev->prog);
glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
assert(status == GL_TRUE);
dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
{
/* buffer setup */
GLsizei vs = sizeof(struct nk_glfw_vertex);
size_t vp = offsetof(struct nk_glfw_vertex, position);
size_t vt = offsetof(struct nk_glfw_vertex, uv);
size_t vc = offsetof(struct nk_glfw_vertex, col);
glGenBuffers(1, &dev->vbo);
glGenBuffers(1, &dev->ebo);
glGenVertexArrays(1, &dev->vao);
glBindVertexArray(dev->vao);
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
glEnableVertexAttribArray((GLuint)dev->attrib_pos);
glEnableVertexAttribArray((GLuint)dev->attrib_uv);
glEnableVertexAttribArray((GLuint)dev->attrib_col);
glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#ifndef __EMSCRIPTEN__
glBindVertexArray(0);
#endif
}
NK_INTERN void
nk_glfw3_device_upload_atlas(struct nk_glfw* glfw, const void *image, int width, int height)
{
struct nk_glfw_device *dev = &glfw->ogl;
glGenTextures(1, &dev->font_tex);
glBindTexture(GL_TEXTURE_2D, dev->font_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, image);
}
NK_API void
nk_glfw3_device_destroy(struct nk_glfw* glfw)
{
struct nk_glfw_device *dev = &glfw->ogl;
glDetachShader(dev->prog, dev->vert_shdr);
glDetachShader(dev->prog, dev->frag_shdr);
glDeleteShader(dev->vert_shdr);
glDeleteShader(dev->frag_shdr);
glDeleteProgram(dev->prog);
glDeleteTextures(1, &dev->font_tex);
glDeleteBuffers(1, &dev->vbo);
glDeleteBuffers(1, &dev->ebo);
nk_buffer_free(&dev->cmds);
}
NK_API void
nk_glfw3_render(struct nk_glfw* glfw, enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer)
{
struct nk_glfw_device *dev = &glfw->ogl;
GLfloat ortho[4][4] = {
{2.0f, 0.0f, 0.0f, 0.0f},
{0.0f,-2.0f, 0.0f, 0.0f},
{0.0f, 0.0f,-1.0f, 0.0f},
{-1.0f,1.0f, 0.0f, 1.0f},
};
ortho[0][0] /= (GLfloat)glfw->width;
ortho[1][1] /= (GLfloat)glfw->height;
/* setup global state */
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glActiveTexture(GL_TEXTURE0);
/* setup program */
glUseProgram(dev->prog);
glUniform1i(dev->uniform_tex, 0);
glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
glViewport(0,0,(GLsizei)glfw->display_width,(GLsizei)glfw->display_height);
{
/* convert from command queue into draw list and draw to screen */
const struct nk_draw_command *cmd = NULL;
void *vertices, *elements;
const nk_draw_index *offset = NULL;
/* allocate vertex and element buffer */
glBindVertexArray(dev->vao);
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW);
/* load draw vertices & elements directly into vertex + element buffer */
#ifndef __EMSCRIPTEN__
vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
#else
vertices = malloc((size_t)max_vertex_buffer);
elements = malloc((size_t)max_element_buffer);
#endif
{
/* fill convert configuration */
struct nk_convert_config config;
static const struct nk_draw_vertex_layout_element vertex_layout[] = {
{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)},
{NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)},
{NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)},
{NK_VERTEX_LAYOUT_END}
};
NK_MEMSET(&config, 0, sizeof(config));
config.vertex_layout = vertex_layout;
config.vertex_size = sizeof(struct nk_glfw_vertex);
config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
config.null = dev->null;
config.circle_segment_count = 22;
config.curve_segment_count = 22;
config.arc_segment_count = 22;
config.global_alpha = 1.0f;
config.shape_AA = AA;
config.line_AA = AA;
/* setup buffers to load vertices and elements */
{struct nk_buffer vbuf, ebuf;
nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer);
nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer);
nk_convert(&glfw->ctx, &dev->cmds, &vbuf, &ebuf, &config);}
}
#ifdef __EMSCRIPTEN__
glBufferSubData(GL_ARRAY_BUFFER, 0, (size_t)max_vertex_buffer, vertices);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, (size_t)max_element_buffer, elements);
free(vertices);
free(elements);
#else
glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
#endif
/* iterate over and execute each draw command */
nk_draw_foreach(cmd, &glfw->ctx, &dev->cmds)
if(glfw->height > 0) //< @r-lyeh: fix opengl/scissor error when win is minimized (h==0)
{
if (!cmd->elem_count) continue;
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
glScissor(
(GLint)(cmd->clip_rect.x * glfw->fb_scale.x),
(GLint)((glfw->height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw->fb_scale.y),
(GLint)(cmd->clip_rect.w * glfw->fb_scale.x),
(GLint)(cmd->clip_rect.h * glfw->fb_scale.y));
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
offset += cmd->elem_count;
}
nk_clear(&glfw->ctx);
}
/* default OpenGL state */
glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#ifndef __EMSCRIPTEN__
glBindVertexArray(0);
#endif
glDisable(GL_BLEND);
glDisable(GL_SCISSOR_TEST);
}
NK_API void
nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint)
{
if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam)
struct nk_glfw* glfw = glfwGetWindowUserPointer(win);
if (glfw->text_len < NK_GLFW_TEXT_MAX)
glfw->text[glfw->text_len++] = codepoint;
}
NK_API void
nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff)
{
if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam)
struct nk_glfw* glfw = glfwGetWindowUserPointer(win);
glfw->scroll.x += (float)xoff;
glfw->scroll.y += (float)yoff;
glfw->scroll_bak.x += (float)xoff; //< @r-lyeh
glfw->scroll_bak.y += (float)yoff; //< @r-lyeh
}
NK_API void
nk_glfw3_mouse_button_callback(GLFWwindow* win, int button, int action, int mods)
{
if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam)
double x, y;
if (button != GLFW_MOUSE_BUTTON_LEFT) return;
struct nk_glfw* glfw = glfwGetWindowUserPointer(win);
glfwGetCursorPos(win, &x, &y);
if (action == GLFW_PRESS) {
double dt = glfwGetTime() - glfw->last_button_click;
if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) {
glfw->is_double_click_down = nk_true;
glfw->double_click_pos = nk_vec2((float)x, (float)y);
}
glfw->last_button_click = glfwGetTime();
} else glfw->is_double_click_down = nk_false;
}
NK_INTERN void
nk_glfw3_clipboard_paste(nk_handle usr, struct nk_text_edit *edit)
{
(void)usr;
struct nk_glfw* glfw = glfwGetWindowUserPointer(window_handle()); // @rlyeh < struct nk_glfw* glfw = usr.ptr;
const char *text = glfwGetClipboardString(glfw->win);
if (text) nk_textedit_paste(edit, text, nk_strlen(text));
}
NK_INTERN void
nk_glfw3_clipboard_copy(nk_handle usr, const char *text, int len)
{
(void)usr;
struct nk_glfw* glfw = glfwGetWindowUserPointer(window_handle()); // @rlyeh < struct nk_glfw* glfw = usr.ptr;
char *str = 0;
if (!len) return;
str = (char*)malloc((size_t)len+1);
if (!str) return;
memcpy(str, text, (size_t)len);
str[len] = '\0';
glfwSetClipboardString(glfw->win, str);
free(str);
}
NK_API struct nk_context*
nk_glfw3_init(struct nk_glfw* glfw, GLFWwindow *win, enum nk_glfw_init_state init_state)
{
glfwSetWindowUserPointer(win, glfw);
glfw->win = win;
if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
glfwSetCharCallback(win, nk_glfw3_char_callback);
glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
}
nk_init_default(&glfw->ctx, 0);
glfw->ctx.clip.copy = nk_glfw3_clipboard_copy;
glfw->ctx.clip.paste = nk_glfw3_clipboard_paste;
glfw->ctx.clip.userdata = nk_handle_ptr(0);
glfw->last_button_click = 0;
nk_glfw3_device_create(glfw);
glfw->is_double_click_down = nk_false;
glfw->double_click_pos = nk_vec2(0, 0);
return &glfw->ctx;
}
NK_API void
nk_glfw3_font_stash_begin(struct nk_glfw* glfw, struct nk_font_atlas **atlas)
{
nk_font_atlas_init_default(&glfw->atlas);
nk_font_atlas_begin(&glfw->atlas);
*atlas = &glfw->atlas;
}
NK_API void
nk_glfw3_font_stash_end(struct nk_glfw* glfw)
{
const void *image; int w, h;
image = nk_font_atlas_bake(&glfw->atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
nk_glfw3_device_upload_atlas(glfw, image, w, h);
nk_font_atlas_end(&glfw->atlas, nk_handle_id((int)glfw->ogl.font_tex), &glfw->ogl.null);
if (glfw->atlas.default_font)
nk_style_set_font(&glfw->ctx, &glfw->atlas.default_font->handle);
nk_style_load_all_cursors(&glfw->ctx, glfw->atlas.cursors); //< @r-lyeh
nk_style_hide_cursor(&glfw->ctx); //< @r-lyeh
}
NK_API void
nk_glfw3_new_frame(struct nk_glfw* glfw)
{
int i;
double x, y;
struct nk_context *ctx = &glfw->ctx;
struct GLFWwindow *win = glfw->win;
glfwGetWindowSize(win, &glfw->width, &glfw->height);
glfwGetFramebufferSize(win, &glfw->display_width, &glfw->display_height);
glfw->fb_scale.x = (float)glfw->display_width/(float)glfw->width;
glfw->fb_scale.y = (float)glfw->display_height/(float)glfw->height;
nk_input_begin(ctx);
for (i = 0; i < glfw->text_len; ++i)
nk_input_unicode(ctx, glfw->text[i]);
#if NK_GLFW_GL3_MOUSE_GRABBING
/* optional grabbing behavior */
if (ctx->input.mouse.grab)
glfwSetInputMode(glfw->win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
else if (ctx->input.mouse.ungrab)
glfwSetInputMode(glfw->win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
#endif
nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||
glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
} else {
nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(ctx, NK_KEY_COPY, 0);
nk_input_key(ctx, NK_KEY_PASTE, 0);
nk_input_key(ctx, NK_KEY_CUT, 0);
nk_input_key(ctx, NK_KEY_SHIFT, 0);
}
if(glfwGetInputMode(win, GLFW_CURSOR) == GLFW_CURSOR_NORMAL) { //< @r-lyeh: do not grab input when mouse is hidden (fps cam)
glfwGetCursorPos(win, &x, &y);
nk_input_motion(ctx, (int)x, (int)y);
#if NK_GLFW_GL3_MOUSE_GRABBING
if (ctx->input.mouse.grabbed) {
glfwSetCursorPos(glfw->win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y);
ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
}
#endif
nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
nk_input_button(ctx, NK_BUTTON_DOUBLE, glfw->double_click_pos.x, glfw->double_click_pos.y, glfw->is_double_click_down);
} //< @r-lyeh
nk_input_scroll(ctx, glfw->scroll);
nk_input_end(&glfw->ctx);
glfw->text_len = 0;
glfw->scroll = nk_vec2(0,0);
}
NK_API
void nk_glfw3_shutdown(struct nk_glfw* glfw)
{
nk_font_atlas_clear(&glfw->atlas);
nk_free(&glfw->ctx);
nk_glfw3_device_destroy(glfw);
memset(glfw, 0, sizeof(*glfw));
}
#endif
#endif

2943
sfw/render_gui/fwk_ui.c Normal file

File diff suppressed because it is too large Load Diff

96
sfw/render_gui/fwk_ui.h Normal file
View File

@ -0,0 +1,96 @@
// -----------------------------------------------------------------------------
// immediate ui framework
// - rlyeh, public domain
//
// @todo: logger/console
// @todo: surround-adaptive window resizing. ie, surrounding windows adapting theirselves to fit dragged active window
enum PANEL_FLAGS {
PANEL_OPEN = 1,
};
API int ui_notify(const char *title, const char *body);
API int ui_window(const char *title, int *enabled);
API int ui_panel(const char *title, int flags); // may be embedded inside a window, or standalone
API int ui_collapse(const char *label, const char *id);
API int ui_collapseo(const char *label, const char *id);
API int ui_contextual();
API int ui_section(const char *title);
API int ui_int(const char *label, int *value);
API int ui_bool(const char *label, bool *value);
API int ui_short(const char *label, short *value);
API int ui_float_(const char *label, float *value, float step);
API int ui_float2_(const char *label, float value[2], float step);
API int ui_float3_(const char *label, float value[3], float step);
API int ui_float4_(const char *label, float value[4], float step);
API int ui_float(const char *label, float *value);
API int ui_float2(const char *label, float value[2]);
API int ui_float3(const char *label, float value[3]);
API int ui_float4(const char *label, float value[4]);
API int ui_mat33(const char *label, float mat33[9]);
API int ui_mat34(const char *label, float mat34[12]);
API int ui_mat44(const char *label, float mat44[16]);
API int ui_double(const char *label, double *value);
API int ui_buffer(const char *label, char *buffer, int buflen);
API int ui_string(const char *label, char **string);
API int ui_color3(const char *label, unsigned *color); //[0..255]
API int ui_color3f(const char *label, float color[3]); //[0..1]
API int ui_color4(const char *label, unsigned *color); //[0..255]
API int ui_color4f(const char *label, float color[4]); //[0..1]
API int ui_unsigned(const char *label, unsigned *value);
API int ui_unsigned2(const char *label, unsigned *value);
API int ui_unsigned3(const char *label, unsigned *value);
API int ui_button(const char *label);
API int ui_button_transparent(const char *label);
API int ui_buttons(int buttons, /*labels*/...);
API int ui_toolbar(const char *options); // int choice = ui_toolbar("A;B;C;D");
API int ui_submenu(const char *options); // int choice = ui_submenu("A;B;C;D");
API int ui_browse(const char **outfile, bool *inlined); // may be embedded inside a window or inside a panel
API int ui_toggle(const char *label, bool *value);
API int ui_dialog(const char *title, const char *text, int choices, bool *show); // @fixme: return
API int ui_list(const char *label, const char **items, int num_items, int *selector);
API int ui_radio(const char *label, const char **items, int num_items, int *selector);
API int ui_texture(const char *label, texture_t t);
API int ui_subtexture(const char *label, texture_t t, unsigned x, unsigned y, unsigned w, unsigned h);
API int ui_image(const char *label, handle id, unsigned w, unsigned h); //(w,h) can be 0
API int ui_subimage(const char *label, handle id, unsigned iw, unsigned ih, unsigned sx, unsigned sy, unsigned sw, unsigned sh);
API int ui_colormap(const char *label, colormap_t *cm); // returns num member changed: 1 for color, 2 for texture map
API int ui_separator();
API int ui_bitmask8(const char *label, uint8_t *bits);
API int ui_bitmask16(const char *label, uint16_t *bits);
API int ui_console();
API int ui_clampf_(const char *label, float *value, float minf, float maxf, float step);
API int ui_clampf(const char *label, float *value, float minf, float maxf);
API int ui_label(const char *label);
API int ui_label2(const char *label, const char *caption);
API int ui_label2_bool(const char *label, bool enabled);
API int ui_label2_float(const char *label, float value);
API int ui_label2_toolbar(const char *label, const char *icons);
API int ui_slider(const char *label, float *value);
API int ui_slider2(const char *label, float *value, const char *caption);
API int ui_contextual_end(int close);
API int ui_collapse_clicked();
API int ui_collapse_end();
API int ui_panel_end();
API int ui_window_end();
API int ui_show(const char *panel_or_window_title, int enabled);
API int ui_dims(const char *panel_or_window_title, float width, float height);
API int ui_visible(const char *panel_or_window_title); // @todo: include ui_collapse() items that are open as well?
API vec2 ui_get_dims();
API int ui_enable();
API int ui_enabled();
API int ui_disable();
API int ui_has_menubar();
API int ui_menu(const char *items); // semicolon-separated or comma-separated items
API int ui_menu_editbox(char *buf, int bufcap);
API int ui_item();
API int ui_popups(); // ui_any_popup()? ui_has_popups()?
API int ui_hover(); // ui_is_hover()?
API int ui_active(); // ui_is_active()?
API int ui_demo(int do_windows);
API void *ui_handle();

1160
sfw/render_gui/fwk_window.c Normal file

File diff suppressed because it is too large Load Diff