diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index da279b1..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,43 +0,0 @@ -CONTRIBUTING -============ -## Submitting changes -Please send a GitHub Pull Request with a clear list of what you've done (read more about [pull requests](http://help.github.com/pull-requests/)). - -## Features -If you have an idea for new features just [open an issue](https://github.com/vurtun/zahnrad/issues) with your suggestion. - * Find and correct spelling mistakes - * Add (insert your favorite platform or render backend here) demo implementation (some possibilities: DirectX 9/DirectX 10/DirectX 11 and win32 with OpenGL) - * Add clipboard user callbacks back into all demos - * Add additional widgets - * Find a better way to change the style or group style options - * Add support for multiple pointers for touch input devices (probably requires to rewrite mouse handling in `struct zr_input`) - * Extend xlib demo to support image drawing with arbitrary image width and height - * Change cursor in `zr_widget_edit_box` and `zr_widget_edit_field` to thin standard cursor version used in editors - * Extend piemenu to support submenus (another ring around the first ring or something like [this:](http://gdj.gdj.netdna-cdn.com/wp-content/uploads/2013/02/ui+concepts+13.gif)) and turn it into a default library widget. - * Add label describing the currently active piemenu entry - * Add tables with scaleable column width - * Rewrite the chart API to support a better range of charts (maybe take notes from Javascript chart frameworks) - * Create an API to allow scaling between groups (maybe extend and convert the demo example) - * Come up with a better way to provide and create widget and window styles - * Add multiple Tab support (maybe use `zr_group` and add a header) - * Extend context to not only support overlapping windows but tiled windows as well - -## Bugs - * Seperator widget is currently bugged and does not work as intended - * Text handling is still a little bit janky and probably needs to be further tested and polished - * `zr_edit_buffer` with multiline flag is bugged for '\n', need to differentiate between visible and non-visible characters - -## Coding conventions - * Only use C89 (ANSI C) - * Do not use any compiler specific extensions - * For indent use four spaces - * Do not typedef structs, unions and enums - * Variable, object and function names should always be lowercase and use underscores instead of camel case - * Whitespace after for, while, if, do and switch - * Always use parentheses if you use the sizeof operator (e.g: sizeof(struct zr_context) and not sizeof struct zr_context) - * Beginning braces on the new line for functions and on the same line otherwise. - * If function becomes to big either a.) create a subblock inside the function and comment or b.) write a functional function - * Only use fixed size types (zr_uint, zr_size, ...) if you really need to and use basic types otherwise - * Do not include any header files in either zahnrad.h or zahnrad.c - * Do not add dependencies rather write your own version if possible - * Write correct commit messages: (http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 4f0e12e..0000000 --- a/LICENSE +++ /dev/null @@ -1,17 +0,0 @@ -Copyright (c) 2016 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. diff --git a/Readme.md b/Readme.md index c73d5bb..087677f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,9 +1,7 @@ -# Zahnrad -[![Coverity Status](https://scan.coverity.com/projects/5863/badge.svg)](https://scan.coverity.com/projects/5863) - +# Nuklear This is a minimal state immediate mode graphical user interface toolkit -written in ANSI C and licensed under zlib. It was designed as a simple embeddable user interface for -application and does not have any direct dependencies, +written in ANSI C and licensed under public domain. It was designed as a simple +embeddable user interface for application and does not have any dependencies, a default renderbackend or OS window and input handling but instead provides a very modular library approach by using simple input state for input and draw commands describing primitive shapes as output. So instead of providing a @@ -12,34 +10,30 @@ render backends it only focuses on the actual UI. ## Features - Immediate mode graphical user interface toolkit +- Single header library - Written in C89 (ANSI C) -- Small codebase (~12kLOC) +- Small codebase (~15kLOC) - Focus on portability, efficiency and simplicity -- No dependencies (not even the standard library) -- No global or hidden state +- No dependencies (not even the standard library if not wanted) - Fully skinnable and customizable +- Low memory footprint with total memory control if needed or wanted - UTF-8 support - -## Optional -- Vertex buffer output -- Font handling +- No global or hidden state +- Customizeable library modules (you can compile and use only what you need) +- Optional font baker and vertex buffer output ## Building -The library is self-contained within four different files that only have to be -copied and compiled into your application. Files zahnrad.c and zahnrad.h make up -the core of the library, while stb_rect_pack.h and stb_truetype.h are -for a optional font handling implementation and can be removed if not needed. -- zahnrad.c -- zahnrad.h -- stb_rect_pack.h (optional) -- stb_truetype.h (optional) +This library is self contained in one single header file and can be used either +in header only mode or in implementation mode. The header only mode is used +by default when included and allows including this header in other headers +and does not contain the actual implementation. -There are no dependencies or a particular building process required. You just have -to compile the .c file and #include zahnrad.h into your project. To actually -run you have to provide the input state, configuration style and memory -for draw commands to the library. After the GUI was executed all draw commands -have to be either executed or optionally converted into a vertex buffer to -draw the GUI. +The implementation mode requires to define the preprocessor macro +`NK_IMPLEMENTATION` in *one* .c/.cpp file before #includeing this file, e.g.: +```c + #define NK_IMPLEMENTATION + #include "nuklear.h" +``` ## Gallery ![screenshot](https://cloud.githubusercontent.com/assets/8057201/11761525/ae06f0ca-a0c6-11e5-819d-5610b25f6ef4.gif) @@ -51,40 +45,100 @@ draw the GUI. ## Example ```c /* init gui state */ -struct zr_context ctx; -zr_init_fixed(&ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font); +struct nk_context ctx; +nk_init_fixed(&ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font); enum {EASY, HARD}; int op = EASY; float value = 0.6f; int i = 20; -struct zr_panel layout; -zr_begin(&ctx, &layout, "Show", zr_rect(50, 50, 220, 220), - ZR_WINDOW_BORDER|ZR_WINDOW_MOVEABLE|ZR_WINDOW_CLOSEABLE); +struct nk_panel layout; +nk_begin(&ctx, &layout, "Show", nk_rect(50, 50, 220, 220), + NK_WINDOW_BORDER|NK_WINDOW_MOVEABLE|NK_WINDOW_CLOSEABLE); { /* fixed widget pixel width */ - zr_layout_row_static(&ctx, 30, 80, 1); - if (zr_button_text(&ctx, "button", ZR_BUTTON_DEFAULT)) { + nk_layout_row_static(&ctx, 30, 80, 1); + if (nk_button_text(&ctx, "button", NK_BUTTON_DEFAULT)) { /* event handling */ } /* fixed widget window ratio width */ - zr_layout_row_dynamic(&ctx, 30, 2); - if (zr_option(&ctx, "easy", op == EASY)) op = EASY; - if (zr_option(&ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(&ctx, 30, 2); + if (nk_option(&ctx, "easy", op == EASY)) op = EASY; + if (nk_option(&ctx, "hard", op == HARD)) op = HARD; /* custom widget pixel width */ - zr_layout_row_begin(&ctx, ZR_STATIC, 30, 2); + nk_layout_row_begin(&ctx, NK_STATIC, 30, 2); { - zr_layout_row_push(&ctx, 50); - zr_label(&ctx, "Volume:", ZR_TEXT_LEFT); - zr_layout_row_push(&ctx, 110); - zr_slider_float(&ctx, 0, &value, 1.0f, 0.1f); + nk_layout_row_push(&ctx, 50); + nk_label(&ctx, "Volume:", NK_TEXT_LEFT); + nk_layout_row_push(&ctx, 110); + nk_slider_float(&ctx, 0, &value, 1.0f, 0.1f); } - zr_layout_row_end(&ctx); + nk_layout_row_end(&ctx); } -zr_end(ctx); +nk_end(ctx); ``` ![example](https://cloud.githubusercontent.com/assets/8057201/10187981/584ecd68-675c-11e5-897c-822ef534a876.png) + +##FAQ +--- +#### Why single-file headers? +Windows doesn't have standard directories where libraries +live. That makes deploying libraries in Windows a lot more +painful than open source developers on Unix-derivates generally +realize. (It also makes library dependencies a lot worse in Windows.) + +There's also a common problem in Windows where a library was built +against a different version of the runtime library, which causes +link conflicts and confusion. Shipping the libs as headers means +you normally just compile them straight into your project without +making libraries, thus sidestepping that problem. + +Making them a single file makes it very easy to just +drop them into a project that needs them. (Of course you can +still put them in a proper shared library tree if you want.) + +Why not two files, one a header and one an implementation? +The difference between 10 files and 9 files is not a big deal, +but the difference between 2 files and 1 file is a big deal. +You don't need to zip or tar the files up, you don't have to +remember to attach *two* files, etc. + +#### Where is the documentation? +Each file has documentation, basic ussage description and +examples at the top of the file. In addition each API function, +struct and member variables are documented as well. +Finally each library has a corresponding test file inside the +test directory for additional working examples. + +#### Why C? +Personally I primarily use C instead of C++ and since I want to +support both C and C++ and C++ is not useable from C I therefore focus +on C. + +#### Why C89? +I use C89 instead of C99/C11 for its portability between different compilers +and accessiblity for other languages. + +##CREDITS: +Developed by Micha Mettke and every direct or indirect contributor to the GitHub. + + +Embeds stb_texedit, stb_truetype and stb_rectpack by Sean Barret (public domain) +Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license). + + +Big thank you to Omar Cornut (ocornut@github) for his imgui library and +giving me the inspiration for this library, Casey Muratori for handmade hero +and his original immediate mode graphical user interface idea and Sean +Barret for his amazing single header libraries which restored by faith +in libraries and brought me to create some of my own. + +##LICENSE: +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish and distribute this file as you see fit. + diff --git a/demo/Readme.md b/demo/Readme.md deleted file mode 100644 index 0805455..0000000 --- a/demo/Readme.md +++ /dev/null @@ -1 +0,0 @@ -# Demo diff --git a/demo/allegro5/Makefile b/demo/allegro5/Makefile index 5d39fd5..0d3bd7e 100644 --- a/demo/allegro5/Makefile +++ b/demo/allegro5/Makefile @@ -8,7 +8,7 @@ DCC = gcc # Flags CFLAGS = -std=c89 -pedantic -SRC = ../../zahnrad.c allegro.c +SRC = main.c OBJ = $(SRC:.c=.o) ifeq ($(OS),Windows_NT) diff --git a/demo/allegro5/allegro.c b/demo/allegro5/allegro.c deleted file mode 100644 index d612dff..0000000 --- a/demo/allegro5/allegro.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - Copyright (c) 2016 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 -#include - -#include -#include - -/* macros */ -#define MAX_VERTEX_MEMORY 512 * 1024 -#define MAX_ELEMENT_MEMORY 128 * 1024 - -#define DEMO_DO_NOT_DRAW_IMAGES -#include "../../zahnrad.h" -#include "../demo.c" - -struct device { - ALLEGRO_DISPLAY *display; - ALLEGRO_EVENT_QUEUE *queue; - ALLEGRO_BITMAP *texture; - ALLEGRO_VERTEX_DECL *vertex_decl; - struct zr_draw_null_texture null; - struct zr_buffer cmds; - void *vertex_buffer; - void *element_buffer; -}; - -struct allegro_vertex { - struct zr_vec2 pos; - struct zr_vec2 uv; - ALLEGRO_COLOR col; -}; - -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 char* -file_load(const char* path, size_t* siz) -{ - char *buf; - FILE *fd = fopen(path, "rb"); - if (!fd) die("Failed to open file: %s\n", path); - fseek(fd, 0, SEEK_END); - *siz = (size_t)ftell(fd); - fseek(fd, 0, SEEK_SET); - buf = (char*)calloc(*siz, 1); - fread(buf, *siz, 1, fd); - fclose(fd); - return buf; -} - -static void -device_init(struct device *dev) -{ - ALLEGRO_VERTEX_ELEMENT elems[] = { - {ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, offsetof(struct allegro_vertex, pos)}, - {ALLEGRO_PRIM_TEX_COORD, ALLEGRO_PRIM_FLOAT_2, offsetof(struct allegro_vertex, uv)}, - {ALLEGRO_PRIM_COLOR_ATTR, 0, offsetof(struct allegro_vertex, col)}, - {0,0,0} - }; - dev->vertex_decl = al_create_vertex_decl(elems, sizeof(struct allegro_vertex)); - - /* allocate memory for drawing process */ - dev->vertex_buffer = calloc(MAX_VERTEX_MEMORY, 1); - dev->element_buffer = calloc(MAX_ELEMENT_MEMORY, 1); -} - -static void -device_shutdown(struct device *dev) -{ - free(dev->vertex_buffer); - free(dev->element_buffer); - zr_buffer_free(&dev->cmds); -} - -static void -device_upload_atlas(struct device *dev, const void *image, int width, int height) -{ - /* create allegro font bitmap */ - ALLEGRO_BITMAP *bitmap = 0; - int flags = al_get_new_bitmap_flags(); - int fmt = al_get_new_bitmap_format(); - al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP|ALLEGRO_MIN_LINEAR|ALLEGRO_MAG_LINEAR); - al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE); - bitmap = al_create_bitmap(width, height); - al_set_new_bitmap_flags(flags); - al_set_new_bitmap_format(fmt); - assert(bitmap); - - { - /* copy font texture into bitmap */ - ALLEGRO_LOCKED_REGION * locked_img; - locked_img = al_lock_bitmap(bitmap, al_get_bitmap_format(bitmap), ALLEGRO_LOCK_WRITEONLY); - assert(locked_img); - memcpy(locked_img->data, image, sizeof(uint32_t)*(size_t)(width*height)); - al_unlock_bitmap(bitmap); - } - - /* convert software texture into hardware texture */ - dev->texture = al_clone_bitmap(bitmap); - al_destroy_bitmap(bitmap); - assert(dev->texture); -} - -static void -device_draw(struct device *dev, struct zr_context *ctx, enum zr_anti_aliasing AA) -{ - int op, src, dst; - al_get_blender(&op, &src, &dst); - al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA); - - { - const struct zr_draw_command *cmd; - struct zr_buffer vbuf, ebuf; - int offset = 0; - struct allegro_vertex *vertices = 0; - int *indicies = 0; - - /* fill converting configuration */ - struct zr_convert_config config; - memset(&config, 0, sizeof(config)); - config.global_alpha = 1.0f; - config.shape_AA = AA; - config.line_AA = AA; - config.circle_segment_count = 22; - config.arc_segment_count = 22; - config.curve_segment_count = 22; - config.null = dev->null; - - /* convert from command into hardware format */ - zr_buffer_init_fixed(&vbuf, dev->vertex_buffer, MAX_VERTEX_MEMORY); - zr_buffer_init_fixed(&ebuf, dev->element_buffer, MAX_ELEMENT_MEMORY); - zr_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config); - - { - /* allegro does not support 32-bit packed color */ - unsigned int i = 0; - struct zr_draw_vertex *verts = (struct zr_draw_vertex*)dev->vertex_buffer; - vertices = calloc(sizeof(struct allegro_vertex), ctx->canvas.vertex_count); - for (i = 0; i < ctx->canvas.vertex_count; ++i) { - zr_byte *c; - vertices[i].pos = verts[i].position; - vertices[i].uv = verts[i].uv; - c = (zr_byte*)&verts[i].col; - vertices[i].col = al_map_rgba(c[0], c[1], c[2], c[3]); - } - } - { - /* allegro does not support 16-bit indicies: - * @OPT: define zr_draw_index as int to fix this issue. */ - unsigned int i = 0; - zr_draw_index *elements = (zr_draw_index*)dev->element_buffer; - indicies = calloc(sizeof(int), ctx->canvas.element_count); - for (i = 0; i < ctx->canvas.element_count; ++i) - indicies[i] = elements[i]; - } - - /* iterate over and execute each draw command */ - zr_draw_foreach(cmd, ctx, &dev->cmds) - { - ALLEGRO_BITMAP *texture = cmd->texture.ptr; - if (!cmd->elem_count) continue; - al_set_clipping_rectangle((int)cmd->clip_rect.x, (int)cmd->clip_rect.y, - (int)cmd->clip_rect.w, (int)cmd->clip_rect.h); - al_draw_indexed_prim(vertices, dev->vertex_decl, texture, &indicies[offset], - (int)cmd->elem_count, ALLEGRO_PRIM_TRIANGLE_LIST); - offset += cmd->elem_count; - } - - free(vertices); - free(indicies); - zr_clear(ctx); - } - al_set_blender(op, src, dst); - al_set_clipping_rectangle(0,0,al_get_display_width(dev->display), - al_get_display_height(dev->display)); -} - -static void -input_key(struct zr_context *ctx, ALLEGRO_EVENT *evt, int down) -{ - int sym = evt->keyboard.keycode; - if (sym == ALLEGRO_KEY_RSHIFT || sym == ALLEGRO_KEY_LSHIFT) - zr_input_key(ctx, ZR_KEY_SHIFT, down); - else if (sym == ALLEGRO_KEY_DELETE) - zr_input_key(ctx, ZR_KEY_DEL, down); - else if (sym == ALLEGRO_KEY_ENTER) - zr_input_key(ctx, ZR_KEY_ENTER, down); - else if (sym == ALLEGRO_KEY_TAB) - zr_input_key(ctx, ZR_KEY_TAB, down); - else if (sym == ALLEGRO_KEY_BACKSPACE) - zr_input_key(ctx, ZR_KEY_BACKSPACE, down); - else if (sym == ALLEGRO_KEY_LEFT) - zr_input_key(ctx, ZR_KEY_LEFT, down); - else if (sym == ALLEGRO_KEY_RIGHT) - zr_input_key(ctx, ZR_KEY_RIGHT, down); - else if (sym == ALLEGRO_KEY_C) - zr_input_key(ctx, ZR_KEY_COPY, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); - else if (sym == ALLEGRO_KEY_V) - zr_input_key(ctx, ZR_KEY_PASTE, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); - else if (sym == ALLEGRO_KEY_X) - zr_input_key(ctx, ZR_KEY_CUT, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); -} - -static void -input_button(struct zr_context *ctx, ALLEGRO_EVENT *evt, int down) -{ - const int x = evt->mouse.x; - const int y = evt->mouse.y; - if (evt->mouse.button == 1) - zr_input_button(ctx, ZR_BUTTON_LEFT, x, y, down); - if (evt->mouse.button == 2) - zr_input_button(ctx, ZR_BUTTON_RIGHT, x, y, down); -} - -int -main(int argc, char *argv[]) -{ - struct device dev; - int running = 1; - - struct demo gui; - struct zr_font *font; - struct zr_font_atlas atlas; - const char *font_path = (argc > 1) ? argv[1]: 0; - - /* Allegro */ - al_init(); - al_install_keyboard(); - al_install_mouse(); - al_init_primitives_addon(); - dev.display = al_create_display(WINDOW_WIDTH, WINDOW_HEIGHT); - al_set_window_title(dev.display, "Zahnrad"); - dev.queue = al_create_event_queue(); - al_register_event_source(dev.queue, al_get_display_event_source(dev.display)); - al_register_event_source(dev.queue, al_get_keyboard_event_source()); - al_register_event_source(dev.queue, al_get_mouse_event_source()); - - device_init(&dev); - { - /* Font */ - const void *image; - int width, height; - - zr_font_atlas_init_default(&atlas); - zr_font_atlas_begin(&atlas); - if (font_path) font = zr_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); - else font = zr_font_atlas_add_default(&atlas, 14.0f, NULL); - image = zr_font_atlas_bake(&atlas, &width, &height, ZR_FONT_ATLAS_RGBA32); - device_upload_atlas(&dev, image, width, height); - zr_font_atlas_end(&atlas, zr_handle_ptr(dev.texture), &dev.null); - - /* GUI */ - memset(&gui, 0, sizeof(gui)); - zr_buffer_init_default(&dev.cmds); - zr_init_default(&gui.ctx, &font->handle); - } - - - while (running) { - /* Input */ - ALLEGRO_EVENT evt; - zr_input_begin(&gui.ctx); - while (al_get_next_event(dev.queue, &evt)) { - if (evt.type == ALLEGRO_EVENT_DISPLAY_CLOSE) goto cleanup; - else if (evt.type == ALLEGRO_EVENT_KEY_UP && evt.keyboard.display == dev.display) - input_key(&gui.ctx, &evt, zr_false); - else if (evt.type == ALLEGRO_EVENT_KEY_DOWN && evt.keyboard.display == dev.display) - input_key(&gui.ctx, &evt, zr_true); - else if (evt.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) - input_button(&gui.ctx, &evt, zr_true); - else if (evt.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) - input_button(&gui.ctx, &evt, zr_false); - else if (evt.type == ALLEGRO_EVENT_MOUSE_AXES) { - zr_input_motion(&gui.ctx, evt.mouse.x, evt.mouse.y); - } else if (evt.type == ALLEGRO_EVENT_KEY_CHAR) { - if (evt.keyboard.display == dev.display) - if (evt.keyboard.unichar > 0 && evt.keyboard.unichar < 0x10000) - zr_input_unicode(&gui.ctx, (zr_rune)evt.keyboard.unichar); - } - } - zr_input_end(&gui.ctx); - - /* GUI */ - running = run_demo(&gui); - - /* Draw */ - al_clear_to_color(al_map_rgba_f(0.2f, 0.2f, 0.2f, 1.0f)); - device_draw(&dev, &gui.ctx, ZR_ANTI_ALIASING_ON); - al_flip_display(); - } - -cleanup: - /* Cleanup */ - if (dev.texture) - al_destroy_bitmap(dev.texture); - if (dev.queue) - al_destroy_event_queue(dev.queue); - if (dev.display) - al_destroy_display(dev.display); - zr_font_atlas_clear(&atlas); - device_shutdown(&dev); - zr_free(&gui.ctx); - return 0; -} - diff --git a/demo/allegro5/main.c b/demo/allegro5/main.c new file mode 100644 index 0000000..0587e08 --- /dev/null +++ b/demo/allegro5/main.c @@ -0,0 +1,131 @@ +/* nuklear - v1.00 - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* macros */ +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +/* these defines are both needed for the header + * and source file. So if you split them remember + * to copy them as well. */ +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#include "nuklear_allegro.h" +#include "nuklear_allegro.c" + +int +main(void) +{ + int running = 1; + struct nk_context *ctx; + struct nk_color background; + ALLEGRO_DISPLAY *display; + ALLEGRO_EVENT_QUEUE *queue; + + /* Allegro */ + al_init(); + al_install_keyboard(); + al_install_mouse(); + al_init_primitives_addon(); + display = al_create_display(WINDOW_WIDTH, WINDOW_HEIGHT); + al_set_window_title(display, "nuklear"); + + queue = al_create_event_queue(); + al_register_event_source(queue, al_get_display_event_source(display)); + al_register_event_source(queue, al_get_keyboard_event_source()); + al_register_event_source(queue, al_get_mouse_event_source()); + + ctx = nk_allegro_init(display, MAX_VERTEX_MEMORY, MAX_ELEMENT_MEMORY); + /* Load Fonts: if none of these are loaded a default font will be used */ + {struct nk_font_atlas *atlas; + nk_allegro_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *robot = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Robot-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_allegro_font_stash_end(); + /*nk_style_set_font(ctx, &droid->handle);*/} + + background = nk_rgb(28,48,62); + while (running) + { + /* Input */ + ALLEGRO_EVENT evt; + nk_input_begin(ctx); + while (al_get_next_event(queue, &evt)) { + if (evt.type == ALLEGRO_EVENT_DISPLAY_CLOSE) goto cleanup; + nk_allegro_handle_event(&evt); + } + nk_input_end(ctx); + + /* GUI */ + {struct nk_panel layout; + if (nk_begin(ctx, &layout, "Demo", nk_rect(50, 50, 200, 200), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button", NK_BUTTON_DEFAULT)) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + + nk_layout_row_dynamic(ctx, 22, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + {struct nk_panel combo; + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, &combo, background, 400)) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + }} + } + nk_end(ctx);} + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + al_clear_to_color(al_map_rgba_f(bg[0], bg[1], bg[2], bg[3])); + nk_allegro_render(NK_ANTI_ALIASING_ON); + al_flip_display();} + } + +cleanup: + /* Cleanup */ + if (queue) al_destroy_event_queue(queue); + if (display) al_destroy_display(display); + nk_allegro_shutdown(); + return 0; +} + diff --git a/demo/allegro5/nuklear_allegro.c b/demo/allegro5/nuklear_allegro.c new file mode 100644 index 0000000..87cc0ce --- /dev/null +++ b/demo/allegro5/nuklear_allegro.c @@ -0,0 +1,271 @@ +#define NK_IMPLEMENTATION +#include "../../nuklear.h" + +#include +#include + +struct nk_allegro_device { + ALLEGRO_BITMAP *texture; + ALLEGRO_VERTEX_DECL *vertex_decl; + struct nk_draw_null_texture null; + struct nk_buffer cmds; + void *vertex_buffer; + void *element_buffer; + int max_vertex_memory; + int max_element_memory; +}; + +struct nk_allegro_vertex { + struct nk_vec2 pos; + struct nk_vec2 uv; + ALLEGRO_COLOR col; +}; + +static struct { + ALLEGRO_DISPLAY *win; + struct nk_allegro_device dev; + struct nk_context ctx; + struct nk_font_atlas atlas; +} allegro; + + +NK_API void +nk_allegro_device_create(int max_vertex_memory, int max_element_memory) +{ + struct nk_allegro_device *dev = &allegro.dev; + ALLEGRO_VERTEX_ELEMENT elems[] = { + {ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, offsetof(struct nk_allegro_vertex, pos)}, + {ALLEGRO_PRIM_TEX_COORD, ALLEGRO_PRIM_FLOAT_2, offsetof(struct nk_allegro_vertex, uv)}, + {ALLEGRO_PRIM_COLOR_ATTR, 0, offsetof(struct nk_allegro_vertex, col)}, + {0,0,0} + }; + dev->vertex_decl = al_create_vertex_decl(elems, sizeof(struct nk_allegro_vertex)); + dev->vertex_buffer = calloc((size_t)max_vertex_memory, 1); + dev->element_buffer = calloc((size_t)max_element_memory, 1); + dev->max_vertex_memory = max_vertex_memory; + dev->max_element_memory = max_element_memory; + nk_buffer_init_default(&dev->cmds); +} + +static void +nk_allegro_device_upload_atlas(const void *image, int width, int height) +{ + /* create allegro font bitmap */ + ALLEGRO_BITMAP *bitmap = 0; + struct nk_allegro_device *dev = &allegro.dev; + int flags = al_get_new_bitmap_flags(); + int fmt = al_get_new_bitmap_format(); + al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP|ALLEGRO_MIN_LINEAR|ALLEGRO_MAG_LINEAR); + al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE); + bitmap = al_create_bitmap(width, height); + al_set_new_bitmap_flags(flags); + al_set_new_bitmap_format(fmt); + assert(bitmap); + + {/* copy font texture into bitmap */ + ALLEGRO_LOCKED_REGION * locked_img; + locked_img = al_lock_bitmap(bitmap, al_get_bitmap_format(bitmap), ALLEGRO_LOCK_WRITEONLY); + assert(locked_img); + memcpy(locked_img->data, image, sizeof(uint32_t)*(size_t)(width*height)); + al_unlock_bitmap(bitmap);} + + /* convert software texture into hardware texture */ + dev->texture = al_clone_bitmap(bitmap); + al_destroy_bitmap(bitmap); + assert(dev->texture); +} + +NK_API void +nk_allegro_render(enum nk_anti_aliasing AA) +{ + int op, src, dst; + struct nk_allegro_device *dev = &allegro.dev; + struct nk_context *ctx = &allegro.ctx; + al_get_blender(&op, &src, &dst); + al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA); + + { + const struct nk_draw_command *cmd; + struct nk_buffer vbuf, ebuf; + int offset = 0; + struct nk_allegro_vertex *vertices = 0; + int *indicies = 0; + + /* fill converting configuration */ + struct nk_convert_config config; + memset(&config, 0, sizeof(config)); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.arc_segment_count = 22; + config.curve_segment_count = 22; + config.null = dev->null; + + /* convert from command into hardware format */ + nk_buffer_init_fixed(&vbuf, dev->vertex_buffer, (nk_size)dev->max_vertex_memory); + nk_buffer_init_fixed(&ebuf, dev->element_buffer, (nk_size)dev->max_element_memory); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config); + + { + /* allegro does not support 32-bit packed color */ + unsigned int i = 0; + struct nk_draw_vertex *verts = (struct nk_draw_vertex*)dev->vertex_buffer; + vertices = calloc(sizeof(struct nk_allegro_vertex), ctx->draw_list.vertex_count); + for (i = 0; i < ctx->draw_list.vertex_count; ++i) { + nk_byte *c; + vertices[i].pos = verts[i].position; + vertices[i].uv = verts[i].uv; + c = (nk_byte*)&verts[i].col; + vertices[i].col = al_map_rgba(c[0], c[1], c[2], c[3]); + } + } + { + /* allegro does not support 16-bit indicies: + * @OPT: define nk_draw_index as int to fix this issue. */ + unsigned int i = 0; + nk_draw_index *elements = (nk_draw_index*)dev->element_buffer; + indicies = calloc(sizeof(int), ctx->draw_list.element_count); + for (i = 0; i < ctx->draw_list.element_count; ++i) + indicies[i] = elements[i]; + } + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) + { + ALLEGRO_BITMAP *texture = cmd->texture.ptr; + if (!cmd->elem_count) continue; + al_set_clipping_rectangle((int)cmd->clip_rect.x, (int)cmd->clip_rect.y, + (int)cmd->clip_rect.w, (int)cmd->clip_rect.h); + al_draw_indexed_prim(vertices, dev->vertex_decl, texture, &indicies[offset], + (int)cmd->elem_count, ALLEGRO_PRIM_TRIANGLE_LIST); + offset += cmd->elem_count; + } + + free(vertices); + free(indicies); + nk_clear(ctx); + } + al_set_blender(op, src, dst); + al_set_clipping_rectangle(0,0, al_get_display_width(allegro.win), + al_get_display_height(allegro.win)); +} + +NK_API void +nk_allegro_device_destroy(void) +{ + struct nk_allegro_device *dev = &allegro.dev; + free(dev->vertex_buffer); + free(dev->element_buffer); + nk_buffer_free(&dev->cmds); +} + + +NK_API struct nk_context* +nk_allegro_init(ALLEGRO_DISPLAY *win, int max_vertex_memory, int max_element_memory) +{ + allegro.win = win; + nk_init_default(&allegro.ctx, 0); + nk_allegro_device_create(max_vertex_memory, max_element_memory); + return &allegro.ctx; +} + +NK_API void +nk_allegro_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&allegro.atlas); + nk_font_atlas_begin(&allegro.atlas); + *atlas = &allegro.atlas; +} + +NK_API void +nk_allegro_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&allegro.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_allegro_device_upload_atlas(image, w, h); + nk_font_atlas_end(&allegro.atlas, nk_handle_ptr(allegro.dev.texture), &allegro.dev.null); + if (allegro.atlas.default_font) + nk_style_set_font(&allegro.ctx, &allegro.atlas.default_font->handle); +} + + +NK_API void +nk_allegro_handle_event(ALLEGRO_EVENT *evt) +{ + struct nk_context *ctx = &allegro.ctx; + if ((evt->type == ALLEGRO_EVENT_KEY_UP || + evt->type == ALLEGRO_EVENT_KEY_DOWN) && + evt->keyboard.display == allegro.win) + { + /* key handler */ + int down = (evt->type == ALLEGRO_EVENT_KEY_UP); + int sym = evt->keyboard.keycode; + if (sym == ALLEGRO_KEY_RSHIFT || sym == ALLEGRO_KEY_LSHIFT) + nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (sym == ALLEGRO_KEY_DELETE) + nk_input_key(ctx, NK_KEY_DEL, down); + else if (sym == ALLEGRO_KEY_ENTER) + nk_input_key(ctx, NK_KEY_ENTER, down); + else if (sym == ALLEGRO_KEY_TAB) + nk_input_key(ctx, NK_KEY_TAB, down); + else if (sym == ALLEGRO_KEY_BACKSPACE) + nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (sym == ALLEGRO_KEY_LEFT) { + if (evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else nk_input_key(ctx, NK_KEY_LEFT, down); + } else if (sym == ALLEGRO_KEY_RIGHT) { + if (evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else nk_input_key(ctx, NK_KEY_RIGHT, down); + } else if (sym == ALLEGRO_KEY_HOME) + nk_input_key(ctx, NK_KEY_TEXT_START, down); + else if (sym == ALLEGRO_KEY_END) + nk_input_key(ctx, NK_KEY_TEXT_END, down); + else if (sym == ALLEGRO_KEY_C) + nk_input_key(ctx, NK_KEY_COPY, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + else if (sym == ALLEGRO_KEY_V) + nk_input_key(ctx, NK_KEY_PASTE, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + else if (sym == ALLEGRO_KEY_X) + nk_input_key(ctx, NK_KEY_CUT, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + else if (sym == ALLEGRO_KEY_Z) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + else if (sym == ALLEGRO_KEY_R) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + else if (sym == ALLEGRO_KEY_X) + nk_input_key(ctx, NK_KEY_CUT, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + else if (sym == ALLEGRO_KEY_B) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + else if (sym == ALLEGRO_KEY_E) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && evt->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL); + + } else if (evt->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN || + evt->type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { + /* button handler */ + int down = evt->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN; + const int x = evt->mouse.x, y = evt->mouse.y; + if (evt->mouse.button == 1) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->mouse.button == 2) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + } else if (evt->type == ALLEGRO_EVENT_MOUSE_AXES) { + /* mouse motion */ + nk_input_motion(ctx, evt->mouse.x, evt->mouse.y); + } else if (evt->type == ALLEGRO_EVENT_KEY_CHAR) { + /* text input */ + if (evt->keyboard.display == allegro.win) + if (evt->keyboard.unichar > 0 && evt->keyboard.unichar < 0x10000) + nk_input_unicode(ctx, (nk_rune)evt->keyboard.unichar); + } +} + +NK_API void +nk_allegro_shutdown(void) +{ + if (allegro.dev.texture) + al_destroy_bitmap(allegro.dev.texture); + free(allegro.dev.vertex_buffer); + free(allegro.dev.element_buffer); +} + diff --git a/demo/allegro5/nuklear_allegro.h b/demo/allegro5/nuklear_allegro.h new file mode 100644 index 0000000..8d9ff00 --- /dev/null +++ b/demo/allegro5/nuklear_allegro.h @@ -0,0 +1,17 @@ +#ifndef NK_ALLEGRO_H_ +#define NK_ALLEGRO_H_ + +#include "../../nuklear.h" + +NK_API struct nk_context* nk_allegro_init(ALLEGRO_DISPLAY *win, int max_vertex_memory, int max_element_memory); +NK_API void nk_allegro_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_allegro_font_stash_end(void); + +NK_API void nk_allegro_handle_event(ALLEGRO_EVENT *evt); +NK_API void nk_allegro_render(enum nk_anti_aliasing); +NK_API void nk_allegro_shutdown(void); + +NK_API void nk_allegro_device_destroy(void); +NK_API void nk_allegro_device_create(int max_vertex_memory, int max_elemnt_memory); + +#endif diff --git a/demo/apple/.gitignore b/demo/apple/.gitignore deleted file mode 100755 index 79d9331..0000000 --- a/demo/apple/.gitignore +++ /dev/null @@ -1,53 +0,0 @@ -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## Build generated -build/ -DerivedData - -## Various settings -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata - -## Other -*.xccheckout -*.moved-aside -*.xcuserstate -*.xcscmblueprint - -## Obj-C/Swift specific -*.hmap -*.ipa - -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# Pods/ - -# Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. -# Carthage/Checkouts - -Carthage/Build - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md - -fastlane/report.xml -fastlane/screenshots diff --git a/demo/apple/Mac_GL.xcodeproj/project.pbxproj b/demo/apple/Mac_GL.xcodeproj/project.pbxproj deleted file mode 100644 index 63a9935..0000000 --- a/demo/apple/Mac_GL.xcodeproj/project.pbxproj +++ /dev/null @@ -1,293 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 500B20241C4B41870002B53F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B20231C4B41870002B53F /* main.m */; }; - 500B20401C4B43930002B53F /* ZahnradBackend.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B203F1C4B43930002B53F /* ZahnradBackend.m */; }; - 500B20421C4B43A30002B53F /* zahnrad.c in Sources */ = {isa = PBXBuildFile; fileRef = 500B20411C4B43A30002B53F /* zahnrad.c */; }; - 500B20481C4B43BC0002B53F /* DroidSans.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20441C4B43BC0002B53F /* DroidSans.ttf */; }; - 500B20491C4B43BC0002B53F /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20451C4B43BC0002B53F /* Roboto-Bold.ttf */; }; - 500B204A1C4B43BC0002B53F /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20461C4B43BC0002B53F /* Roboto-Light.ttf */; }; - 500B204B1C4B43BC0002B53F /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20471C4B43BC0002B53F /* Roboto-Regular.ttf */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 500B201C1C4B41870002B53F /* Mac_GL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mac_GL.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 500B20231C4B41870002B53F /* main.m */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; tabWidth = 4; }; - 500B202A1C4B41880002B53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 500B203E1C4B43930002B53F /* ZahnradBackend.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = ZahnradBackend.h; sourceTree = SOURCE_ROOT; tabWidth = 4; }; - 500B203F1C4B43930002B53F /* ZahnradBackend.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = ZahnradBackend.m; sourceTree = SOURCE_ROOT; tabWidth = 4; }; - 500B20411C4B43A30002B53F /* zahnrad.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; name = zahnrad.c; path = ../../../zahnrad.c; sourceTree = ""; tabWidth = 5; }; - 500B20441C4B43BC0002B53F /* DroidSans.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = DroidSans.ttf; sourceTree = ""; }; - 500B20451C4B43BC0002B53F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = ""; }; - 500B20461C4B43BC0002B53F /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = ""; }; - 500B20471C4B43BC0002B53F /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; }; - 504DFDEA1C4EA2CD00D3714A /* zahnrad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zahnrad.h; path = ../../../zahnrad.h; sourceTree = ""; }; - 504DFDED1C4EA34800D3714A /* demo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demo.c; path = ../../demo.c; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 500B20191C4B41870002B53F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 500B20131C4B41870002B53F = { - isa = PBXGroup; - children = ( - 500B201E1C4B41870002B53F /* Mac_GL */, - 500B20431C4B43BC0002B53F /* font */, - 500B201D1C4B41870002B53F /* Products */, - ); - sourceTree = ""; - }; - 500B201D1C4B41870002B53F /* Products */ = { - isa = PBXGroup; - children = ( - 500B201C1C4B41870002B53F /* Mac_GL.app */, - ); - name = Products; - sourceTree = ""; - }; - 500B201E1C4B41870002B53F /* Mac_GL */ = { - isa = PBXGroup; - children = ( - 504DFDED1C4EA34800D3714A /* demo.c */, - 500B203E1C4B43930002B53F /* ZahnradBackend.h */, - 500B203F1C4B43930002B53F /* ZahnradBackend.m */, - 500B20231C4B41870002B53F /* main.m */, - 504DFDEA1C4EA2CD00D3714A /* zahnrad.h */, - 500B20411C4B43A30002B53F /* zahnrad.c */, - 500B202A1C4B41880002B53F /* Info.plist */, - ); - path = Mac_GL; - sourceTree = ""; - }; - 500B20431C4B43BC0002B53F /* font */ = { - isa = PBXGroup; - children = ( - 500B20441C4B43BC0002B53F /* DroidSans.ttf */, - 500B20451C4B43BC0002B53F /* Roboto-Bold.ttf */, - 500B20461C4B43BC0002B53F /* Roboto-Light.ttf */, - 500B20471C4B43BC0002B53F /* Roboto-Regular.ttf */, - ); - name = font; - path = ../../font; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 500B201B1C4B41870002B53F /* Mac_GL */ = { - isa = PBXNativeTarget; - buildConfigurationList = 500B202D1C4B41880002B53F /* Build configuration list for PBXNativeTarget "Mac_GL" */; - buildPhases = ( - 500B20181C4B41870002B53F /* Sources */, - 500B20191C4B41870002B53F /* Frameworks */, - 500B201A1C4B41870002B53F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Mac_GL; - productName = Mac_GL; - productReference = 500B201C1C4B41870002B53F /* Mac_GL.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 500B20141C4B41870002B53F /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0720; - ORGANIZATIONNAME = richi; - TargetAttributes = { - 500B201B1C4B41870002B53F = { - CreatedOnToolsVersion = 7.2; - }; - }; - }; - buildConfigurationList = 500B20171C4B41870002B53F /* Build configuration list for PBXProject "Mac_GL" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 500B20131C4B41870002B53F; - productRefGroup = 500B201D1C4B41870002B53F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 500B201B1C4B41870002B53F /* Mac_GL */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 500B201A1C4B41870002B53F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 500B20491C4B43BC0002B53F /* Roboto-Bold.ttf in Resources */, - 500B20481C4B43BC0002B53F /* DroidSans.ttf in Resources */, - 500B204B1C4B43BC0002B53F /* Roboto-Regular.ttf in Resources */, - 500B204A1C4B43BC0002B53F /* Roboto-Light.ttf in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 500B20181C4B41870002B53F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 500B20241C4B41870002B53F /* main.m in Sources */, - 500B20421C4B43A30002B53F /* zahnrad.c in Sources */, - 500B20401C4B43930002B53F /* ZahnradBackend.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 500B202B1C4B41880002B53F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - }; - name = Debug; - }; - 500B202C1C4B41880002B53F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - }; - name = Release; - }; - 500B202E1C4B41880002B53F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Mac_GL/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.Mac-GL"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 500B202F1C4B41880002B53F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Mac_GL/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.Mac-GL"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 500B20171C4B41870002B53F /* Build configuration list for PBXProject "Mac_GL" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 500B202B1C4B41880002B53F /* Debug */, - 500B202C1C4B41880002B53F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 500B202D1C4B41880002B53F /* Build configuration list for PBXNativeTarget "Mac_GL" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 500B202E1C4B41880002B53F /* Debug */, - 500B202F1C4B41880002B53F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 500B20141C4B41870002B53F /* Project object */; -} diff --git a/demo/apple/Mac_GL.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/demo/apple/Mac_GL.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index da62683..0000000 --- a/demo/apple/Mac_GL.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/demo/apple/Mac_GL/Info.plist b/demo/apple/Mac_GL/Info.plist deleted file mode 100644 index 0019db9..0000000 --- a/demo/apple/Mac_GL/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - Copyright © 2016 richi. All rights reserved. - NSPrincipalClass - NSApplication - - diff --git a/demo/apple/Mac_GL/main.m b/demo/apple/Mac_GL/main.m deleted file mode 100644 index 4eee973..0000000 --- a/demo/apple/Mac_GL/main.m +++ /dev/null @@ -1,400 +0,0 @@ -/* - Copyright (c) 2016 richi - - 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. - */ - - -#import "ZahnradBackend.h" - - -@interface ZahnradView : NSOpenGLView - -@end - - -@implementation ZahnradView -{ - ZahnradBackend* zr; - - NSTrackingArea* currentTrackingArea; - CVDisplayLinkRef displayLink; -} - - -#pragma mark - Setup - - - -- (instancetype) initWithFrame: (NSRect) frameRect -{ - NSOpenGLPixelFormatAttribute pixelFormatAttributes[] = { - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAAccelerated, - NSOpenGLPFANoRecovery, - 0 - }; - - NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: pixelFormatAttributes]; - assert(self = [super initWithFrame: frameRect pixelFormat: pixelFormat]); - - [self updateTrackingArea]; - - [[self openGLContext] makeCurrentContext]; - zr = [[ZahnradBackend alloc] initWithView: self]; - - return self; -} - - -#if !SIMULATE_TOUCH - - -static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) -{ - return [(__bridge ZahnradView*) displayLinkContext drawFrameForTime: outputTime]; -} - - -- (void) prepareOpenGL -{ - // Synchronize buffer swaps with vertical refresh rate - GLint swapInt = 1; - [[self openGLContext] setValues: &swapInt forParameter: NSOpenGLCPSwapInterval]; - - // Create a display link capable of being used with all active displays - CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); - - // Set the renderer output callback function - CVDisplayLinkSetOutputCallback(displayLink, &displayLinkCallback, (__bridge void* _Nullable)(self)); - - // Set the display link for the current renderer - CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; - CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; - CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); - - // Activate the display link - CVDisplayLinkStart(displayLink); -} - - -#endif - - -- (void) glLocked: (dispatch_block_t) block -{ - CGLLockContext(self.openGLContext.CGLContextObj); - - @try - { - block(); - } - @catch (NSException *exception) - { - NSLog(@"%@", exception); - } - @finally - { - CGLUnlockContext(self.openGLContext.CGLContextObj); - } -} - - -#pragma mark - Drawing - - - -- (void) drawFrame -{ - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - [zr updateFrame]; - [zr drawFrameWithScale: 1.0 width: self.bounds.size.width height: self.bounds.size.height]; - - [[self openGLContext] flushBuffer]; -} - - -- (CVReturn) drawFrameForTime: (const CVTimeStamp*) outputTime -{ - @autoreleasepool - { - [self glLocked: ^{ - [[self openGLContext] makeCurrentContext]; - [self drawFrame]; - }]; - } - - return kCVReturnSuccess; -} - - -- (void) drawRect: (NSRect) dirtyRect -{ - [self glLocked: ^{ - [[self openGLContext] makeCurrentContext]; - [super drawRect: dirtyRect]; - [self drawFrame]; - }]; -} - - -#pragma mark - User Input - - - -- (void) addEvent: (NSDictionary*) event -{ - [self glLocked: ^{ - [zr addEvent: event]; - }]; - -#if SIMULATE_TOUCH - - [self setNeedsDisplay: YES]; - -#endif -} - - -- (void) mouseMoved: (NSEvent*) theEvent -{ -#if !SIMULATE_TOUCH - - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @1, @"pos" : NSStringFromPoint(location)}]; - -#endif -} - - -- (void) mouseDragged: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @2, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) rightMouseDragged: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @3, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) otherMouseDragged: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @4, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) mouseDown: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @5, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) mouseUp: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @6, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) rightMouseDown: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @7, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) rightMouseUp: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @8, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) otherMouseDown: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @9, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) otherMouseUp: (NSEvent*) theEvent -{ - NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; - [self addEvent: @{@"type" : @10, @"pos" : NSStringFromPoint(location)}]; -} - - -- (void) scrollWheel: (NSEvent*) theEvent -{ - [self addEvent: @{@"type" : @11, @"deltaX" : @(theEvent.deltaX), @"deltaY" : @(theEvent.deltaY)}]; -} - - -- (void) keyDown: (NSEvent*) theEvent -{ - NSString* str = theEvent.characters; - - if (str.length) - [self addEvent: @{@"type" : @12, @"txt" : str, @"mod" : @(theEvent.modifierFlags)}]; -} - - -- (void) keyUp: (NSEvent*) theEvent -{ - NSString* str = theEvent.characters; - - if (str.length) - [self addEvent: @{@"type" : @13, @"txt" : str, @"mod" : @(theEvent.modifierFlags)}]; -} - - -- (BOOL) acceptsFirstResponder -{ - return YES; -} - - -- (void) removeCurrentTrackingArea -{ - if (currentTrackingArea) - { - [self removeTrackingArea: currentTrackingArea]; - currentTrackingArea = nil; - } -} - - -- (void) updateTrackingArea -{ - [self removeCurrentTrackingArea]; - -#if !SIMULATE_TOUCH - - currentTrackingArea = [[NSTrackingArea alloc] - initWithRect: self.bounds - options: NSTrackingMouseMoved | NSTrackingActiveInActiveApp - owner: self - userInfo: nil]; - - [self addTrackingArea: currentTrackingArea]; - -#endif -} - - -- (void) reshape -{ - [self updateTrackingArea]; - NSRect bounds = [self bounds]; - - [self glLocked: ^{ - [[self openGLContext] makeCurrentContext]; - glViewport((GLint)NSMinX(bounds), (GLint)NSMinY(bounds), (GLint)NSWidth(bounds), (GLint)NSHeight(bounds)); - }]; -} - - -- (void) update -{ - [self glLocked: ^{ - [super update]; - }]; -} - - -@end - - -#pragma mark - App Delegate - - - -@interface AppDelegate : NSObject - -@property (strong) IBOutlet NSWindow* window; - -@end - - -@implementation AppDelegate - - -- (void) applicationDidFinishLaunching: (NSNotification*) aNotification -{ - NSString* appName = [[NSProcessInfo processInfo] processName]; - - NSLog(@"Starting %@", appName); - - NSMenu* appMenu = [NSMenu new]; - [appMenu addItem: [[NSMenuItem alloc] initWithTitle: [@"Quit " stringByAppendingString: appName] - action: @selector(terminate:) - keyEquivalent: @"q"]]; - NSMenuItem* appMenuItem = [NSMenuItem new]; - [appMenuItem setSubmenu: appMenu]; - NSMenu* menubar = [NSMenu new]; - [menubar addItem: appMenuItem]; - [NSApp setMainMenu: menubar]; - - NSRect frame = NSMakeRect(0, 0, 800, 600); - - _window = [[NSWindow alloc] initWithContentRect: frame - styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask - backing: NSBackingStoreBuffered - defer: NO]; - - [_window setContentView: [[ZahnradView alloc] initWithFrame: frame]]; - [_window setDelegate: self]; - - [_window setTitle: appName]; - [_window cascadeTopLeftFromPoint: NSMakePoint(20, 20)]; - [_window makeKeyAndOrderFront: nil]; -} - - -- (void) windowWillClose: (NSNotification*) notification -{ - [NSApp terminate: self]; -} - - -@end - - -int main() -{ - @autoreleasepool - { - [NSApplication sharedApplication]; - [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular]; - - AppDelegate* appDelegate = [AppDelegate new]; - [NSApp setDelegate: appDelegate]; - - [NSApp run]; - } - - return EXIT_SUCCESS; -} - - diff --git a/demo/apple/README.md b/demo/apple/README.md deleted file mode 100755 index 34396e2..0000000 --- a/demo/apple/README.md +++ /dev/null @@ -1,19 +0,0 @@ -Zahnrad for Mac OS X, iOS and tvOS -================================== - -This is a bunch of experimental render backends for [Zahnrad](https://github.com/vurtun/zahnrad) a gorgeous immediate mode GUI toolkit. - -Included are native versions for Mac OS X, iOS and tvOS. The Mac version is using OpenGL 3.2. iOS and tvOS are running on OpenGL ES 3.2. - -Versions utilising Metal are in the making. - -How to use? ------------ - -Open `ZahnradBackend.xcworkspace` with XCode and start your engines. - - -License -------- - -[WTFPL](http://www.wtfpl.net) \ No newline at end of file diff --git a/demo/apple/ZahnradBackend.h b/demo/apple/ZahnradBackend.h deleted file mode 100644 index 036e9ad..0000000 --- a/demo/apple/ZahnradBackend.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright (c) 2016 Micha Mettke - Macintosh / Objective-C adaptation (c) 2016 richi - - 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. - */ - -#import - -#import "zahnrad.h" - -#define SIMULATE_TOUCH 0 - -#if (TARGET_OS_IPHONE && (!TARGET_OS_TV)) || SIMULATE_TOUCH - #define ZR_REFRESH_ON_EVENT_ONLY 0 - #define ZR_TOUCH_SCREEN 1 -#endif - -#if TARGET_OS_IPHONE - #import - #import - #import - #import - #define GLVIEW GLKView -#else - #import - #import - #import - #import - #define GLVIEW NSOpenGLView -#endif - - -@interface ZahnradBackend : NSObject - - -@property (readonly) GLVIEW* view; - -- (instancetype) initWithView: (GLVIEW*) view; - -- (void) addEvent: (NSDictionary*) event; -- (void) updateFrame; -- (void) drawFrameWithScale: (CGFloat) scale width: (CGFloat) width height: (CGFloat) height; - -@end - - -@interface ZahnradBackend (Adapter) - - -- (struct zr_context*) createContextWithBuffer: (struct zr_buffer*) cmds - allocator: (struct zr_allocator*) alloc - defaultFont: (struct zr_user_font*) defFont; - -- (int) fillFrame; - - -@end - - -void zr_backend_show_keyboard(zr_hash hash, struct zr_rect bounds, struct zr_buffer* text); -void zr_backend_hide_keyboard(void); - -int zr_touch_edit_string(struct zr_context *ctx, zr_flags flags, char *text, zr_size *len, zr_size max, zr_filter filter, zr_hash unique_id); - - - diff --git a/demo/apple/ZahnradBackend.m b/demo/apple/ZahnradBackend.m deleted file mode 100644 index 746dcf1..0000000 --- a/demo/apple/ZahnradBackend.m +++ /dev/null @@ -1,767 +0,0 @@ -/* - Copyright (c) 2016 Micha Mettke - Macintosh / Objective-C adaptation (c) 2016 richi - - 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. - */ - -#import "ZahnradBackend.h" - - -#define MAX_VERTEX_MEMORY (512 * 1024) -#define MAX_ELEMENT_MEMORY (128 * 1024) - - -// This is the adapter for demo.c. -// #else does provide a generic version to start -// your own project. - - -#if 1 - -#if TARGET_OS_IPHONE - -#define zr_edit_string(_ct, _fl, _bu, _le, _ma, _fi) zr_touch_edit_string(_ct, _fl, _bu, _le, _ma, _fi, (zr_hash)__COUNTER__) - -#endif - - -#define DEMO_DO_NOT_DRAW_IMAGES -#include "../demo.c" - -@implementation ZahnradBackend (Adapter) - -static struct demo gui; - - -- (struct zr_context*) createContextWithBuffer: (struct zr_buffer*) cmds - allocator: (struct zr_allocator*) alloc - defaultFont: (struct zr_user_font*) defFont -{ - memset(&gui, 0, sizeof(gui)); - zr_buffer_init(cmds, alloc, 4096); - zr_init(&gui.ctx, alloc, defFont); - return &gui.ctx; -} - - -- (int) fillFrame; -{ - return run_demo(&gui); -} - - -@end - - -#else - - -#import "ZahnradBackend.h" - - -@implementation ZahnradBackend (Adapter) - -static struct zr_context context; - - -- (struct zr_context*) createContextWithBuffer: (struct zr_buffer*) cmds - allocator: (struct zr_allocator*) alloc - defaultFont: (struct zr_user_font*) defFont -{ - memset(&context, 0, sizeof(context)); - - zr_buffer_init(cmds, alloc, 4096); - zr_init(&context, alloc, defFont); - - return &context; -} - - -- (int) fillFrame; -{ - return 1; -} - -@end - - -#endif - - -@implementation ZahnradBackend -{ - struct zr_context* context; - - struct zr_allocator alloc; - struct zr_user_font sysFnt; - struct zr_font font; - struct zr_draw_null_texture nullTexture; - - struct zr_buffer cmds; - NSMutableArray* bufferedCommands; - NSMutableArray* events; - - struct - { - GLuint vbo, vao, ebo; - GLuint program; - GLuint vertexShader; - GLuint fragmentShader; - GLint attributePosition; - GLint attributeUV; - GLint attributeColor; - GLint uniformTexture; - GLint uniformProjection; - GLuint fontTexture; - } device; -} - - -static void* mem_alloc(zr_handle unused, size_t size) -{ - return calloc(1, size); -} - - -static void mem_free(zr_handle unused, void* ptr) -{ - free(ptr); -} - - -#pragma mark - Setup - - - -- (instancetype) initWithView: (GLVIEW*) view; -{ - if (!(self = [super init])) return self; - - _view = view; - - events = [NSMutableArray new]; - bufferedCommands = [NSMutableArray new]; - - [self setupGL]; - - NSURL* fontURL = [[NSBundle mainBundle] URLForResource: @"DroidSans" withExtension: @"ttf"]; - assert(fontURL != nil); - - alloc.userdata.ptr = NULL; - alloc.alloc = mem_alloc; - alloc.free = mem_free; - - sysFnt = [self fontFromUrl: fontURL height: 14 range: zr_font_default_glyph_ranges()]; - - context = [self createContextWithBuffer: &cmds allocator: &alloc defaultFont: &sysFnt]; - [self fillFrame]; - - return self; -} - - -- (void) setupGL -{ - GLint status; - - static const GLchar* vss = -#if TARGET_OS_IPHONE - "#version 300 es\n" -#else - "#version 150\n" -#endif - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 TexCoord;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "void main() {\n" - " Frag_UV = TexCoord;\n" - " Frag_Color = Color;\n" - " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" - "}\n"; - - static const GLchar* fss = -#if TARGET_OS_IPHONE - "#version 300 es\n" -#else - "#version 150\n" -#endif - "precision mediump float;\n" - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main(){\n" - " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" - "}\n"; - - device.program = glCreateProgram(); - device.vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(device.vertexShader, 1, &vss, 0); - glCompileShader(device.vertexShader); - -#if defined(DEBUG) - GLint logLength; - glGetShaderiv(device.vertexShader, GL_INFO_LOG_LENGTH, &logLength); - if (logLength > 0) - { - GLchar* log = (GLchar*)malloc(logLength); - glGetShaderInfoLog(device.vertexShader, logLength, &logLength, log); - NSLog(@"Shader compile log:\n%s", log); - free(log); - } -#endif - - glGetShaderiv(device.vertexShader, GL_COMPILE_STATUS, &status); - assert(status == GL_TRUE); - - device.fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(device.fragmentShader, 1, &fss, 0); - glCompileShader(device.fragmentShader); - -#if defined(DEBUG) - glGetShaderiv(device.fragmentShader, GL_INFO_LOG_LENGTH, &logLength); - if (logLength > 0) - { - GLchar* log = (GLchar*)malloc(logLength); - glGetShaderInfoLog(device.fragmentShader, logLength, &logLength, log); - NSLog(@"Shader compile log:\n%s", log); - free(log); - } -#endif - - glGetShaderiv(device.fragmentShader, GL_COMPILE_STATUS, &status); - assert(status == GL_TRUE); - - glAttachShader(device.program, device.vertexShader); - glAttachShader(device.program, device.fragmentShader); - glLinkProgram(device.program); - -#if defined(DEBUG) - glGetProgramiv(device.program, GL_INFO_LOG_LENGTH, &logLength); - if (logLength > 0) - { - GLchar* log = (GLchar*)malloc(logLength); - glGetProgramInfoLog(device.program, logLength, &logLength, log); - NSLog(@"Program link log:\n%s", log); - free(log); - } -#endif - - glGetProgramiv(device.program, GL_LINK_STATUS, &status); - assert(status == GL_TRUE); - - device.uniformTexture = glGetUniformLocation(device.program, "Texture"); - device.uniformProjection = glGetUniformLocation(device.program, "ProjMtx"); - device.attributePosition = glGetAttribLocation(device.program, "Position"); - device.attributeUV = glGetAttribLocation(device.program, "TexCoord"); - device.attributeColor = glGetAttribLocation(device.program, "Color"); - - // buffer setup - GLsizei vs = sizeof(struct zr_draw_vertex); - size_t vp = offsetof(struct zr_draw_vertex, position); - size_t vt = offsetof(struct zr_draw_vertex, uv); - size_t vc = offsetof(struct zr_draw_vertex, col); - - // allocate vertex and element buffer - glGenBuffers(1, &device.vbo); - glGenBuffers(1, &device.ebo); - glGenVertexArrays(1, &device.vao); - - glBindVertexArray(device.vao); - glBindBuffer(GL_ARRAY_BUFFER, device.vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, device.ebo); - - glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); - - glEnableVertexAttribArray((GLuint)device.attributePosition); - glEnableVertexAttribArray((GLuint)device.attributeUV); - glEnableVertexAttribArray((GLuint)device.attributeColor); - - glVertexAttribPointer((GLuint)device.attributePosition, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); - glVertexAttribPointer((GLuint)device.attributeUV, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); - glVertexAttribPointer((GLuint)device.attributeColor, 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); - glBindVertexArray(0); -} - - -- (struct zr_user_font) fontFromUrl: (NSURL*) url height: (unsigned) fontHeight range: (const zr_rune*) range -{ - struct zr_baked_font baked_font; - struct zr_recti custom; - - memset(&baked_font, 0, sizeof(baked_font)); - memset(&custom, 0, sizeof(custom)); - - // bake and upload font texture - NSData* ttfData = [NSData dataWithContentsOfURL: url]; - assert(ttfData != nil); - - // setup font configuration - struct zr_font_config config; - memset(&config, 0, sizeof(config)); - config.ttf_blob = (void*)ttfData.bytes; - config.font = &baked_font; - config.coord_type = ZR_COORD_UV; - config.range = range; - config.pixel_snap = zr_false; - config.size = (float)fontHeight; - config.spacing = zr_vec2(0, 0); - config.oversample_h = 1; - config.oversample_v = 1; - config.merge_mode = 0; - - // query needed amount of memory for the font baking process - int glyph_count; - size_t tmp_size; - zr_font_bake_memory(&tmp_size, &glyph_count, &config, 1); - struct zr_font_glyph* glyphes = (struct zr_font_glyph*)calloc(sizeof(struct zr_font_glyph), (size_t)glyph_count); - void* tmp = calloc(1, tmp_size); - - // pack all glyphes and return needed image width, height and memory size - int img_width, img_height; - size_t img_size; - custom.w = 2; custom.h = 2; - assert(zr_font_bake_pack(&img_size, &img_width, &img_height, &custom, tmp, tmp_size, &config, 1)); - - // bake all glyphes and custom white pixel into image - const char* custom_data = "...."; - void* img = calloc(1, img_size); - zr_font_bake(img, img_width, img_height, tmp, tmp_size, glyphes, glyph_count, &config, 1); - zr_font_bake_custom_data(img, img_width, img_height, custom, custom_data, 2, 2, '.', 'X'); - - // convert alpha8 image into rgba8 image - void* img_rgba = calloc(4, (size_t)(img_height * img_width)); - zr_font_bake_convert(img_rgba, img_width, img_height, img); - free(img); - img = img_rgba; - - // upload baked font image - glGenTextures(1, &device.fontTexture); - glBindTexture(GL_TEXTURE_2D, device.fontTexture); - 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)img_width, (GLsizei)img_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img); - - free(tmp); - free(img); - - // default white pixel in a texture which is needed to draw primitives - nullTexture.texture.id = (int)device.fontTexture; - nullTexture.uv = zr_vec2((custom.x + 0.5f) / (float)img_width, (custom.y + 0.5f) / (float)img_height); - - /* setup font with glyphes. IMPORTANT: the font only references the glyphes - this was done to have the possibility to have multible fonts with one - total glyph array. Not quite sure if it is a good thing since the - glyphes have to be freed as well. - */ - zr_font_init(&font, (float)fontHeight, '?', glyphes, &baked_font, nullTexture.texture); - - return &font.handle; -} - - -#pragma mark - Drawing - - - -- (void) drawFrameWithScale: (CGFloat) scale width: (CGFloat) width height: (CGFloat) height -{ - // save opengl state - GLint last_prog, last_tex; - GLint last_ebo, last_vbo, last_vao; - glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); - - // 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 - 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] /= width; - ortho[1][1] /= height; - glUseProgram(device.program); - glUniform1i(device.uniformTexture, 0); - glUniformMatrix4fv(device.uniformProjection, 1, GL_FALSE, &ortho[0][0]); - - // activate vertex and element buffer - glBindVertexArray(device.vao); - glBindBuffer(GL_ARRAY_BUFFER, device.vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, device.ebo); - - // if draw commands have been buffered we will draw only those - if (bufferedCommands.count) - { - const struct zr_draw_command* cmd; - const zr_draw_index* offset = NULL; - - // iterate over and execute each draw command - for (NSData* data in bufferedCommands) - { - cmd = data.bytes; - glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); - glScissor((GLint)(cmd->clip_rect.x * scale), - (GLint)((height - (cmd->clip_rect.y + cmd->clip_rect.h)) * scale), - (GLint)(cmd->clip_rect.w * scale), - (GLint)(cmd->clip_rect.h * scale)); - glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); - offset += cmd->elem_count; - } - } - else - { - // load draw vertices & elements into vertex + element buffer - void* vertices = glMapBufferRange(GL_ARRAY_BUFFER, 0, MAX_VERTEX_MEMORY, GL_MAP_WRITE_BIT); - void* elements = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, MAX_ELEMENT_MEMORY, GL_MAP_WRITE_BIT); - // - // fill converting configuration - struct zr_convert_config config; - memset(&config, 0, sizeof(config)); - config.global_alpha = 1.0f; - config.shape_AA = ZR_ANTI_ALIASING_ON; - config.line_AA = ZR_ANTI_ALIASING_ON; - config.circle_segment_count = 22; - config.curve_segment_count = 22; - config.arc_segment_count = 22; - config.null = nullTexture; - // - // setup buffers to load vertices and elements - struct zr_buffer vbuf, ebuf; - zr_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); - zr_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); - zr_convert(context, &cmds, &vbuf, &ebuf, &config); - - glUnmapBuffer(GL_ARRAY_BUFFER); - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - // - // convert from command queue into draw list and draw to screen - const struct zr_draw_command* cmd; - const zr_draw_index* offset = NULL; - [bufferedCommands removeAllObjects]; - // iterate over and execute each draw command - zr_draw_foreach(cmd, context, &cmds) - { - if (cmd->elem_count > 0) - { -#if ZR_REFRESH_ON_EVENT_ONLY - [bufferedCommands addObject: [NSData dataWithBytes: cmd length: sizeof(struct zr_draw_command)]]; -#endif - glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); - glScissor((GLint)(cmd->clip_rect.x * scale), - (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale), - (GLint)(cmd->clip_rect.w * scale), - (GLint)(cmd->clip_rect.h * scale)); - glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); - offset += cmd->elem_count; - } - } - zr_clear(context); - } - // restore old state - glUseProgram((GLuint)last_prog); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); - glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); - glBindVertexArray((GLuint)last_vao); - glDisable(GL_SCISSOR_TEST); -} - - -#pragma mark - Events - - - -- (void) updateFrame -{ - -#if ZR_REFRESH_ON_EVENT_ONLY - if (events.count) -#endif - { - [self handleEvents]; - [self fillFrame]; - } -} - - -- (void) handleEvents -{ - NSArray* currentEvents = events.copy; - [events removeAllObjects]; - - zr_input_begin(context); - - for (NSDictionary* event in currentEvents) - { -#if TARGET_OS_IPHONE - CGPoint location = CGPointFromString(event[@"pos"]); -#else - NSPoint location = NSPointFromString(event[@"pos"]); - location.y = (int)(self.view.bounds.size.height - location.y); -#endif - - int x = (int)location.x; - int y = (int)location.y; - int type = [event[@"type"] intValue]; - - switch (type) - { - -#if ZR_REFRESH_ON_EVENT_ONLY || ZR_TOUCH_SCREEN - - case 2: case 3: case 4: - zr_input_motion(context, x, y); - break; - case 5: - zr_input_motion(context, x, y); - zr_input_end(context); - zr_input_begin(context); - zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_true); - break; - case 6: - zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_false); - zr_input_end(context); - [self fillFrame]; - zr_clear(context); - zr_input_begin(context); - zr_input_motion(context, INT_MAX, INT_MAX); - break; - -#else - - case 1: case 2: case 3: case 4: - zr_input_motion(context, x, y); - break; - case 5: - zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_true); - break; - case 6: - zr_input_button(context, ZR_BUTTON_LEFT, x, y, zr_false); - break; - case 7: - zr_input_button(context, ZR_BUTTON_RIGHT, x, y, zr_true); - break; - case 8: - zr_input_button(context, ZR_BUTTON_RIGHT, x, y, zr_false); - break; - case 9: - zr_input_button(context, ZR_BUTTON_MIDDLE, x, y, zr_true); - break; - case 10: - zr_input_button(context, ZR_BUTTON_MIDDLE, x, y, zr_false); - break; - case 11: - zr_input_scroll(context, -[event[@"deltaY"] floatValue]); - break; -#endif - - case 12: - case 13: - { - int down = type == 12 ? zr_true : zr_false; - NSString* str = event[@"txt"]; - NSData* data = [str dataUsingEncoding: NSUTF32LittleEndianStringEncoding]; - const uint32_t* c = data.bytes; - NSInteger n = data.length / sizeof(uint32_t); - -#if TARGET_OS_IPHONE - - if (down) - { - if (n && *c == '\b') - { - zr_input_key(context, ZR_KEY_BACKSPACE, zr_true); - zr_input_end(context); - [self fillFrame]; - zr_clear(context); - zr_input_begin(context); - zr_input_key(context, ZR_KEY_BACKSPACE, zr_false); - } - else - { - for (NSInteger i = 0; i < n; i += 1, c += 1) - zr_input_unicode(context, *c); - } - } - -#else - - for (NSInteger i = 0; i < n; i += 1, c += 1) - { - if (*c == 127 || *c < ' ' || *c >= NSUpArrowFunctionKey) - { - switch (*c) - { - case NSLeftArrowFunctionKey: - zr_input_key(context, ZR_KEY_LEFT, down); - break; - case NSRightArrowFunctionKey: - zr_input_key(context, ZR_KEY_RIGHT, down); - break; - case 3: - zr_input_key(context, ZR_KEY_COPY, down); - break; - case 22: - zr_input_key(context, ZR_KEY_PASTE, down); - break; - case 24: - zr_input_key(context, ZR_KEY_CUT, down); - break; - case 9: - zr_input_key(context, ZR_KEY_TAB, down); - break; - case 13: - zr_input_key(context, ZR_KEY_ENTER, down); - break; - case 127: - zr_input_key(context, ZR_KEY_BACKSPACE, down); - break; - default: - break; - } - } - else if (down) - { - zr_input_unicode(context, *c); - } - } -#endif - } - break; - - default: - break; - } - } - - zr_input_end(context); -} - - -- (void) addEvent: (NSDictionary*) event -{ - [bufferedCommands removeAllObjects]; - [events addObject: event]; -} - - -@end - - -@protocol ZahnradKeyboardDelegate - -- (void) showKeyboard: (NSDictionary*) info; -- (void) hideKeyboard; - -@end - - -#undef zr_edit_string - - -#if TARGET_OS_IPHONE - - -void zr_backend_show_keyboard(zr_hash zrHash, struct zr_rect zrBounds, struct zr_buffer* zrText) -{ - CGRect frame = CGRectMake(zrBounds.x, zrBounds.y, zrBounds.w, zrBounds.h); - id ad = [[UIApplication sharedApplication] delegate]; - if ([ad respondsToSelector: @selector(showKeyboard:)]) - { - char str[zrText->allocated + 1]; - strncpy(str, zrText->memory.ptr, zrText->allocated); - str[zrText->allocated] = 0; - NSString* text = [NSString stringWithCString: str encoding: NSUTF8StringEncoding]; - [ad performSelector: @selector(showKeyboard:) withObject: @{@"hash" : @(zrHash), @"text" : text, @"frame" : NSStringFromCGRect(frame)}]; - } -} - - -void zr_backend_hide_keyboard(void) -{ - id ad = [[UIApplication sharedApplication] delegate]; - if ([ad respondsToSelector: @selector(hideKeyboard)]) - [ad performSelector: @selector(hideKeyboard)]; -} - - -int zr_touch_edit_string(struct zr_context *ctx, zr_flags flags, char *text, zr_size *len, zr_size max, zr_filter filter, zr_hash unique_id) -{ - zr_flags state; - struct zr_rect bounds; - - zr_layout_peek(&bounds, ctx); - state = zr_edit_string(ctx, flags, text, len, max, filter); - if (state & ZR_EDIT_ACTIVATED) - { - struct zr_buffer buffer; - zr_buffer_init_fixed(&buffer, text, max); - buffer.allocated = *len; - - zr_backend_show_keyboard((zr_hash)unique_id, bounds, &buffer); - } - else if (state & ZR_EDIT_DEACTIVATED) - { - zr_backend_hide_keyboard(); - } - - return state; -} - - -#else - - -void zr_backend_show_keyboard(zr_hash zrHash, struct zr_rect zrBounds, struct zr_buffer* zrText) -{ -} - - -void zr_backend_hide_keyboard(void) -{ -} - - -int zr_touch_edit_string(struct zr_context *ctx, zr_flags flags, char *text, zr_size *len, zr_size max, zr_filter filter, zr_hash unique_id) -{ - return zr_edit_string(ctx, flags, text, len, max, filter); -} - - -#endif - - - diff --git a/demo/apple/ZahnradBackend.xcworkspace/contents.xcworkspacedata b/demo/apple/ZahnradBackend.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 86a441d..0000000 --- a/demo/apple/ZahnradBackend.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - diff --git a/demo/apple/iOS_CoreGraphics.xcodeproj/project.pbxproj b/demo/apple/iOS_CoreGraphics.xcodeproj/project.pbxproj deleted file mode 100644 index b433fa5..0000000 --- a/demo/apple/iOS_CoreGraphics.xcodeproj/project.pbxproj +++ /dev/null @@ -1,275 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 50F9C54A1C5FE56C009533F4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 50F9C5491C5FE56C009533F4 /* main.m */; }; - 50F9C5611C5FE7BD009533F4 /* zahnrad.c in Sources */ = {isa = PBXBuildFile; fileRef = 50F9C55F1C5FE7BD009533F4 /* zahnrad.c */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 50F9C5451C5FE56C009533F4 /* iOS_CoreGraphics.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOS_CoreGraphics.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 50F9C5491C5FE56C009533F4 /* main.m */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; tabWidth = 4; }; - 50F9C5591C5FE56C009533F4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 50F9C55F1C5FE7BD009533F4 /* zahnrad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = zahnrad.c; path = ../../../zahnrad.c; sourceTree = ""; }; - 50F9C5601C5FE7BD009533F4 /* zahnrad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zahnrad.h; path = ../../../zahnrad.h; sourceTree = ""; }; - 50F9C5621C5FE7D9009533F4 /* demo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demo.c; path = ../../demo.c; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 50F9C5421C5FE56C009533F4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 50F9C53C1C5FE56C009533F4 = { - isa = PBXGroup; - children = ( - 50F9C5471C5FE56C009533F4 /* iOS_CoreGraphics */, - 50F9C5461C5FE56C009533F4 /* Products */, - ); - sourceTree = ""; - }; - 50F9C5461C5FE56C009533F4 /* Products */ = { - isa = PBXGroup; - children = ( - 50F9C5451C5FE56C009533F4 /* iOS_CoreGraphics.app */, - ); - name = Products; - sourceTree = ""; - }; - 50F9C5471C5FE56C009533F4 /* iOS_CoreGraphics */ = { - isa = PBXGroup; - children = ( - 50F9C5621C5FE7D9009533F4 /* demo.c */, - 50F9C55F1C5FE7BD009533F4 /* zahnrad.c */, - 50F9C5601C5FE7BD009533F4 /* zahnrad.h */, - 50F9C5491C5FE56C009533F4 /* main.m */, - 50F9C5591C5FE56C009533F4 /* Info.plist */, - 50F9C5481C5FE56C009533F4 /* Supporting Files */, - ); - path = iOS_CoreGraphics; - sourceTree = ""; - }; - 50F9C5481C5FE56C009533F4 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 50F9C5441C5FE56C009533F4 /* iOS_CoreGraphics */ = { - isa = PBXNativeTarget; - buildConfigurationList = 50F9C55C1C5FE56C009533F4 /* Build configuration list for PBXNativeTarget "iOS_CoreGraphics" */; - buildPhases = ( - 50F9C5411C5FE56C009533F4 /* Sources */, - 50F9C5421C5FE56C009533F4 /* Frameworks */, - 50F9C5431C5FE56C009533F4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = iOS_CoreGraphics; - productName = iOS_CoreGraphics; - productReference = 50F9C5451C5FE56C009533F4 /* iOS_CoreGraphics.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 50F9C53D1C5FE56C009533F4 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0720; - ORGANIZATIONNAME = richi; - TargetAttributes = { - 50F9C5441C5FE56C009533F4 = { - CreatedOnToolsVersion = 7.2; - }; - }; - }; - buildConfigurationList = 50F9C5401C5FE56C009533F4 /* Build configuration list for PBXProject "iOS_CoreGraphics" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 50F9C53C1C5FE56C009533F4; - productRefGroup = 50F9C5461C5FE56C009533F4 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 50F9C5441C5FE56C009533F4 /* iOS_CoreGraphics */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 50F9C5431C5FE56C009533F4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 50F9C5411C5FE56C009533F4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 50F9C5611C5FE7BD009533F4 /* zahnrad.c in Sources */, - 50F9C54A1C5FE56C009533F4 /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 50F9C55A1C5FE56C009533F4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 50F9C55B1C5FE56C009533F4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 50F9C55D1C5FE56C009533F4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets"; - INFOPLIST_FILE = iOS_CoreGraphics/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.iOS-CoreGraphics"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 50F9C55E1C5FE56C009533F4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets"; - INFOPLIST_FILE = iOS_CoreGraphics/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.iOS-CoreGraphics"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 50F9C5401C5FE56C009533F4 /* Build configuration list for PBXProject "iOS_CoreGraphics" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 50F9C55A1C5FE56C009533F4 /* Debug */, - 50F9C55B1C5FE56C009533F4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 50F9C55C1C5FE56C009533F4 /* Build configuration list for PBXNativeTarget "iOS_CoreGraphics" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 50F9C55D1C5FE56C009533F4 /* Debug */, - 50F9C55E1C5FE56C009533F4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 50F9C53D1C5FE56C009533F4 /* Project object */; -} diff --git a/demo/apple/iOS_CoreGraphics.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/demo/apple/iOS_CoreGraphics.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 8471094..0000000 --- a/demo/apple/iOS_CoreGraphics.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/demo/apple/iOS_CoreGraphics/Info.plist b/demo/apple/iOS_CoreGraphics/Info.plist deleted file mode 100644 index ff71b22..0000000 --- a/demo/apple/iOS_CoreGraphics/Info.plist +++ /dev/null @@ -1,50 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIcons - - CFBundleIcons~ipad - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIRequiredDeviceCapabilities - - armv7 - - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/demo/apple/iOS_CoreGraphics/main.m b/demo/apple/iOS_CoreGraphics/main.m deleted file mode 100644 index cbe19d9..0000000 --- a/demo/apple/iOS_CoreGraphics/main.m +++ /dev/null @@ -1,341 +0,0 @@ -/* - Copyright (c) 2016 richi - - 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. - */ - - -#import - -#define DEMO_DO_NOT_DRAW_IMAGES -#include "zahnrad.h" -#include "../../demo.c" - - -static void* mem_alloc(zr_handle unused, size_t size) -{ - return calloc(1, size); -} - - -static void mem_free(zr_handle unused, void* ptr) -{ - free(ptr); -} - - -NSString* stringFromZR(const char* text, zr_size len) -{ - char cstr[len + 1]; - strncpy(cstr, text, len); - cstr[len] = 0; - return [NSString stringWithUTF8String: cstr]; -} - - -static zr_size fontTextWidth(zr_handle handle, float height, const char* text, zr_size len) -{ - UIFont* font = (__bridge UIFont*) handle.ptr; - NSString* aString = stringFromZR(text, len); - NSDictionary* attributes = @{ NSFontAttributeName: font}; - CGSize stringSize = [aString sizeWithAttributes: attributes]; - - return ceil(stringSize.width); -} - - -#pragma mark - ZRView - - - -@interface ZRView : UIView -{ - UIFont* font; - struct zr_user_font usrfnt; - struct zr_allocator alloc; - - struct demo gui; -} - -@end - - -@implementation ZRView - - -- (instancetype) initWithFrame: (CGRect) frame -{ - if (!(self = [super initWithFrame: frame])) - return self; - - alloc.userdata.ptr = NULL; - alloc.alloc = mem_alloc; - alloc.free = mem_free; - - font = [UIFont systemFontOfSize: 11]; - font = [UIFont fontWithName: @"Menlo-Regular" size: 11]; - - NSDictionary* attributes = @{NSFontAttributeName: font}; - CGSize stringSize = [@"W" sizeWithAttributes: attributes]; - - memset(&usrfnt, 0, sizeof(usrfnt)); - usrfnt.height = round(stringSize.height); - usrfnt.width = fontTextWidth; - usrfnt.userdata.ptr = (__bridge void*)(font); - - zr_init(&gui.ctx, &alloc, &usrfnt); - zr_clear(&gui.ctx); - run_demo(&gui); - - return self; -} - - -#define setColor(_c) \ -CGContextSetRGBStrokeColor(context, _c->color.r / 255.0, _c->color.g / 255.0, _c->color.b / 255.0, _c->color.a / 255.0); \ -CGContextSetRGBFillColor(context, _c->color.r / 255.0, _c->color.g / 255.0, _c->color.b / 255.0, _c->color.a / 255.0); - - -- (void) drawRect: (CGRect) rect -{ - CGContextRef context = UIGraphicsGetCurrentContext(); - UIGraphicsPushContext(context); - - UIColor* color = [UIColor blackColor]; - CGContextSetFillColorWithColor(context, color.CGColor); - CGContextFillRect(context, rect); - - const struct zr_command* cmd; - - CGContextSaveGState(context); - - zr_foreach(cmd, &gui.ctx) - { - switch (cmd->type) - { - case ZR_COMMAND_SCISSOR: - { - const struct zr_command_scissor* s = zr_command(scissor, cmd); - CGRect cr = CGRectMake(s->x, s->y, s->w, s->h); - CGContextRestoreGState(context); - CGContextSaveGState(context); - CGContextClipToRect(context, cr); - } break; - - case ZR_COMMAND_LINE: - { - const struct zr_command_line* l = zr_command(line, cmd); - setColor(l); - CGContextBeginPath(context); - CGContextMoveToPoint(context, l->begin.x + .5, l->begin.y + .5); - CGContextAddLineToPoint(context, l->end.x + .5, l->end.y + .5); - CGContextStrokePath(context); - } break; - - case ZR_COMMAND_RECT: - { - const struct zr_command_rect* r = zr_command(rect, cmd); - CGRect cr = CGRectMake(r->x, r->y, r->w, r->h); - setColor(r); - if (!r->rounding) CGContextFillRect(context, cr); - else - { - CGMutablePathRef p = CGPathCreateMutable(); - CGPathAddRoundedRect(p, nil, cr, r->rounding, r->rounding); - CGPathCloseSubpath(p); - CGContextBeginPath(context); - CGContextAddPath(context, p); - CGContextDrawPath(context, kCGPathFill); - CGPathRelease(p); - } - } break; - - case ZR_COMMAND_CIRCLE: - { - const struct zr_command_circle* c = zr_command(circle, cmd); - CGRect cr = CGRectMake(c->x, c->y, c->w, c->h); - setColor(c); - CGContextFillEllipseInRect(context, cr); - } break; - - case ZR_COMMAND_TRIANGLE: - { - const struct zr_command_triangle* t = zr_command(triangle, cmd); - setColor(t); - CGContextBeginPath(context); - CGContextMoveToPoint (context, t->a.x, t->a.y); - CGContextAddLineToPoint(context, t->b.x, t->b.y); - CGContextAddLineToPoint(context, t->c.x, t->c.y); - CGContextClosePath(context); - CGContextFillPath(context); - } break; - - case ZR_COMMAND_TEXT: - { - const struct zr_command_text* t = zr_command(text, cmd); - CGRect cr = CGRectMake(t->x, t->y, t->w, t->h); - CGContextSetRGBFillColor(context, t->background.r / 255.0, t->background.g / 255.0, t->background.b / 255.0, t->background.a / 255.0); - CGContextFillRect(context, cr); - NSString* aString = stringFromZR(t->string, t->length); - UIColor* fg = [UIColor colorWithRed: t->foreground.r / 255.0 green: t->foreground.g / 255.0 blue: t->foreground.b / 255.0 alpha: t->foreground.a / 255.0]; - UIColor* bg = [UIColor colorWithRed: t->background.r / 255.0 green: t->background.g / 255.0 blue: t->background.b / 255.0 alpha: t->background.a / 255.0]; - NSDictionary* attributes = @{ NSFontAttributeName: font, NSForegroundColorAttributeName: fg, NSBackgroundColorAttributeName: bg }; - CGSize size = [aString sizeWithAttributes: attributes]; - CGRect xr = CGRectMake(cr.origin.x, cr.origin.y + (cr.size.height - size.height) / 2.0, cr.size.width, size.height); - [aString drawInRect: xr withAttributes: attributes]; - } break; - - case ZR_COMMAND_CURVE: - case ZR_COMMAND_IMAGE: - case ZR_COMMAND_ARC: - case ZR_COMMAND_NOP: - default: - break; - } - } - - UIGraphicsPopContext(); -} - - -- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event -{ - UITouch* touch = [[event allTouches] anyObject]; - CGPoint location = [touch locationInView: self]; - - zr_input_begin(&gui.ctx); - zr_input_motion(&gui.ctx, location.x, location.y); - zr_input_end(&gui.ctx); - - zr_clear(&gui.ctx); - zr_input_begin(&gui.ctx); - zr_input_button(&gui.ctx, ZR_BUTTON_LEFT, location.x, location.y, zr_true); - zr_input_end(&gui.ctx); - - run_demo(&gui); - [self setNeedsDisplay]; -} - - -- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event -{ - UITouch* touch = [[event allTouches] anyObject]; - CGPoint location = [touch locationInView: self]; - - zr_clear(&gui.ctx); - - zr_input_begin(&gui.ctx); - zr_input_motion(&gui.ctx, location.x, location.y); - zr_input_end(&gui.ctx); - - run_demo(&gui); - [self setNeedsDisplay]; -} - - -- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event -{ - UITouch* touch = [[event allTouches] anyObject]; - CGPoint location = [touch locationInView: self]; - - zr_clear(&gui.ctx); - zr_input_begin(&gui.ctx); - zr_input_motion(&gui.ctx, location.x, location.y); - zr_input_button(&gui.ctx, ZR_BUTTON_LEFT, location.x, location.y, zr_false); - zr_input_end(&gui.ctx); - run_demo(&gui); - - zr_clear(&gui.ctx); - zr_input_begin(&gui.ctx); - zr_input_motion(&gui.ctx, 0, 0); - zr_input_end(&gui.ctx); - - run_demo(&gui); - [self setNeedsDisplay]; -} - - -- (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event -{ - [self touchesEnded: touches withEvent: event]; -} - - -@end - - -#pragma mark - ViewController - - - -@interface ViewController : UIViewController - -@property (strong, nonatomic) ZRView* zrView; - -@end - - -@implementation ViewController - - -- (void) viewDidLoad -{ - [super viewDidLoad]; - - _zrView = [[ZRView alloc] initWithFrame: self.view.bounds]; - [self.view addSubview: _zrView]; -} - - -@end - - -#pragma mark - App Delegate - - - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow* window; -@property (strong, nonatomic) ViewController* viewController; - -@end - - -@implementation AppDelegate - - -- (BOOL) application: (UIApplication*) application didFinishLaunchingWithOptions: (NSDictionary*) launchOptions -{ - _window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; - _viewController = [ViewController new]; - _window.rootViewController = _viewController; - [_window makeKeyAndVisible]; - - return YES; -} - - -@end - - -int main(int argc, char* argv[]) -{ - @autoreleasepool - { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} - - diff --git a/demo/apple/iOS_GL_ES.xcodeproj/project.pbxproj b/demo/apple/iOS_GL_ES.xcodeproj/project.pbxproj deleted file mode 100644 index 772892c..0000000 --- a/demo/apple/iOS_GL_ES.xcodeproj/project.pbxproj +++ /dev/null @@ -1,296 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 500B20321C4B42B60002B53F /* ZahnradBackend.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B20311C4B42B60002B53F /* ZahnradBackend.m */; }; - 500B20341C4B43010002B53F /* zahnrad.c in Sources */ = {isa = PBXBuildFile; fileRef = 500B20331C4B43010002B53F /* zahnrad.c */; }; - 500B203A1C4B43380002B53F /* DroidSans.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20361C4B43380002B53F /* DroidSans.ttf */; }; - 500B203B1C4B43380002B53F /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20371C4B43380002B53F /* Roboto-Bold.ttf */; }; - 500B203C1C4B43380002B53F /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20381C4B43380002B53F /* Roboto-Light.ttf */; }; - 500B203D1C4B43380002B53F /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20391C4B43380002B53F /* Roboto-Regular.ttf */; }; - 504DFDF11C4EA36500D3714A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B1FF91C4B410F0002B53F /* main.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 500B1FF51C4B410F0002B53F /* iOS_GL_ES.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOS_GL_ES.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 500B1FF91C4B410F0002B53F /* main.m */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; tabWidth = 4; }; - 500B200D1C4B410F0002B53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 500B20301C4B42B60002B53F /* ZahnradBackend.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = ZahnradBackend.h; sourceTree = SOURCE_ROOT; tabWidth = 4; }; - 500B20311C4B42B60002B53F /* ZahnradBackend.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = ZahnradBackend.m; sourceTree = SOURCE_ROOT; tabWidth = 4; }; - 500B20331C4B43010002B53F /* zahnrad.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; name = zahnrad.c; path = ../../../zahnrad.c; sourceTree = ""; tabWidth = 4; }; - 500B20361C4B43380002B53F /* DroidSans.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = DroidSans.ttf; sourceTree = ""; }; - 500B20371C4B43380002B53F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = ""; }; - 500B20381C4B43380002B53F /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = ""; }; - 500B20391C4B43380002B53F /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; }; - 504DFDEB1C4EA2EE00D3714A /* zahnrad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zahnrad.h; path = ../../../zahnrad.h; sourceTree = ""; }; - 504DFDEF1C4EA36200D3714A /* demo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demo.c; path = ../../demo.c; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 500B1FF21C4B410F0002B53F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 500B1FEC1C4B410F0002B53F = { - isa = PBXGroup; - children = ( - 500B1FF71C4B410F0002B53F /* iOS_GL_ES */, - 500B20351C4B43380002B53F /* font */, - 500B1FF61C4B410F0002B53F /* Products */, - ); - sourceTree = ""; - }; - 500B1FF61C4B410F0002B53F /* Products */ = { - isa = PBXGroup; - children = ( - 500B1FF51C4B410F0002B53F /* iOS_GL_ES.app */, - ); - name = Products; - sourceTree = ""; - }; - 500B1FF71C4B410F0002B53F /* iOS_GL_ES */ = { - isa = PBXGroup; - children = ( - 504DFDEF1C4EA36200D3714A /* demo.c */, - 500B20301C4B42B60002B53F /* ZahnradBackend.h */, - 500B20311C4B42B60002B53F /* ZahnradBackend.m */, - 500B1FF91C4B410F0002B53F /* main.m */, - 504DFDEB1C4EA2EE00D3714A /* zahnrad.h */, - 500B20331C4B43010002B53F /* zahnrad.c */, - 500B200D1C4B410F0002B53F /* Info.plist */, - ); - path = iOS_GL_ES; - sourceTree = ""; - }; - 500B20351C4B43380002B53F /* font */ = { - isa = PBXGroup; - children = ( - 500B20361C4B43380002B53F /* DroidSans.ttf */, - 500B20371C4B43380002B53F /* Roboto-Bold.ttf */, - 500B20381C4B43380002B53F /* Roboto-Light.ttf */, - 500B20391C4B43380002B53F /* Roboto-Regular.ttf */, - ); - name = font; - path = ../../font; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 500B1FF41C4B410F0002B53F /* iOS_GL_ES */ = { - isa = PBXNativeTarget; - buildConfigurationList = 500B20101C4B410F0002B53F /* Build configuration list for PBXNativeTarget "iOS_GL_ES" */; - buildPhases = ( - 500B1FF11C4B410F0002B53F /* Sources */, - 500B1FF21C4B410F0002B53F /* Frameworks */, - 500B1FF31C4B410F0002B53F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = iOS_GL_ES; - productName = iOS_GL_ES; - productReference = 500B1FF51C4B410F0002B53F /* iOS_GL_ES.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 500B1FED1C4B410F0002B53F /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0720; - ORGANIZATIONNAME = richi; - TargetAttributes = { - 500B1FF41C4B410F0002B53F = { - CreatedOnToolsVersion = 7.2; - }; - }; - }; - buildConfigurationList = 500B1FF01C4B410F0002B53F /* Build configuration list for PBXProject "iOS_GL_ES" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 500B1FEC1C4B410F0002B53F; - productRefGroup = 500B1FF61C4B410F0002B53F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 500B1FF41C4B410F0002B53F /* iOS_GL_ES */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 500B1FF31C4B410F0002B53F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 500B203B1C4B43380002B53F /* Roboto-Bold.ttf in Resources */, - 500B203A1C4B43380002B53F /* DroidSans.ttf in Resources */, - 500B203D1C4B43380002B53F /* Roboto-Regular.ttf in Resources */, - 500B203C1C4B43380002B53F /* Roboto-Light.ttf in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 500B1FF11C4B410F0002B53F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 500B20341C4B43010002B53F /* zahnrad.c in Sources */, - 504DFDF11C4EA36500D3714A /* main.m in Sources */, - 500B20321C4B42B60002B53F /* ZahnradBackend.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 500B200E1C4B410F0002B53F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 500B200F1C4B410F0002B53F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 500B20111C4B410F0002B53F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - INFOPLIST_FILE = iOS_GL_ES/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.iOS-GL-ES"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 500B20121C4B410F0002B53F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - INFOPLIST_FILE = iOS_GL_ES/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.iOS-GL-ES"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 500B1FF01C4B410F0002B53F /* Build configuration list for PBXProject "iOS_GL_ES" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 500B200E1C4B410F0002B53F /* Debug */, - 500B200F1C4B410F0002B53F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 500B20101C4B410F0002B53F /* Build configuration list for PBXNativeTarget "iOS_GL_ES" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 500B20111C4B410F0002B53F /* Debug */, - 500B20121C4B410F0002B53F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 500B1FED1C4B410F0002B53F /* Project object */; -} diff --git a/demo/apple/iOS_GL_ES.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/demo/apple/iOS_GL_ES.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 69dff87..0000000 --- a/demo/apple/iOS_GL_ES.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/demo/apple/iOS_GL_ES/Info.plist b/demo/apple/iOS_GL_ES/Info.plist deleted file mode 100644 index 984f44e..0000000 --- a/demo/apple/iOS_GL_ES/Info.plist +++ /dev/null @@ -1,48 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/demo/apple/iOS_GL_ES/main.m b/demo/apple/iOS_GL_ES/main.m deleted file mode 100644 index c962160..0000000 --- a/demo/apple/iOS_GL_ES/main.m +++ /dev/null @@ -1,225 +0,0 @@ -/* - Copyright (c) 2016 richi - - 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. - */ - -#import -#import - -#import "ZahnradBackend.h" - - -@interface GameViewController : GLKViewController -@end - - -@implementation GameViewController -{ - BOOL keyboardAllowed; - NSUInteger keyboardHash; - - EAGLContext* context; - ZahnradBackend* zr; -} - - -- (void) viewDidLoad -{ - [super viewDidLoad]; - - keyboardHash = -1; - - context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES3]; - assert(context != nil); - - ((GLKView*)self.view).context = context; - ((GLKView*)self.view).drawableDepthFormat = GLKViewDrawableDepthFormatNone; - - [EAGLContext setCurrentContext: context]; - zr = [[ZahnradBackend alloc] initWithView: ((GLKView*)self.view)]; -} - - -- (BOOL) prefersStatusBarHidden -{ - return YES; -} - - -- (void) update -{ - [zr updateFrame]; -} - - -- (void) glkView: (GLKView*) view drawInRect: (CGRect) rect -{ - CGRect bounds = view.bounds; - CGFloat scale = view.window.screen.scale; - - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - [zr drawFrameWithScale: scale width: bounds.size.width height: bounds.size.height]; -} - - -#pragma mark - User Input - - - -- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event -{ - UITouch* touch = [[event allTouches] anyObject]; - CGPoint location = [touch locationInView: self.view]; - - [zr addEvent: @{@"type" : @2, @"pos" : NSStringFromCGPoint(location)}]; -} - - -- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event -{ - UITouch* touch = [[event allTouches] anyObject]; - CGPoint location = [touch locationInView: self.view]; - - [zr addEvent: @{@"type" : @5, @"pos" : NSStringFromCGPoint(location)}]; -} - - -- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event -{ - UITouch* touch = [[event allTouches] anyObject]; - CGPoint location = [touch locationInView: self.view]; - - [zr addEvent: @{@"type" : @6, @"pos" : NSStringFromCGPoint(location)}]; -} - - -- (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event -{ - [self touchesEnded: touches withEvent: event]; -} - - -- (void) insertText: (NSString*) text -{ - [zr addEvent: @{@"type" : @12, @"txt" : text, @"mod" : @0}]; -} - - -- (void) deleteBackward -{ - [zr addEvent: @{@"type" : @12, @"txt" : @"\b", @"mod" : @0}]; -} - - -- (BOOL) hasText -{ - return NO; -} - - -- (BOOL) canBecomeFirstResponder -{ - return keyboardAllowed; -} - - -- (BOOL) canResignFirstResponder -{ - return YES; -} - - -- (void) showKeyboard: (NSDictionary*) info -{ - NSUInteger hash = [info[@"hash"] unsignedIntegerValue]; - - if (hash != keyboardHash) - { - keyboardHash = hash; - keyboardAllowed = YES; - if (!self.isFirstResponder) - [self becomeFirstResponder]; - } -} - - -- (void) hideKeyboard -{ - keyboardHash = -1; - keyboardAllowed = NO; - [self resignFirstResponder]; -} - - -@end - - -#pragma mark - App Delegate - - - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow* window; -@property (strong, nonatomic) GameViewController* gameViewController; - -@end - - -@implementation AppDelegate - - -- (void) showKeyboard: (NSDictionary*) info -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self.gameViewController showKeyboard: info]; - }); -} - - -- (void) hideKeyboard -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self.gameViewController hideKeyboard]; - }); -} - - -- (BOOL) application: (UIApplication*) application didFinishLaunchingWithOptions: (NSDictionary*) launchOptions -{ - _window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; - _gameViewController = [GameViewController new]; - _window.rootViewController = _gameViewController; - [_window makeKeyAndVisible]; - - return YES; -} - - -@end - - -int main(int argc, char* argv[]) -{ - @autoreleasepool - { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} - - diff --git a/demo/apple/tvOS_GL_ES.xcodeproj/project.pbxproj b/demo/apple/tvOS_GL_ES.xcodeproj/project.pbxproj deleted file mode 100644 index 92f10be..0000000 --- a/demo/apple/tvOS_GL_ES.xcodeproj/project.pbxproj +++ /dev/null @@ -1,292 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 500B20661C4B4AB00002B53F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B20651C4B4AB00002B53F /* main.m */; }; - 500B208A1C4B4D460002B53F /* ZahnradBackend.m in Sources */ = {isa = PBXBuildFile; fileRef = 500B20891C4B4D460002B53F /* ZahnradBackend.m */; }; - 500B208C1C4B4D580002B53F /* zahnrad.c in Sources */ = {isa = PBXBuildFile; fileRef = 500B208B1C4B4D580002B53F /* zahnrad.c */; }; - 500B20921C4B4D700002B53F /* DroidSans.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B208E1C4B4D700002B53F /* DroidSans.ttf */; }; - 500B20931C4B4D700002B53F /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B208F1C4B4D700002B53F /* Roboto-Bold.ttf */; }; - 500B20941C4B4D700002B53F /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20901C4B4D700002B53F /* Roboto-Light.ttf */; }; - 500B20951C4B4D700002B53F /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 500B20911C4B4D700002B53F /* Roboto-Regular.ttf */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 500B20611C4B4AB00002B53F /* tvOS_GL_ES.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvOS_GL_ES.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 500B20651C4B4AB00002B53F /* main.m */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; tabWidth = 4; }; - 500B20741C4B4AB00002B53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 500B20881C4B4D460002B53F /* ZahnradBackend.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = ZahnradBackend.h; sourceTree = SOURCE_ROOT; tabWidth = 4; }; - 500B20891C4B4D460002B53F /* ZahnradBackend.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = ZahnradBackend.m; sourceTree = SOURCE_ROOT; tabWidth = 4; }; - 500B208B1C4B4D580002B53F /* zahnrad.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; name = zahnrad.c; path = ../../../zahnrad.c; sourceTree = ""; tabWidth = 5; }; - 500B208E1C4B4D700002B53F /* DroidSans.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = DroidSans.ttf; sourceTree = ""; }; - 500B208F1C4B4D700002B53F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = ""; }; - 500B20901C4B4D700002B53F /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = ""; }; - 500B20911C4B4D700002B53F /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; }; - 504DFDEC1C4EA2FF00D3714A /* zahnrad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zahnrad.h; path = ../../../zahnrad.h; sourceTree = ""; }; - 504DFDF21C4EA37700D3714A /* demo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demo.c; path = ../../demo.c; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 500B205E1C4B4AB00002B53F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 500B20581C4B4AB00002B53F = { - isa = PBXGroup; - children = ( - 500B20631C4B4AB00002B53F /* tvOS_GL_ES */, - 500B208D1C4B4D700002B53F /* font */, - 500B20621C4B4AB00002B53F /* Products */, - ); - sourceTree = ""; - }; - 500B20621C4B4AB00002B53F /* Products */ = { - isa = PBXGroup; - children = ( - 500B20611C4B4AB00002B53F /* tvOS_GL_ES.app */, - ); - name = Products; - sourceTree = ""; - }; - 500B20631C4B4AB00002B53F /* tvOS_GL_ES */ = { - isa = PBXGroup; - children = ( - 504DFDF21C4EA37700D3714A /* demo.c */, - 500B20881C4B4D460002B53F /* ZahnradBackend.h */, - 500B20891C4B4D460002B53F /* ZahnradBackend.m */, - 500B20651C4B4AB00002B53F /* main.m */, - 504DFDEC1C4EA2FF00D3714A /* zahnrad.h */, - 500B208B1C4B4D580002B53F /* zahnrad.c */, - 500B20741C4B4AB00002B53F /* Info.plist */, - ); - path = tvOS_GL_ES; - sourceTree = ""; - }; - 500B208D1C4B4D700002B53F /* font */ = { - isa = PBXGroup; - children = ( - 500B208E1C4B4D700002B53F /* DroidSans.ttf */, - 500B208F1C4B4D700002B53F /* Roboto-Bold.ttf */, - 500B20901C4B4D700002B53F /* Roboto-Light.ttf */, - 500B20911C4B4D700002B53F /* Roboto-Regular.ttf */, - ); - name = font; - path = ../../font; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 500B20601C4B4AB00002B53F /* tvOS_GL_ES */ = { - isa = PBXNativeTarget; - buildConfigurationList = 500B20771C4B4AB00002B53F /* Build configuration list for PBXNativeTarget "tvOS_GL_ES" */; - buildPhases = ( - 500B205D1C4B4AB00002B53F /* Sources */, - 500B205E1C4B4AB00002B53F /* Frameworks */, - 500B205F1C4B4AB00002B53F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = tvOS_GL_ES; - productName = tvOS_GL_ES; - productReference = 500B20611C4B4AB00002B53F /* tvOS_GL_ES.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 500B20591C4B4AB00002B53F /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0720; - ORGANIZATIONNAME = richi; - TargetAttributes = { - 500B20601C4B4AB00002B53F = { - CreatedOnToolsVersion = 7.2; - }; - }; - }; - buildConfigurationList = 500B205C1C4B4AB00002B53F /* Build configuration list for PBXProject "tvOS_GL_ES" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 500B20581C4B4AB00002B53F; - productRefGroup = 500B20621C4B4AB00002B53F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 500B20601C4B4AB00002B53F /* tvOS_GL_ES */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 500B205F1C4B4AB00002B53F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 500B20931C4B4D700002B53F /* Roboto-Bold.ttf in Resources */, - 500B20921C4B4D700002B53F /* DroidSans.ttf in Resources */, - 500B20951C4B4D700002B53F /* Roboto-Regular.ttf in Resources */, - 500B20941C4B4D700002B53F /* Roboto-Light.ttf in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 500B205D1C4B4AB00002B53F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 500B20661C4B4AB00002B53F /* main.m in Sources */, - 500B208C1C4B4D580002B53F /* zahnrad.c in Sources */, - 500B208A1C4B4D460002B53F /* ZahnradBackend.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 500B20751C4B4AB00002B53F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = appletvos; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.1; - }; - name = Debug; - }; - 500B20761C4B4AB00002B53F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = appletvos; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.1; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 500B20781C4B4AB00002B53F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = tvOS_GL_ES/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.tvOS-GL-ES"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 500B20791C4B4AB00002B53F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = tvOS_GL_ES/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.richi.tvOS-GL-ES"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 500B205C1C4B4AB00002B53F /* Build configuration list for PBXProject "tvOS_GL_ES" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 500B20751C4B4AB00002B53F /* Debug */, - 500B20761C4B4AB00002B53F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 500B20771C4B4AB00002B53F /* Build configuration list for PBXNativeTarget "tvOS_GL_ES" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 500B20781C4B4AB00002B53F /* Debug */, - 500B20791C4B4AB00002B53F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 500B20591C4B4AB00002B53F /* Project object */; -} diff --git a/demo/apple/tvOS_GL_ES.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/demo/apple/tvOS_GL_ES.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index f18f2c1..0000000 --- a/demo/apple/tvOS_GL_ES.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/demo/apple/tvOS_GL_ES/Info.plist b/demo/apple/tvOS_GL_ES/Info.plist deleted file mode 100644 index 4f33860..0000000 --- a/demo/apple/tvOS_GL_ES/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - - diff --git a/demo/apple/tvOS_GL_ES/main.m b/demo/apple/tvOS_GL_ES/main.m deleted file mode 100644 index 6a76e4f..0000000 --- a/demo/apple/tvOS_GL_ES/main.m +++ /dev/null @@ -1,254 +0,0 @@ -/* - Copyright (c) 2016 richi - - 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. - */ - -#import -#import - -#import "ZahnradBackend.h" - - -@interface GameViewController : GLKViewController -@end - - -@implementation GameViewController -{ - NSUInteger keyboardHash; - UITextField* textInputField; - - CGPoint cursor; - UIView* cursorView; - - EAGLContext* context; - ZahnradBackend* zr; - -} - - -- (void) viewDidLoad -{ - [super viewDidLoad]; - - keyboardHash = -1; - - context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES3]; - assert(context != nil); - - ((GLKView*)self.view).context = context; - ((GLKView*)self.view).drawableDepthFormat = GLKViewDrawableDepthFormatNone; - - [EAGLContext setCurrentContext: context]; - zr = [[ZahnradBackend alloc] initWithView: ((GLKView*)self.view)]; - - UIPanGestureRecognizer* panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action: @selector(pan:)]; - [self.view addGestureRecognizer:panRecognizer]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - cursor = CGPointMake(95, 70); - cursorView = [[UIView alloc] initWithFrame: CGRectMake(cursor.x, cursor.y, 10, 10)]; - cursorView.backgroundColor = UIColor.redColor; - [self.view addSubview: cursorView]; - [zr addEvent: @{@"type" : @2, @"pos" : NSStringFromCGPoint(cursor)}]; - }); -} - - -- (void) update -{ - [zr updateFrame]; -} - - -- (void) glkView: (GLKView*) view drawInRect: (CGRect) rect -{ - CGRect bounds = view.bounds; - CGFloat scale = view.window.screen.scale; - - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - [zr drawFrameWithScale: scale width: bounds.size.width height: bounds.size.height]; -} - - -#pragma mark - User Input - - - -- (void) pan: (UIPanGestureRecognizer*) panRecognizer -{ - CGPoint delta = [panRecognizer translationInView: self.view]; - -#if 0 - - CGFloat s = 33.0; - - cursor.x += delta.x / s; - cursor.y += delta.y / s; - -#else - - CGFloat s = 150.0; - - if (delta.x != 0.0) - cursor.x += pow(2.0, fabs(delta.x / s)) * (delta.x > 0.0 ? 1.0 : -1.0); - if (delta.y != 0.0) - cursor.y += pow(2.0, fabs(delta.y / s)) * (delta.y > 0.0 ? 1.0 : -1.0); -#endif - - - CGRect bounds = self.view.bounds; - if (cursor.x < CGRectGetMinX(bounds)) cursor.x = CGRectGetMinX(bounds); - if (cursor.x > CGRectGetMaxX(bounds)) cursor.x = CGRectGetMaxX(bounds); - if (cursor.y < CGRectGetMinY(bounds)) cursor.y = CGRectGetMinY(bounds); - if (cursor.y > CGRectGetMaxY(bounds)) cursor.y = CGRectGetMaxY(bounds); - - cursorView.center = cursor; - - [zr addEvent: @{@"type" : @2, @"pos" : NSStringFromCGPoint(cursor)}]; -} - - -- (void) pressesBegan: (NSSet*) presses withEvent: (UIPressesEvent*) event -{ - UIPress* press = [[event allPresses] anyObject]; - if (press.type != UIPressTypeSelect) return; - - [zr addEvent: @{@"type" : @5, @"pos" : NSStringFromCGPoint(cursor)}]; -} - - -- (void) pressesEnded: (NSSet*) presses withEvent: (UIPressesEvent*) event -{ - UIPress* press = [[event allPresses] anyObject]; - if (press.type != UIPressTypeSelect) return; - - [zr addEvent: @{@"type" : @6, @"pos" : NSStringFromCGPoint(cursor)}]; -} - - -- (void) pressesCancelled: (NSSet*) presses withEvent: (UIPressesEvent*) event -{ - [self pressesEnded: presses withEvent: event]; -} - - - -- (BOOL) textFieldShouldReturn: (UITextField*) textField -{ - return YES; -} - - -- (void) textFieldDidEndEditing: (UITextField*) textField -{ - [textInputField resignFirstResponder]; - [textInputField removeFromSuperview]; - textInputField = nil; - - [zr addEvent: @{@"type" : @12, @"txt" : textField.text, @"mod" : @0}]; -} - - -- (void) showKeyboard: (NSDictionary*) info -{ - NSUInteger hash = [info[@"hash"] unsignedIntegerValue]; - - if (hash != keyboardHash) - { - keyboardHash = hash; - if (!textInputField) - { - CGRect frame = CGRectFromString(info[@"frame"]); - textInputField = [[UITextField alloc] initWithFrame: frame]; - textInputField.delegate = self; - textInputField.text = info[@"text"]; - [self.view addSubview: textInputField]; - [textInputField becomeFirstResponder]; - } - } -} - - -- (void) hideKeyboard -{ - [textInputField resignFirstResponder]; - [textInputField removeFromSuperview]; - textInputField = nil; - - keyboardHash = -1; -} - - -@end - - -#pragma mark - App Delegate - - - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow* window; -@property (strong, nonatomic) GameViewController* gameViewController; - -@end - - -@implementation AppDelegate - - -- (void) showKeyboard: (NSDictionary*) info -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self.gameViewController showKeyboard: info]; - }); -} - - -- (void) hideKeyboard -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self.gameViewController hideKeyboard]; - }); -} - - -- (BOOL) application: (UIApplication*) application didFinishLaunchingWithOptions: (NSDictionary*) launchOptions -{ - _window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; - _gameViewController = [GameViewController new]; - _window.rootViewController = _gameViewController; - [_window makeKeyAndVisible]; - - return YES; -} - - -@end - - -int main(int argc, char* argv[]) -{ - @autoreleasepool - { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} - - diff --git a/demo/demo.c b/demo/demo.c deleted file mode 100644 index 3b3fed2..0000000 --- a/demo/demo.c +++ /dev/null @@ -1,2676 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a,b) ((a) < (b) ? (b) : (a)) -#endif -#ifndef CLAMP -#define CLAMP(i,v,x) (MAX(MIN(v,x), i)) -#endif -#define LEN(a) (sizeof(a)/sizeof(a)[0]) -#define UNUSED(a) ((void)(a)) - -#define MAX_BUFFER 64 -#define MAX_MEMORY (64 * 1024) -#define MAX_COMMAND_MEMORY (16 * 1024) -#define WINDOW_WIDTH 1200 -#define WINDOW_HEIGHT 800 - -struct icons { - struct zr_image unchecked; - struct zr_image checked; - struct zr_image rocket; - struct zr_image cloud; - struct zr_image pen; - struct zr_image play; - struct zr_image pause; - struct zr_image stop; - struct zr_image prev; - struct zr_image next; - struct zr_image tools; - struct zr_image dir; - struct zr_image copy; - struct zr_image convert; - struct zr_image del; - struct zr_image edit; - struct zr_image images[9]; - struct zr_image menu[6]; - - struct zr_image desktop; - struct zr_image home; - struct zr_image computer; - struct zr_image directory; - - struct zr_image default_file; - struct zr_image text_file; - struct zr_image music_file; - struct zr_image font_file; - struct zr_image img_file; - struct zr_image movie_file; - - struct zr_image button; - struct zr_image button_hover; - struct zr_image check; - struct zr_image check_cursor; - struct zr_image close; - struct zr_image combo; - struct zr_image slider_cursor; - struct zr_image header; - struct zr_image window; -}; - -enum theme {THEME_BLACK, THEME_WHITE, THEME_RED, THEME_BLUE, THEME_DARK}; -struct demo { - void *memory; - struct zr_context ctx; - struct icons icons; - enum theme theme; - struct zr_memory_status status; - - int show_filex; - int show_simple; - int show_demo; - int show_node; - int show_grid; - int show_button; - int show_basic; -}; - -/* =============================================================== - * - * SIMPLE WINDOW - * - * ===============================================================*/ -static void -simple_window(struct zr_context *ctx) -{ - /* simple demo window */ - struct zr_panel layout; - if (zr_begin(ctx, &layout, "Show", zr_rect(420, 350, 200, 200), - ZR_WINDOW_BORDER|ZR_WINDOW_MOVABLE|ZR_WINDOW_SCALABLE| - ZR_WINDOW_CLOSABLE|ZR_WINDOW_MINIMIZABLE|ZR_WINDOW_TITLE)) - { - enum {EASY, HARD}; - static int op = EASY; - static int property = 20; - - zr_layout_row_static(ctx, 30, 80, 1); - if (zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT)) { - /* event handling */ - } - zr_layout_row_dynamic(ctx, 30, 2); - if (zr_option_label(ctx, "easy", op == EASY)) op = EASY; - if (zr_option_label(ctx, "hard", op == HARD)) op = HARD; - - zr_layout_row_dynamic(ctx, 22, 1); - zr_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); - } - zr_end(ctx); -} - -/* =============================================================== - * - * DEMO WINDOW - * - * ===============================================================*/ -static void -demo_window(struct demo *gui, struct zr_context *ctx) -{ - struct zr_panel menu; - - /* window flags */ - static int show_menu = zr_true; - static int titlebar = zr_true; - static int border = zr_true; - static int resize = zr_true; - static int moveable = zr_true; - static int no_scrollbar = zr_false; - static zr_flags window_flags = 0; - static int minimizable = zr_true; - static int close = zr_true; - - /* popups */ - static enum zr_style_header_align header_align = ZR_HEADER_RIGHT; - static int show_app_about = zr_false; - struct zr_panel layout; - - /* window flags */ - window_flags = 0; - ctx->style.window.header.align = header_align; - if (border) window_flags |= ZR_WINDOW_BORDER; - if (resize) window_flags |= ZR_WINDOW_SCALABLE; - if (moveable) window_flags |= ZR_WINDOW_MOVABLE; - if (no_scrollbar) window_flags |= ZR_WINDOW_NO_SCROLLBAR; - if (minimizable) window_flags |= ZR_WINDOW_MINIMIZABLE; - if (close) window_flags |= ZR_WINDOW_CLOSABLE; - - if (zr_begin(ctx, &layout, "Demo", zr_rect(10, 10, 400, 750), window_flags)) - { - if (show_menu) - { - /* menubar */ - enum menu_states {MENU_DEFAULT, MENU_WINDOWS}; - static enum menu_states menu_state = MENU_DEFAULT; - static zr_size mprog = 60; - static int mslider = 10; - static int mcheck = zr_true; - - zr_menubar_begin(ctx); - zr_layout_row_begin(ctx, ZR_STATIC, 25, 2); - zr_layout_row_push(ctx, 45); - if (zr_menu_begin_label(ctx, &menu, "MENU", ZR_TEXT_LEFT, 120)) - { - zr_layout_row_dynamic(ctx, 25, 1); - if (menu_state == MENU_DEFAULT) { - static size_t prog = 40; - static int slider = 10; - static int check = zr_true; - - if (zr_menu_item_label(ctx, "Hide", ZR_TEXT_LEFT)) - show_menu = zr_false; - if (zr_menu_item_label(ctx, "About", ZR_TEXT_LEFT)) - show_app_about = zr_true; - zr_progress(ctx, &prog, 100, ZR_MODIFIABLE); - zr_slider_int(ctx, 0, &slider, 16, 1); - zr_checkbox_label(ctx, "check", &check); - if (zr_button_symbol_label(ctx, ZR_SYMBOL_TRIANGLE_RIGHT, - "Windows", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT)) - menu_state = MENU_WINDOWS; - } else { - if (zr_selectable_label(ctx, "Simple", ZR_TEXT_LEFT, &gui->show_simple) && !gui->show_simple) - zr_window_close(ctx, "Show"); - if (zr_selectable_label(ctx, "Node Editor", ZR_TEXT_LEFT, &gui->show_node) && !gui->show_node) - zr_window_close(ctx, "Node Editor"); - #ifndef DEMO_DO_NOT_DRAW_IMAGES - if (zr_selectable_label(ctx, "Grid", ZR_TEXT_LEFT, &gui->show_grid) && !gui->show_grid) - zr_window_close(ctx, "Grid Demo"); - if (zr_selectable_label(ctx, "Basic", ZR_TEXT_LEFT, &gui->show_basic) && !gui->show_basic) - zr_window_close(ctx, "Basic Demo"); - if (zr_selectable_label(ctx, "Button", ZR_TEXT_LEFT, &gui->show_button) && !gui->show_button) - zr_window_close(ctx, "Button Demo"); - #endif - if (zr_button_symbol_label(ctx, ZR_SYMBOL_TRIANGLE_LEFT, - "Back", ZR_TEXT_RIGHT, ZR_BUTTON_DEFAULT)) - menu_state = MENU_DEFAULT; - } - zr_menu_end(ctx); - } else menu_state = MENU_DEFAULT; - zr_layout_row_push(ctx, 70); - zr_progress(ctx, &mprog, 100, ZR_MODIFIABLE); - zr_slider_int(ctx, 0, &mslider, 16, 1); - zr_checkbox_label(ctx, "check", &mcheck); - zr_menubar_end(ctx); - } - - if (show_app_about) - { - /* about popup */ - struct zr_panel popup; - static struct zr_rect s = {20, 100, 300, 190}; - if (zr_popup_begin(ctx, &popup, ZR_POPUP_STATIC, "About", ZR_WINDOW_CLOSABLE, s)) - { - zr_layout_row_dynamic(ctx, 20, 1); - zr_label(ctx, "Zahnrad", ZR_TEXT_LEFT); - zr_label(ctx, "By Micha Mettke", ZR_TEXT_LEFT); - zr_label(ctx, "Zahnrad is licensed under the zlib License.", ZR_TEXT_LEFT); - zr_label(ctx, "See LICENSE for more information", ZR_TEXT_LEFT); - zr_popup_end(ctx); - } else show_app_about = zr_false; - } - - /* window flags */ - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Window", ZR_MINIMIZED)) { - zr_layout_row_dynamic(ctx, 30, 2); - zr_checkbox_label(ctx, "Titlebar", &titlebar); - zr_checkbox_label(ctx, "Menu", &show_menu); - zr_checkbox_label(ctx, "Border", &border); - zr_checkbox_label(ctx, "Resizable", &resize); - zr_checkbox_label(ctx, "Moveable", &moveable); - zr_checkbox_label(ctx, "No Scrollbar", &no_scrollbar); - zr_checkbox_label(ctx, "Minimizable", &minimizable); - zr_checkbox_label(ctx, "Closeable", &close); - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Widgets", ZR_MINIMIZED)) - { - enum options {A,B,C}; - static int checkbox; - static int option; - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Text", ZR_MINIMIZED)) - { - /* Text Widgets */ - zr_layout_row_dynamic(ctx, 20, 1); - zr_label(ctx, "Label aligned left", ZR_TEXT_LEFT); - zr_label(ctx, "Label aligned centered", ZR_TEXT_CENTERED); - zr_label(ctx, "Label aligned right", ZR_TEXT_RIGHT); - zr_label_colored(ctx, "Blue text", ZR_TEXT_LEFT, zr_rgb(0,0,255)); - zr_label_colored(ctx, "Yellow text", ZR_TEXT_LEFT, zr_rgb(255,255,0)); - zr_text(ctx, "Text without /0", 15, ZR_TEXT_RIGHT); - - zr_layout_row_static(ctx, 100, 200, 1); - zr_label_wrap(ctx, "This is a very long line to hopefully get this text to be wrapped into multiple lines to show line wrapping"); - zr_layout_row_dynamic(ctx, 100, 1); - zr_label_wrap(ctx, "This is another long text to show dynamic window changes on multiline text"); - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Button", ZR_MINIMIZED)) - { - /* Buttons Widgets */ - zr_layout_row_static(ctx, 30, 100, 3); - if (zr_button_label(ctx, "Button", ZR_BUTTON_DEFAULT)) - fprintf(stdout, "Button pressed!\n"); - if (zr_button_label(ctx, "Repeater", ZR_BUTTON_REPEATER)) - fprintf(stdout, "Repeater is being pressed!\n"); - zr_button_color(ctx, zr_rgb(0,0,255), ZR_BUTTON_DEFAULT); - - zr_layout_row_static(ctx, 20, 20, 8); - zr_button_symbol(ctx, ZR_SYMBOL_CIRCLE, ZR_BUTTON_DEFAULT); - zr_button_symbol(ctx, ZR_SYMBOL_CIRCLE_FILLED, ZR_BUTTON_DEFAULT); - zr_button_symbol(ctx, ZR_SYMBOL_RECT, ZR_BUTTON_DEFAULT); - zr_button_symbol(ctx, ZR_SYMBOL_RECT_FILLED, ZR_BUTTON_DEFAULT); - zr_button_symbol(ctx, ZR_SYMBOL_TRIANGLE_UP, ZR_BUTTON_DEFAULT); - zr_button_symbol(ctx, ZR_SYMBOL_TRIANGLE_DOWN, ZR_BUTTON_DEFAULT); - zr_button_symbol(ctx, ZR_SYMBOL_TRIANGLE_LEFT, ZR_BUTTON_DEFAULT); - zr_button_symbol(ctx, ZR_SYMBOL_TRIANGLE_RIGHT, ZR_BUTTON_DEFAULT); - - zr_layout_row_static(ctx, 30, 100, 2); - zr_button_symbol_label(ctx, ZR_SYMBOL_TRIANGLE_LEFT, "prev", ZR_TEXT_RIGHT, ZR_BUTTON_DEFAULT); - zr_button_symbol_label(ctx, ZR_SYMBOL_TRIANGLE_RIGHT, "next", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT); - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Basic", ZR_MINIMIZED)) - { - /* Basic widgets */ - static int int_slider = 5; - static float float_slider = 2.5f; - static size_t prog_value = 40; - static float property_float = 2; - static int property_int = 10; - static int property_neg = 10; - - static float range_float_min = 0; - static float range_float_max = 100; - static float range_float_value = 50; - static int range_int_min = 0; - static int range_int_value = 2048; - static int range_int_max = 4096; - static const float ratio[] = {120, 150}; - - zr_layout_row_static(ctx, 30, 100, 1); - zr_checkbox_label(ctx, "Checkbox", &checkbox); - - zr_layout_row_static(ctx, 30, 80, 3); - option = zr_option_label(ctx, "optionA", option == A) ? A : option; - option = zr_option_label(ctx, "optionB", option == B) ? B : option; - option = zr_option_label(ctx, "optionC", option == C) ? C : option; - - - zr_layout_row(ctx, ZR_STATIC, 30, 2, ratio); - zr_labelf(ctx, ZR_TEXT_LEFT, "Slider int"); - zr_slider_int(ctx, 0, &int_slider, 10, 1); - - zr_label(ctx, "Slider float", ZR_TEXT_LEFT); - zr_slider_float(ctx, 0, &float_slider, 5.0, 0.5f); - zr_labelf(ctx, ZR_TEXT_LEFT, "Progressbar" , prog_value); - zr_progress(ctx, &prog_value, 100, ZR_MODIFIABLE); - - zr_layout_row(ctx, ZR_STATIC, 25, 2, ratio); - zr_label(ctx, "Property float:", ZR_TEXT_LEFT); - zr_property_float(ctx, "Float:", 0, &property_float, 64.0f, 0.1f, 0.2f); - zr_label(ctx, "Property int:", ZR_TEXT_LEFT); - zr_property_int(ctx, "Int:", 0, &property_int, 100.0f, 1, 1); - zr_label(ctx, "Property neg:", ZR_TEXT_LEFT); - zr_property_int(ctx, "Neg:", -10, &property_neg, 10, 1, 1); - - zr_layout_row_dynamic(ctx, 25, 1); - zr_label(ctx, "Range:", ZR_TEXT_LEFT); - zr_layout_row_dynamic(ctx, 25, 3); - zr_property_float(ctx, "#min:", 0, &range_float_min, range_float_max, 1.0f, 0.2f); - zr_property_float(ctx, "#float:", range_float_min, &range_float_value, range_float_max, 1.0f, 0.2f); - zr_property_float(ctx, "#max:", range_float_min, &range_float_max, 100, 1.0f, 0.2f); - - zr_property_int(ctx, "#min:", INT_MIN, &range_int_min, range_int_max, 1, 10); - zr_property_int(ctx, "#neg:", range_int_min, &range_int_value, range_int_max, 1, 10); - zr_property_int(ctx, "#max:", range_int_min, &range_int_max, INT_MAX, 1, 10); - - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Selectable", ZR_MINIMIZED)) - { - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "List", ZR_MINIMIZED)) - { - static int selected[4] = {zr_false, zr_false, zr_true, zr_false}; - zr_layout_row_static(ctx, 18, 100, 1); - zr_selectable_label(ctx, "Selectable", ZR_TEXT_LEFT, &selected[0]); - zr_selectable_label(ctx, "Selectable", ZR_TEXT_LEFT, &selected[1]); - zr_label(ctx, "Not Selectable", ZR_TEXT_LEFT); - zr_selectable_label(ctx, "Selectable", ZR_TEXT_LEFT, &selected[2]); - zr_selectable_label(ctx, "Selectable", ZR_TEXT_LEFT, &selected[3]); - zr_layout_pop(ctx); - } - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Grid", ZR_MINIMIZED)) - { - int i; - static int selected[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; - zr_layout_row_static(ctx, 50, 50, 4); - for (i = 0; i < 16; ++i) { - if (zr_selectable_label(ctx, "Z", ZR_TEXT_CENTERED, &selected[i])) { - int x = (i % 4), y = i / 4; - if (x > 0) selected[i - 1] ^= 1; - if (x < 3) selected[i + 1] ^= 1; - if (y > 0) selected[i - 4] ^= 1; - if (y < 3) selected[i + 4] ^= 1; - } - } - zr_layout_pop(ctx); - } - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Combo", ZR_MINIMIZED)) - { - /* Combobox Widgets - * In this library comboboxes are not limited to being a popup - * list of selectable text. Instead it is a abstract concept of - * having something that is *selected* or displayed, a popup window - * which opens if something needs to be modified and the content - * of the popup which causes the *selected* or displayed value to - * change or if wanted close the combobox. - * - * While strange at first handling comboboxes in a abstract way - * solves the problem of overloaded window content. For example - * changing a color value requires 4 value modifier (slider, property,...) - * for RGBA then you need a label and ways to display the current color. - * If you want to go fancy you even add rgb and hsv ratio boxes. - * While fine for one color if you have a lot of them it because - * tedious to look at and quite wasteful in space. You could add - * a popup which modifies the color but this does not solve the - * fact that it still requires a lot of cluttered space to do. - * - * In these kind of instance abstract comboboxes are quite handy. All - * value modifiers are hidden inside the combobox popup and only - * the color is shown if not open. This combines the clarity of the - * popup with the ease of use of just using the space for modifiers. - * - * Other instances are for example time and especially date picker, - * which only show the currently activated time/data and hide the - * selection logic inside the combobox popup. - */ - static float chart_selection = 8.0f; - static int current_weapon = 0; - static int check_values[5]; - static float position[3]; - static struct zr_color combo_color = {130, 50, 50, 255}; - static struct zr_color combo_color2 = {130, 180, 50, 255}; - static size_t prog_a = 20, prog_b = 40, prog_c = 10, prog_d = 90; - static const char *weapons[] = {"Fist","Pistol","Shotgun","Plasma","BFG"}; - - char buffer[64]; - size_t sum = 0; - struct zr_panel combo; - - /* default combobox */ - zr_layout_row_static(ctx, 25, 200, 1); - current_weapon = zr_combo(ctx, weapons, LEN(weapons), current_weapon, 25); - - /* slider color combobox */ - if (zr_combo_begin_color(ctx, &combo, combo_color, 200)) { - float ratios[] = {0.15f, 0.85f}; - zr_layout_row(ctx, ZR_DYNAMIC, 30, 2, ratios); - zr_label(ctx, "R:", ZR_TEXT_LEFT); - combo_color.r = (zr_byte)zr_slide_int(ctx, 0, combo_color.r, 255, 5); - zr_label(ctx, "G:", ZR_TEXT_LEFT); - combo_color.g = (zr_byte)zr_slide_int(ctx, 0, combo_color.g, 255, 5); - zr_label(ctx, "B:", ZR_TEXT_LEFT); - combo_color.b = (zr_byte)zr_slide_int(ctx, 0, combo_color.b, 255, 5); - zr_label(ctx, "A:", ZR_TEXT_LEFT); - combo_color.a = (zr_byte)zr_slide_int(ctx, 0, combo_color.a , 255, 5); - zr_combo_end(ctx); - } - - /* complex color combobox */ - if (zr_combo_begin_color(ctx, &combo, combo_color2, 400)) { - enum color_mode {COL_RGB, COL_HSV}; - static int col_mode = COL_RGB; - #ifndef DEMO_DO_NOT_USE_COLOR_PICKER - zr_layout_row_dynamic(ctx, 120, 1); - combo_color2 = zr_color_picker(ctx, combo_color2, ZR_RGBA); - #endif - - zr_layout_row_dynamic(ctx, 25, 2); - col_mode = zr_option_label(ctx, "RGB", col_mode == COL_RGB) ? COL_RGB : col_mode; - col_mode = zr_option_label(ctx, "HSV", col_mode == COL_HSV) ? COL_HSV : col_mode; - - zr_layout_row_dynamic(ctx, 25, 1); - if (col_mode == COL_RGB) { - combo_color2.r = (zr_byte)zr_propertyi(ctx, "#R:", 0, combo_color2.r, 255, 1,1); - combo_color2.g = (zr_byte)zr_propertyi(ctx, "#G:", 0, combo_color2.g, 255, 1,1); - combo_color2.b = (zr_byte)zr_propertyi(ctx, "#B:", 0, combo_color2.b, 255, 1,1); - combo_color2.a = (zr_byte)zr_propertyi(ctx, "#A:", 0, combo_color2.a, 255, 1,1); - } else { - zr_byte tmp[4]; - zr_color_hsva_bv(tmp, combo_color2); - tmp[0] = (zr_byte)zr_propertyi(ctx, "#H:", 0, tmp[0], 255, 1,1); - tmp[1] = (zr_byte)zr_propertyi(ctx, "#S:", 0, tmp[1], 255, 1,1); - tmp[2] = (zr_byte)zr_propertyi(ctx, "#V:", 0, tmp[2], 255, 1,1); - tmp[3] = (zr_byte)zr_propertyi(ctx, "#A:", 0, tmp[3], 255, 1,1); - combo_color2 = zr_hsva_bv(tmp); - } - zr_combo_end(ctx); - } - - /* progressbar combobox */ - sum = prog_a + prog_b + prog_c + prog_d; - sprintf(buffer, "%lu", sum); - if (zr_combo_begin_label(ctx, &combo, buffer, 200)) { - zr_layout_row_dynamic(ctx, 30, 1); - zr_progress(ctx, &prog_a, 100, ZR_MODIFIABLE); - zr_progress(ctx, &prog_b, 100, ZR_MODIFIABLE); - zr_progress(ctx, &prog_c, 100, ZR_MODIFIABLE); - zr_progress(ctx, &prog_d, 100, ZR_MODIFIABLE); - zr_combo_end(ctx); - } - - /* checkbox combobox */ - sum = (size_t)(check_values[0] + check_values[1] + check_values[2] + check_values[3] + check_values[4]); - sprintf(buffer, "%lu", sum); - if (zr_combo_begin_label(ctx, &combo, buffer, 200)) { - zr_layout_row_dynamic(ctx, 30, 1); - zr_checkbox_label(ctx, weapons[0], &check_values[0]); - zr_checkbox_label(ctx, weapons[1], &check_values[1]); - zr_checkbox_label(ctx, weapons[2], &check_values[2]); - zr_checkbox_label(ctx, weapons[3], &check_values[3]); - zr_combo_end(ctx); - } - - /* complex text combobox */ - sprintf(buffer, "%.2f, %.2f, %.2f", position[0], position[1],position[2]); - if (zr_combo_begin_label(ctx, &combo, buffer, 200)) { - zr_layout_row_dynamic(ctx, 25, 1); - zr_property_float(ctx, "#X:", -1024.0f, &position[0], 1024.0f, 1,0.5f); - zr_property_float(ctx, "#Y:", -1024.0f, &position[1], 1024.0f, 1,0.5f); - zr_property_float(ctx, "#Z:", -1024.0f, &position[2], 1024.0f, 1,0.5f); - zr_combo_end(ctx); - } - - /* chart combobox */ - sprintf(buffer, "%.1f", chart_selection); - if (zr_combo_begin_label(ctx, &combo, buffer, 250)) { - size_t i = 0; - static const float values[]={26.0f,13.0f,30.0f,15.0f,25.0f,10.0f,20.0f,40.0f, 12.0f, 8.0f, 22.0f, 28.0f, 5.0f}; - zr_layout_row_dynamic(ctx, 150, 1); - zr_chart_begin(ctx, ZR_CHART_COLUMN, LEN(values), 0, 50); - for (i = 0; i < LEN(values); ++i) { - zr_flags res = zr_chart_push(ctx, values[i]); - if (res & ZR_CHART_CLICKED) { - chart_selection = values[i]; - zr_combo_close(ctx); - } - } - zr_chart_end(ctx); - zr_combo_end(ctx); - } - - { - static int time_selected = 0; - static int date_selected = 0; - static struct tm sel_time; - static struct tm sel_date; - if (!time_selected || !date_selected) { - /* keep time and date updated if nothing is selected */ - time_t cur_time = time(0); - struct tm *n = localtime(&cur_time); - if (!time_selected) - memcpy(&sel_time, n, sizeof(struct tm)); - if (!date_selected) - memcpy(&sel_date, n, sizeof(struct tm)); - } - - /* time combobox */ - sprintf(buffer, "%02d:%02d:%02d", sel_time.tm_hour, sel_time.tm_min, sel_time.tm_sec); - if (zr_combo_begin_label(ctx, &combo, buffer, 250)) { - time_selected = 1; - zr_layout_row_dynamic(ctx, 25, 1); - sel_time.tm_sec = zr_propertyi(ctx, "#S:", 0, sel_time.tm_sec, 60, 1, 1); - sel_time.tm_min = zr_propertyi(ctx, "#M:", 0, sel_time.tm_min, 60, 1, 1); - sel_time.tm_hour = zr_propertyi(ctx, "#H:", 0, sel_time.tm_hour, 23, 1, 1); - zr_combo_end(ctx); - } - - /* date combobox */ - zr_layout_row_static(ctx, 25, 350, 1); - sprintf(buffer, "%02d-%02d-%02d", sel_date.tm_mday, sel_date.tm_mon+1, sel_date.tm_year+1900); - if (zr_combo_begin_label(ctx, &combo, buffer, 400)) - { - int i = 0; - const char *month[] = {"January", "February", "March", "Apil", "May", "June", "July", "August", "September", "Ocotober", "November", "December"}; - const char *week_days[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; - const int month_days[] = {31,28,31,30,31,30,31,31,30,31,30,31}; - int year = sel_date.tm_year+1900; - int leap_year = (!(year % 4) && ((year % 100))) || !(year % 400); - int days = (sel_date.tm_mon == 1) ? - month_days[sel_date.tm_mon] + leap_year: - month_days[sel_date.tm_mon]; - - /* header with month and year */ - date_selected = 1; - zr_layout_row_begin(ctx, ZR_DYNAMIC, 20, 3); - zr_layout_row_push(ctx, 0.05f); - if (zr_button_symbol(ctx, ZR_SYMBOL_TRIANGLE_LEFT, ZR_BUTTON_DEFAULT)) { - if (sel_date.tm_mon == 0) { - sel_date.tm_mon = 11; - sel_date.tm_year = MAX(0, sel_date.tm_year-1); - } else sel_date.tm_mon--; - } - zr_layout_row_push(ctx, 0.9f); - sprintf(buffer, "%s %d", month[sel_date.tm_mon], year); - zr_label(ctx, buffer, ZR_TEXT_CENTERED); - zr_layout_row_push(ctx, 0.05f); - if (zr_button_symbol(ctx, ZR_SYMBOL_TRIANGLE_RIGHT, ZR_BUTTON_DEFAULT)) { - if (sel_date.tm_mon == 11) { - sel_date.tm_mon = 0; - sel_date.tm_year++; - } else sel_date.tm_mon++; - } - zr_layout_row_end(ctx); - - /* good old week day formula (double because precision) */ - {int year_n = (sel_date.tm_mon < 2) ? year-1: year; - int y = year_n % 100; - int c = year_n / 100; - int y4 = (int)((float)y / 4); - int c4 = (int)((float)c / 4); - int m = (int)(2.6 * (double)(((sel_date.tm_mon + 10) % 12) + 1) - 0.2); - int week_day = (((1 + m + y + y4 + c4 - 2 * c) % 7) + 7) % 7; - - /* weekdays */ - zr_layout_row_dynamic(ctx, 35, 7); - for (i = 0; i < (int)LEN(week_days); ++i) - zr_label(ctx, week_days[i], ZR_TEXT_CENTERED); - - /* days */ - if (week_day > 0) zr_spacing(ctx, week_day); - for (i = 1; i <= days; ++i) { - sprintf(buffer, "%d", i); - if (zr_button_label(ctx, buffer, ZR_BUTTON_DEFAULT)) { - sel_date.tm_mday = i; - zr_combo_close(ctx); - } - }} - zr_combo_end(ctx); - } - } - - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Input", ZR_MINIMIZED)) - { - static const float ratio[] = {120, 150}; - static char field_buffer[64]; - static char text[9][64]; - static size_t text_len[9]; - static char box_buffer[512]; - static size_t field_len; - static size_t box_len; - zr_flags active; - - zr_layout_row(ctx, ZR_STATIC, 25, 2, ratio); - zr_label(ctx, "Default:", ZR_TEXT_LEFT); - - zr_edit_string(ctx, ZR_EDIT_CURSOR, text[0], &text_len[0], 64, zr_filter_default); - zr_label(ctx, "Int:", ZR_TEXT_LEFT); - zr_edit_string(ctx, ZR_EDIT_CURSOR, text[1], &text_len[1], 64, zr_filter_decimal); - zr_label(ctx, "Float:", ZR_TEXT_LEFT); - zr_edit_string(ctx, ZR_EDIT_CURSOR, text[2], &text_len[2], 64, zr_filter_float); - zr_label(ctx, "Hex:", ZR_TEXT_LEFT); - zr_edit_string(ctx, ZR_EDIT_CURSOR, text[4], &text_len[4], 64, zr_filter_hex); - zr_label(ctx, "Octal:", ZR_TEXT_LEFT); - zr_edit_string(ctx, ZR_EDIT_CURSOR, text[5], &text_len[5], 64, zr_filter_oct); - zr_label(ctx, "Binary:", ZR_TEXT_LEFT); - zr_edit_string(ctx, ZR_EDIT_CURSOR, text[6], &text_len[6], 64, zr_filter_binary); - - zr_label(ctx, "Password:", ZR_TEXT_LEFT); - { - size_t i = 0; - size_t old_len = text_len[8]; - char buffer[64]; - for (i = 0; i < text_len[8]; ++i) buffer[i] = '*'; - zr_edit_string(ctx, ZR_EDIT_SIMPLE, buffer, &text_len[8], 64, zr_filter_default); - if (old_len < text_len[8]) - memcpy(&text[8][old_len], &buffer[old_len], text_len[8] - old_len); - } - - zr_label(ctx, "Field:", ZR_TEXT_LEFT); - zr_edit_string(ctx, ZR_EDIT_FIELD, field_buffer, &field_len, 64, zr_filter_default); - - zr_label(ctx, "Box:", ZR_TEXT_LEFT); - zr_layout_row_static(ctx, 180, 278, 1); - zr_edit_string(ctx, ZR_EDIT_BOX, box_buffer, &box_len, 512, zr_filter_default); - - zr_layout_row(ctx, ZR_STATIC, 25, 2, ratio); - active = zr_edit_string(ctx, ZR_EDIT_FIELD|ZR_EDIT_SIGCOMIT, text[7], &text_len[7], 64, zr_filter_ascii); - if (zr_button_label(ctx, "Submit", ZR_BUTTON_DEFAULT) || - (active & ZR_EDIT_COMMITED)) - { - text[7][text_len[7]] = '\n'; - text_len[7]++; - memcpy(&box_buffer[box_len], &text[7], text_len[7]); - box_len += text_len[7]; - text_len[7] = 0; - } - zr_layout_row_end(ctx); - zr_layout_pop(ctx); - } - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Chart", ZR_MINIMIZED)) - { - /* Chart Widgets - * This library has two different rather simple charts. The line and the - * column chart. Both provide a simple way of visualizing values and - * have a retain mode and immedidate mode API version. For the retain - * mode version `zr_plot` and `zr_plot_function` you either provide - * an array or a callback to call to handle drawing the graph. - * For the immediate mode version you start by calling `zr_chart_begin` - * and need to provide min and max values for scaling on the Y-axis. - * and then call `zr_chart_push` to push values into the chart. - * Finally `zr_chart_end` needs to be called to end the process. */ - float id = 0; - static int col_index = -1; - static int line_index = -1; - float step = (2*3.141592654f) / 32; - - int i; - int index = -1; - struct zr_rect bounds; - - /* line chart */ - id = 0; - index = -1; - zr_layout_row_dynamic(ctx, 100, 1); - bounds = zr_widget_bounds(ctx); - if (zr_chart_begin(ctx, ZR_CHART_LINES, 32, -1.0f, 1.0f)) { - for (i = 0; i < 32; ++i) { - zr_flags res = zr_chart_push(ctx, (float)cos(id)); - if (res & ZR_CHART_HOVERING) - index = (int)i; - if (res & ZR_CHART_CLICKED) - line_index = (int)i; - id += step; - } - zr_chart_end(ctx); - } - - if (index != -1) { - char buffer[ZR_MAX_NUMBER_BUFFER]; - float val = (float)cos((float)index*step); - sprintf(buffer, "Value: %.2f", val); - zr_tooltip(ctx, buffer); - } - if (line_index != -1) { - zr_layout_row_dynamic(ctx, 20, 1); - zr_labelf(ctx, ZR_TEXT_LEFT, "Selected value: %.2f", (float)cos((float)index*step)); - } - - /* column chart */ - zr_layout_row_dynamic(ctx, 100, 1); - bounds = zr_widget_bounds(ctx); - if (zr_chart_begin(ctx, ZR_CHART_COLUMN, 32, 0.0f, 1.0f)) { - for (i = 0; i < 32; ++i) { - zr_flags res = zr_chart_push(ctx, (float)fabs(sin(id))); - if (res & ZR_CHART_HOVERING) - index = (int)i; - if (res & ZR_CHART_CLICKED) - col_index = (int)i; - id += step; - } - zr_chart_end(ctx); - } - if (index != -1) { - char buffer[ZR_MAX_NUMBER_BUFFER]; - sprintf(buffer, "Value: %.2f", (float)fabs(sin(step * (float)index))); - zr_tooltip(ctx, buffer); - } - if (col_index != -1) { - zr_layout_row_dynamic(ctx, 20, 1); - zr_labelf(ctx, ZR_TEXT_LEFT, "Selected value: %.2f", (float)fabs(sin(step * (float)col_index))); - } - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Popup", ZR_MINIMIZED)) - { - static struct zr_color color = {255,0,0, 255}; - static int select[4]; - static int popup_active; - const struct zr_input *in = &ctx->input; - struct zr_rect bounds; - - /* menu contextual */ - zr_layout_row_static(ctx, 30, 150, 1); - bounds = zr_widget_bounds(ctx); - zr_label(ctx, "Right click me for menu", ZR_TEXT_LEFT); - - if (zr_contextual_begin(ctx, &menu, 0, zr_vec2(100, 300), bounds)) { - static size_t prog = 40; - static int slider = 10; - - zr_layout_row_dynamic(ctx, 25, 1); - zr_checkbox_label(ctx, "Menu", &show_menu); - zr_progress(ctx, &prog, 100, ZR_MODIFIABLE); - zr_slider_int(ctx, 0, &slider, 16, 1); - if (zr_contextual_item_label(ctx, "About", ZR_TEXT_CENTERED)) - show_app_about = zr_true; - zr_selectable_label(ctx, select[0]?"Unselect":"Select", ZR_TEXT_LEFT, &select[0]); - zr_selectable_label(ctx, select[1]?"Unselect":"Select", ZR_TEXT_LEFT, &select[1]); - zr_selectable_label(ctx, select[2]?"Unselect":"Select", ZR_TEXT_LEFT, &select[2]); - zr_selectable_label(ctx, select[3]?"Unselect":"Select", ZR_TEXT_LEFT, &select[3]); - zr_contextual_end(ctx); - } - - /* color contextual */ - zr_layout_row_begin(ctx, ZR_STATIC, 30, 2); - zr_layout_row_push(ctx, 100); - zr_label(ctx, "Right Click here:", ZR_TEXT_LEFT); - zr_layout_row_push(ctx, 50); - bounds = zr_widget_bounds(ctx); - zr_button_color(ctx, color, ZR_BUTTON_DEFAULT); - zr_layout_row_end(ctx); - - if (zr_contextual_begin(ctx, &menu, 0, zr_vec2(350, 60), bounds)) { - zr_layout_row_dynamic(ctx, 30, 4); - color.r = (zr_byte)zr_propertyi(ctx, "#r", 0, color.r, 255, 1, 1); - color.g = (zr_byte)zr_propertyi(ctx, "#g", 0, color.g, 255, 1, 1); - color.b = (zr_byte)zr_propertyi(ctx, "#b", 0, color.b, 255, 1, 1); - color.a = (zr_byte)zr_propertyi(ctx, "#a", 0, color.a, 255, 1, 1); - zr_contextual_end(ctx); - } - - /* popup */ - zr_layout_row_begin(ctx, ZR_STATIC, 30, 2); - zr_layout_row_push(ctx, 100); - zr_label(ctx, "Popup:", ZR_TEXT_LEFT); - zr_layout_row_push(ctx, 50); - if (zr_button_label(ctx, "Popup", ZR_BUTTON_DEFAULT)) - popup_active = 1; - zr_layout_row_end(ctx); - - if (popup_active) - { - static struct zr_rect s = {20, 100, 220, 150}; - if (zr_popup_begin(ctx, &menu, ZR_POPUP_STATIC, "Error", ZR_WINDOW_DYNAMIC, s)) - { - zr_layout_row_dynamic(ctx, 25, 1); - zr_label(ctx, "A terrible error as occured", ZR_TEXT_LEFT); - zr_layout_row_dynamic(ctx, 25, 2); - if (zr_button_label(ctx, "OK", ZR_BUTTON_DEFAULT)) { - popup_active = 0; - zr_popup_close(ctx); - } - if (zr_button_label(ctx, "Cancel", ZR_BUTTON_DEFAULT)) { - popup_active = 0; - zr_popup_close(ctx); - } - zr_popup_end(ctx); - } else popup_active = zr_false; - } - - /* tooltip */ - zr_layout_row_static(ctx, 30, 150, 1); - bounds = zr_widget_bounds(ctx); - zr_label(ctx, "Hover me for tooltip", ZR_TEXT_LEFT); - if (zr_input_is_mouse_hovering_rect(in, bounds)) - zr_tooltip(ctx, "This is a tooltip"); - - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Layout", ZR_MINIMIZED)) - { - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Widget", ZR_MINIMIZED)) - { - float ratio_two[] = {0.2f, 0.6f, 0.2f}; - float width_two[] = {100, 200, 50}; - - zr_layout_row_dynamic(ctx, 30, 1); - zr_label(ctx, "Dynamic fixed column layout with generated position and size:", ZR_TEXT_LEFT); - zr_layout_row_dynamic(ctx, 30, 3); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - - zr_layout_row_dynamic(ctx, 30, 1); - zr_label(ctx, "static fixed column layout with generated position and size:", ZR_TEXT_LEFT); - zr_layout_row_static(ctx, 30, 100, 3); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - - zr_layout_row_dynamic(ctx, 30, 1); - zr_label(ctx, "Dynamic array-based custom column layout with generated position and custom size:",ZR_TEXT_LEFT); - zr_layout_row(ctx, ZR_DYNAMIC, 30, 3, ratio_two); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - - zr_layout_row_dynamic(ctx, 30, 1); - zr_label(ctx, "Static array-based custom column layout with generated position and custom size:",ZR_TEXT_LEFT ); - zr_layout_row(ctx, ZR_STATIC, 30, 3, width_two); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - - zr_layout_row_dynamic(ctx, 30, 1); - zr_label(ctx, "Dynamic immediate mode custom column layout with generated position and custom size:",ZR_TEXT_LEFT); - zr_layout_row_begin(ctx, ZR_DYNAMIC, 30, 3); - zr_layout_row_push(ctx, 0.2f); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_row_push(ctx, 0.6f); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_row_push(ctx, 0.2f); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_row_end(ctx); - - zr_layout_row_dynamic(ctx, 30, 1); - zr_label(ctx, "Static immmediate mode custom column layout with generated position and custom size:", ZR_TEXT_LEFT); - zr_layout_row_begin(ctx, ZR_STATIC, 30, 3); - zr_layout_row_push(ctx, 100); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_row_push(ctx, 200); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_row_push(ctx, 50); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_row_end(ctx); - - zr_layout_row_dynamic(ctx, 30, 1); - zr_label(ctx, "Static free space with custom position and custom size:", ZR_TEXT_LEFT); - zr_layout_space_begin(ctx, ZR_STATIC, 120, 4); - zr_layout_space_push(ctx, zr_rect(100, 0, 100, 30)); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_space_push(ctx, zr_rect(0, 15, 100, 30)); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_space_push(ctx, zr_rect(200, 15, 100, 30)); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_space_push(ctx, zr_rect(100, 30, 100, 30)); - zr_button_label(ctx, "button", ZR_BUTTON_DEFAULT); - zr_layout_space_end(ctx); - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Group", ZR_MINIMIZED)) - { - static int group_titlebar = zr_false; - static int group_border = zr_true; - static int group_no_scrollbar = zr_false; - static int group_width = 320; - static int group_height = 200; - struct zr_panel tab; - - zr_flags group_flags = 0; - if (group_border) group_flags |= ZR_WINDOW_BORDER; - if (group_no_scrollbar) group_flags |= ZR_WINDOW_NO_SCROLLBAR; - if (group_titlebar) group_flags |= ZR_WINDOW_TITLE; - - zr_layout_row_dynamic(ctx, 30, 3); - zr_checkbox_label(ctx, "Titlebar", &group_titlebar); - zr_checkbox_label(ctx, "Border", &group_border); - zr_checkbox_label(ctx, "No Scrollbar", &group_no_scrollbar); - - zr_layout_row_begin(ctx, ZR_STATIC, 22, 2); - zr_layout_row_push(ctx, 50); - zr_label(ctx, "size:", ZR_TEXT_LEFT); - zr_layout_row_push(ctx, 130); - zr_property_int(ctx, "#Width:", 100, &group_width, 500, 10, 1); - zr_layout_row_push(ctx, 130); - zr_property_int(ctx, "#Height:", 100, &group_height, 500, 10, 1); - zr_layout_row_end(ctx); - - zr_layout_row_static(ctx, (float)group_height, group_width, 2); - if (zr_group_begin(ctx, &tab, "Group", group_flags)) { - int i = 0; - static int selected[16]; - zr_layout_row_static(ctx, 18, 100, 1); - for (i = 0; i < 16; ++i) - zr_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", ZR_TEXT_CENTERED, &selected[i]); - zr_group_end(ctx); - } - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Simple", ZR_MINIMIZED)) - { - struct zr_panel tab; - zr_layout_row_dynamic(ctx, 300, 2); - if (zr_group_begin(ctx, &tab, "Group_Without_Border", 0)) { - int i = 0; - char buffer[64]; - zr_layout_row_static(ctx, 18, 150, 1); - for (i = 0; i < 64; ++i) { - sprintf(buffer, "0x%02x", i); - zr_labelf(ctx, ZR_TEXT_LEFT, "%s: scrollable region", buffer); - } - zr_group_end(ctx); - } - if (zr_group_begin(ctx, &tab, "Group_With_Border", ZR_WINDOW_BORDER)) { - int i = 0; - char buffer[64]; - zr_layout_row_dynamic(ctx, 25, 2); - for (i = 0; i < 64; ++i) { - sprintf(buffer, "%08d", ((((i%7)*10)^32))+(64+(i%2)*2)); - zr_button_label(ctx, buffer, ZR_BUTTON_DEFAULT); - } - zr_group_end(ctx); - } - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Complex", ZR_MINIMIZED)) - { - int i; - struct zr_panel tab; - zr_layout_space_begin(ctx, ZR_STATIC, 500, 64); - zr_layout_space_push(ctx, zr_rect(0,0,150,500)); - if (zr_group_begin(ctx, &tab, "Group_left", ZR_WINDOW_BORDER)) { - static int selected[32]; - zr_layout_row_static(ctx, 18, 100, 1); - for (i = 0; i < 32; ++i) - zr_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", ZR_TEXT_CENTERED, &selected[i]); - zr_group_end(ctx); - } - - zr_layout_space_push(ctx, zr_rect(160,0,150,240)); - if (zr_group_begin(ctx, &tab, "Group_top", ZR_WINDOW_BORDER)) { - zr_layout_row_dynamic(ctx, 25, 1); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - - zr_layout_space_push(ctx, zr_rect(160,250,150,250)); - if (zr_group_begin(ctx, &tab, "Group_buttom", ZR_WINDOW_BORDER)) { - zr_layout_row_dynamic(ctx, 25, 1); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - - zr_layout_space_push(ctx, zr_rect(320,0,150,150)); - if (zr_group_begin(ctx, &tab, "Group_right_top", ZR_WINDOW_BORDER)) { - static int selected[4]; - zr_layout_row_static(ctx, 18, 100, 1); - for (i = 0; i < 4; ++i) - zr_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", ZR_TEXT_CENTERED, &selected[i]); - zr_group_end(ctx); - } - - zr_layout_space_push(ctx, zr_rect(320,160,150,150)); - if (zr_group_begin(ctx, &tab, "Group_right_center", ZR_WINDOW_BORDER)) { - static int selected[4]; - zr_layout_row_static(ctx, 18, 100, 1); - for (i = 0; i < 4; ++i) - zr_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", ZR_TEXT_CENTERED, &selected[i]); - zr_group_end(ctx); - } - - zr_layout_space_push(ctx, zr_rect(320,320,150,150)); - if (zr_group_begin(ctx, &tab, "Group_right_bottom", ZR_WINDOW_BORDER)) { - static int selected[4]; - zr_layout_row_static(ctx, 18, 100, 1); - for (i = 0; i < 4; ++i) - zr_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", ZR_TEXT_CENTERED, &selected[i]); - zr_group_end(ctx); - } - zr_layout_space_end(ctx); - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Splitter", ZR_MINIMIZED)) - { - const struct zr_input *in = &ctx->input; - zr_layout_row_static(ctx, 20, 320, 1); - zr_label(ctx, "Use slider and spinner to change tile size", ZR_TEXT_LEFT); - zr_label(ctx, "Drag the space between tiles to change tile ratio", ZR_TEXT_LEFT); - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Vertical", ZR_MINIMIZED)) - { - static float a = 100, b = 100, c = 100; - struct zr_rect bounds; - struct zr_panel sub; - - float row_layout[5]; - row_layout[0] = a; - row_layout[1] = 8; - row_layout[2] = b; - row_layout[3] = 8; - row_layout[4] = c; - - /* header */ - zr_layout_row_static(ctx, 30, 100, 2); - zr_label(ctx, "left:", ZR_TEXT_LEFT); - zr_slider_float(ctx, 10.0f, &a, 200.0f, 10.0f); - - zr_label(ctx, "middle:", ZR_TEXT_LEFT); - zr_slider_float(ctx, 10.0f, &b, 200.0f, 10.0f); - - zr_label(ctx, "right:", ZR_TEXT_LEFT); - zr_slider_float(ctx, 10.0f, &c, 200.0f, 10.0f); - - /* tiles */ - zr_layout_row(ctx, ZR_STATIC, 200, 5, row_layout); - - /* left space */ - if (zr_group_begin(ctx, &sub, "left", ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR)) { - zr_layout_row_dynamic(ctx, 25, 1); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - - /* scaler */ - bounds = zr_widget_bounds(ctx); - zr_spacing(ctx, 1); - if ((zr_input_is_mouse_hovering_rect(in, bounds) || - zr_input_is_mouse_prev_hovering_rect(in, bounds)) && - zr_input_is_mouse_down(in, ZR_BUTTON_LEFT)) - { - a = row_layout[0] + in->mouse.delta.x; - b = row_layout[2] - in->mouse.delta.x; - } - - /* middle space */ - if (zr_group_begin(ctx, &sub, "center", ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR)) { - zr_layout_row_dynamic(ctx, 25, 1); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - - /* scaler */ - bounds = zr_widget_bounds(ctx); - zr_spacing(ctx, 1); - if ((zr_input_is_mouse_hovering_rect(in, bounds) || - zr_input_is_mouse_prev_hovering_rect(in, bounds)) && - zr_input_is_mouse_down(in, ZR_BUTTON_LEFT)) - { - b = (row_layout[2] + in->mouse.delta.x); - c = (row_layout[4] - in->mouse.delta.x); - } - - /* right space */ - if (zr_group_begin(ctx, &sub, "right", ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR)) { - zr_layout_row_dynamic(ctx, 25, 1); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - - zr_layout_pop(ctx); - } - - if (zr_layout_push(ctx, ZR_LAYOUT_NODE, "Horizontal", ZR_MINIMIZED)) - { - static float a = 100, b = 100, c = 100; - struct zr_panel sub; - struct zr_rect bounds; - - /* header */ - zr_layout_row_static(ctx, 30, 100, 2); - zr_label(ctx, "top:", ZR_TEXT_LEFT); - zr_slider_float(ctx, 10.0f, &a, 200.0f, 10.0f); - - zr_label(ctx, "middle:", ZR_TEXT_LEFT); - zr_slider_float(ctx, 10.0f, &b, 200.0f, 10.0f); - - zr_label(ctx, "bottom:", ZR_TEXT_LEFT); - zr_slider_float(ctx, 10.0f, &c, 200.0f, 10.0f); - - /* top space */ - zr_layout_row_dynamic(ctx, a, 1); - if (zr_group_begin(ctx, &sub, "top", ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_BORDER)) { - zr_layout_row_dynamic(ctx, 25, 3); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - - /* scaler */ - zr_layout_row_dynamic(ctx, 8, 1); - bounds = zr_widget_bounds(ctx); - zr_spacing(ctx, 1); - if ((zr_input_is_mouse_hovering_rect(in, bounds) || - zr_input_is_mouse_prev_hovering_rect(in, bounds)) && - zr_input_is_mouse_down(in, ZR_BUTTON_LEFT)) - { - a = a + in->mouse.delta.y; - b = b - in->mouse.delta.y; - } - - /* middle space */ - zr_layout_row_dynamic(ctx, b, 1); - if (zr_group_begin(ctx, &sub, "middle", ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_BORDER)) { - zr_layout_row_dynamic(ctx, 25, 3); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - - { - /* scaler */ - zr_layout_row_dynamic(ctx, 8, 1); - bounds = zr_widget_bounds(ctx); - if ((zr_input_is_mouse_hovering_rect(in, bounds) || - zr_input_is_mouse_prev_hovering_rect(in, bounds)) && - zr_input_is_mouse_down(in, ZR_BUTTON_LEFT)) - { - b = b + in->mouse.delta.y; - c = c - in->mouse.delta.y; - } - } - - /* bottom space */ - zr_layout_row_dynamic(ctx, c, 1); - if (zr_group_begin(ctx, &sub, "bottom", ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_BORDER)) { - zr_layout_row_dynamic(ctx, 25, 3); - zr_button_label(ctx, "#FFAA", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFBB", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFCC", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFDD", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFEE", ZR_BUTTON_DEFAULT); - zr_button_label(ctx, "#FFFF", ZR_BUTTON_DEFAULT); - zr_group_end(ctx); - } - zr_layout_pop(ctx); - } - zr_layout_pop(ctx); - } - zr_layout_pop(ctx); - } - } - zr_end(ctx); -} - -/* =============================================================== - * - * CUSTOM WIDGET - * - * ===============================================================*/ -static int -ui_piemenu(struct zr_context *ctx, struct zr_vec2 pos, float radius, - struct zr_image *icons, int item_count) -{ - int ret = -1; - struct zr_rect total_space; - struct zr_panel popup; - struct zr_rect bounds; - int active_item = 0; - - /* pie menu popup */ - struct zr_color border = ctx->style.window.border_color; - struct zr_style_item background = ctx->style.window.fixed_background; - ctx->style.window.fixed_background = zr_style_item_hide(); - ctx->style.window.border_color = zr_rgba(0,0,0,0); - - total_space = zr_window_get_content_region(ctx); - zr_popup_begin(ctx, &popup, ZR_POPUP_STATIC, "piemenu", ZR_WINDOW_NO_SCROLLBAR, - zr_rect(pos.x - total_space.x - radius, pos.y - radius - total_space.y, - 2*radius,2*radius)); - - total_space = zr_window_get_content_region(ctx); - zr_layout_row_dynamic(ctx, total_space.h, 1); - { - int i = 0; - struct zr_command_buffer* out = zr_window_get_canvas(ctx); - const struct zr_input *in = &ctx->input; - { - /* allocate complete popup space for the menu */ - enum zr_widget_layout_states state; - total_space = zr_window_get_content_region(ctx); - total_space.x = total_space.y = 0; - state = zr_widget(&bounds, ctx); - } - - /* outer circle */ - zr_fill_circle(out, bounds, zr_rgb(50,50,50)); - { - /* circle buttons */ - float step = (2 * 3.141592654f) / (float)(MAX(1,item_count)); - float a_min = 0; float a_max = step; - - struct zr_vec2 center = zr_vec2(bounds.x + bounds.w / 2.0f, bounds.y + bounds.h / 2.0f); - struct zr_vec2 drag = zr_vec2(in->mouse.pos.x - center.x, in->mouse.pos.y - center.y); - float angle = (float)atan2(drag.y, drag.x); - if (angle < -0.0f) angle += 2.0f * 3.141592654f; - active_item = (int)(angle/step); - - for (i = 0; i < item_count; ++i) { - struct zr_rect content; - float rx, ry, dx, dy, a; - zr_fill_arc(out, center.x, center.y, (bounds.w/2.0f), - a_min, a_max, (active_item == i) ? zr_rgb(45,100,255): zr_rgb(60,60,60)); - - /* seperator line */ - rx = bounds.w/2.0f; ry = 0; - dx = rx * (float)cos(a_min) - ry * (float)sin(a_min); - dy = rx * (float)sin(a_min) + ry * (float)cos(a_min); - zr_stroke_line(out, center.x, center.y, - center.x + dx, center.y + dy, 1.0f, zr_rgb(50,50,50)); - - /* button content */ - a = a_min + (a_max - a_min)/2.0f; - rx = bounds.w/2.5f; ry = 0; - content.w = 30; content.h = 30; - content.x = center.x + ((rx * (float)cos(a) - ry * (float)sin(a)) - content.w/2.0f); - content.y = center.y + (rx * (float)sin(a) + ry * (float)cos(a) - content.h/2.0f); - zr_draw_image(out, content, &icons[i]); - a_min = a_max; a_max += step; - } - } - { - /* inner circle */ - struct zr_rect inner; - inner.x = bounds.x + bounds.w/2 - bounds.w/4; - inner.y = bounds.y + bounds.h/2 - bounds.h/4; - inner.w = bounds.w/2; inner.h = bounds.h/2; - zr_fill_circle(out, inner, zr_rgb(45,45,45)); - - /* active icon content */ - bounds.w = inner.w / 2.0f; - bounds.h = inner.h / 2.0f; - bounds.x = inner.x + inner.w/2 - bounds.w/2; - bounds.y = inner.y + inner.h/2 - bounds.h/2; - zr_draw_image(out, bounds, &icons[active_item]); - } - } - zr_layout_space_end(ctx); - zr_popup_end(ctx); - - ctx->style.window.fixed_background = background; - ctx->style.window.border_color = border; - - if (!zr_input_is_mouse_down(&ctx->input, ZR_BUTTON_RIGHT)) - return active_item; - else return ret; -} - -/* =============================================================== - * - * GRID - * - * ===============================================================*/ -static void -grid_demo(struct zr_context *ctx) -{ - static char text[3][64]; - static size_t text_len[3]; - static const char *items[] = {"Item 0","item 1","item 2"}; - static int selected_item = 0; - static int check = 1; - struct zr_panel layout; - - int i; - struct zr_panel combo; - ctx->style.font.height = 20; - if (zr_begin(ctx, &layout, "Grid Demo", zr_rect(600, 350, 275, 250), - ZR_WINDOW_TITLE|ZR_WINDOW_BORDER|ZR_WINDOW_MOVABLE| - ZR_WINDOW_BORDER_HEADER|ZR_WINDOW_NO_SCROLLBAR)) - { - ctx->style.font.height = 18; - zr_layout_row_dynamic(ctx, 30, 2); - zr_label(ctx, "Floating point:", ZR_TEXT_RIGHT); - zr_edit_string(ctx, ZR_EDIT_FIELD, text[0], &text_len[0], 64, zr_filter_float); - zr_label(ctx, "Hexadeximal:", ZR_TEXT_RIGHT); - zr_edit_string(ctx, ZR_EDIT_FIELD, text[1], &text_len[1], 64, zr_filter_hex); - zr_label(ctx, "Binary:", ZR_TEXT_RIGHT); - zr_edit_string(ctx, ZR_EDIT_FIELD, text[2], &text_len[2], 64, zr_filter_binary); - zr_label(ctx, "Checkbox:", ZR_TEXT_RIGHT); - zr_checkbox_label(ctx, "Check me", &check); - zr_label(ctx, "Combobox:", ZR_TEXT_RIGHT); - - if (zr_combo_begin_label(ctx, &combo, items[selected_item], 200)) { - zr_layout_row_dynamic(ctx, 30, 1); - for (i = 0; i < 3; ++i) - if (zr_combo_item_label(ctx, items[i], ZR_TEXT_LEFT)) - selected_item = i; - zr_combo_end(ctx); - } - } - zr_end(ctx); - ctx->style.font.height = 14; -} - -/* =============================================================== - * - * BUTTON DEMO - * - * ===============================================================*/ -static void -ui_header(struct zr_context *ctx, const char *title) -{ - ctx->style.font.height = 18; - zr_layout_row_dynamic(ctx, 20, 1); - zr_label(ctx, title, ZR_TEXT_LEFT); -} - -static void -ui_widget(struct zr_context *ctx, float height, float font_height) -{ - static const float ratio[] = {0.15f, 0.85f}; - ctx->style.font.height = font_height; - zr_layout_row(ctx, ZR_DYNAMIC, height, 2, ratio); - zr_spacing(ctx, 1); -} - -static void -ui_widget_centered(struct zr_context *ctx, float height, float font_height) -{ - static const float ratio[] = {0.15f, 0.50f, 0.35f}; - ctx->style.font.height = font_height; - zr_layout_row(ctx, ZR_DYNAMIC, height, 3, ratio); - zr_spacing(ctx, 1); -} - -static void -button_demo(struct zr_context *ctx, struct icons *img) -{ - struct zr_panel layout; - struct zr_panel menu; - static int option = 1; - static int toggle0 = 1; - static int toggle1 = 0; - static int toggle2 = 1; - - ctx->style.font.height = 20; - zr_begin(ctx, &layout, "Button Demo", zr_rect(50,50,255,610), - ZR_WINDOW_BORDER|ZR_WINDOW_MOVABLE|ZR_WINDOW_BORDER_HEADER|ZR_WINDOW_TITLE); - - /*------------------------------------------------ - * MENU - *------------------------------------------------*/ - zr_menubar_begin(ctx); - { - /* toolbar */ - zr_layout_row_static(ctx, 40, 40, 4); - if (zr_menu_begin_image(ctx, &menu, "Music", img->play, 120)) - { - /* settings */ - zr_layout_row_dynamic(ctx, 25, 1); - zr_menu_item_image_label(ctx, img->play, "Play", ZR_TEXT_RIGHT); - zr_menu_item_image_label(ctx, img->stop, "Stop", ZR_TEXT_RIGHT); - zr_menu_item_image_label(ctx, img->pause, "Pause", ZR_TEXT_RIGHT); - zr_menu_item_image_label(ctx, img->next, "Next", ZR_TEXT_RIGHT); - zr_menu_item_image_label(ctx, img->prev, "Prev", ZR_TEXT_RIGHT); - zr_menu_end(ctx); - } - zr_button_image(ctx, img->tools, ZR_BUTTON_DEFAULT); - zr_button_image(ctx, img->cloud, ZR_BUTTON_DEFAULT); - zr_button_image(ctx, img->pen, ZR_BUTTON_DEFAULT); - } - zr_menubar_end(ctx); - - /*------------------------------------------------ - * BUTTON - *------------------------------------------------*/ - ui_header(ctx, "Push buttons"); - ui_widget(ctx, 35, 22); - if (zr_button_label(ctx, "Push me", ZR_BUTTON_DEFAULT)) - fprintf(stdout, "pushed!\n"); - ui_widget(ctx, 35, 22); - if (zr_button_image_label(ctx, img->rocket, "Styled", ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT)) - fprintf(stdout, "rocket!\n"); - - /*------------------------------------------------ - * REPEATER - *------------------------------------------------*/ - ui_header(ctx, "Repeater"); - ui_widget(ctx, 35, 22); - if (zr_button_label(ctx, "Press me", ZR_BUTTON_REPEATER)) - fprintf(stdout, "pressed!\n"); - - /*------------------------------------------------ - * TOGGLE - *------------------------------------------------*/ - ui_header(ctx, "Toggle buttons"); - ui_widget(ctx, 35, 22); - if (zr_button_image_label(ctx, (toggle0) ? img->checked: img->unchecked, - "Toggle", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT)) toggle0 = !toggle0; - - ui_widget(ctx, 35, 22); - if (zr_button_image_label(ctx, (toggle1) ? img->checked: img->unchecked, - "Toggle", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT)) toggle1 = !toggle1; - - ui_widget(ctx, 35, 22); - if (zr_button_image_label(ctx, (toggle2) ? img->checked: img->unchecked, - "Toggle", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT)) toggle2 = !toggle2; - - /*------------------------------------------------ - * RADIO - *------------------------------------------------*/ - ui_header(ctx, "Radio buttons"); - ui_widget(ctx, 35, 22); - if (zr_button_symbol_label(ctx, (option == 0)?ZR_SYMBOL_CIRCLE_FILLED:ZR_SYMBOL_CIRCLE, - "Select", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT)) option = 0; - ui_widget(ctx, 35, 22); - if (zr_button_symbol_label(ctx, (option == 1)?ZR_SYMBOL_CIRCLE_FILLED:ZR_SYMBOL_CIRCLE, - "Select", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT)) option = 1; - ui_widget(ctx, 35, 22); - if (zr_button_symbol_label(ctx, (option == 2)?ZR_SYMBOL_CIRCLE_FILLED:ZR_SYMBOL_CIRCLE, - "Select", ZR_TEXT_LEFT, ZR_BUTTON_DEFAULT)) option = 2; - - /*------------------------------------------------ - * CONTEXTUAL - *------------------------------------------------*/ - if (zr_contextual_begin(ctx, &menu, ZR_WINDOW_NO_SCROLLBAR, zr_vec2(120, 200), zr_window_get_bounds(ctx))) { - ctx->style.font.height = 18; - zr_layout_row_dynamic(ctx, 25, 1); - if (zr_contextual_item_image_label(ctx, img->copy, "Clone", ZR_TEXT_RIGHT)) - fprintf(stdout, "pressed clone!\n"); - if (zr_contextual_item_image_label(ctx, img->del, "Delete", ZR_TEXT_RIGHT)) - fprintf(stdout, "pressed delete!\n"); - if (zr_contextual_item_image_label(ctx, img->convert, "Convert", ZR_TEXT_RIGHT)) - fprintf(stdout, "pressed convert!\n"); - if (zr_contextual_item_image_label(ctx, img->edit, "Edit", ZR_TEXT_RIGHT)) - fprintf(stdout, "pressed edit!\n"); - zr_contextual_end(ctx); - } - ctx->style.font.height = 14; - zr_end(ctx); -} - -/* =============================================================== - * - * BASIC DEMO - * - * ===============================================================*/ -static void -basic_demo(struct zr_context *ctx, struct icons *img) -{ - static int image_active; - static int check0 = 1; - static int check1 = 0; - static size_t prog = 80; - static int selected_item = 0; - static int selected_image = 3; - static int selected_icon = 0; - static const char *items[] = {"Item 0","item 1","item 2"}; - static int piemenu_active = 0; - static struct zr_vec2 piemenu_pos; - - int i = 0; - struct zr_panel layout; - struct zr_panel combo; - ctx->style.font.height = 20; - zr_begin(ctx, &layout, "Basic Demo", zr_rect(320, 50, 275, 610), - ZR_WINDOW_BORDER|ZR_WINDOW_MOVABLE|ZR_WINDOW_BORDER_HEADER|ZR_WINDOW_TITLE); - - /*------------------------------------------------ - * POPUP BUTTON - *------------------------------------------------*/ - ui_header(ctx, "Popup & Scrollbar & Images"); - ui_widget(ctx, 35, 22); - if (zr_button_image_label(ctx, img->dir, - "Images", ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT)) - image_active = !image_active; - - /*------------------------------------------------ - * SELECTED IMAGE - *------------------------------------------------*/ - ui_header(ctx, "Selected Image"); - ui_widget_centered(ctx, 100, 22); - zr_image(ctx, img->images[selected_image]); - - /*------------------------------------------------ - * IMAGE POPUP - *------------------------------------------------*/ - if (image_active) { - struct zr_panel popup; - if (zr_popup_begin(ctx, &popup, ZR_POPUP_STATIC, "Image Popup", 0, zr_rect(265, 0, 320, 220))) { - zr_layout_row_static(ctx, 82, 82, 3); - for (i = 0; i < 9; ++i) { - if (zr_button_image(ctx, img->images[i], ZR_BUTTON_DEFAULT)) { - selected_image = i; - image_active = 0; - zr_popup_close(ctx); - } - } - zr_popup_end(ctx); - } - } - /*------------------------------------------------ - * COMBOBOX - *------------------------------------------------*/ - ui_header(ctx, "Combo box"); - ui_widget(ctx, 40, 22); - if (zr_combo_begin_label(ctx, &combo, items[selected_item], 200)) { - zr_layout_row_dynamic(ctx, 35, 1); - for (i = 0; i < 3; ++i) - if (zr_combo_item_label(ctx, items[i], ZR_TEXT_LEFT)) - selected_item = i; - zr_combo_end(ctx); - } - - ui_widget(ctx, 40, 22); - if (zr_combo_begin_image_label(ctx, &combo, items[selected_icon], img->images[selected_icon], 200)) { - zr_layout_row_dynamic(ctx, 35, 1); - for (i = 0; i < 3; ++i) - if (zr_combo_item_image_label(ctx, img->images[i], items[i], ZR_TEXT_RIGHT)) - selected_icon = i; - zr_combo_end(ctx); - } - - /*------------------------------------------------ - * CHECKBOX - *------------------------------------------------*/ - ui_header(ctx, "Checkbox"); - ui_widget(ctx, 30, 22); - zr_checkbox_label(ctx, "Flag 1", &check0); - ui_widget(ctx, 30, 22); - zr_checkbox_label(ctx, "Flag 2", &check1); - - /*------------------------------------------------ - * PROGRESSBAR - *------------------------------------------------*/ - ui_header(ctx, "Progressbar"); - ui_widget(ctx, 35, 22); - zr_progress(ctx, &prog, 100, zr_true); - - /*------------------------------------------------ - * PIEMENU - *------------------------------------------------*/ - if (zr_input_is_mouse_click_down_in_rect(&ctx->input, ZR_BUTTON_RIGHT, - layout.bounds,zr_true)){ - piemenu_pos = ctx->input.mouse.pos; - piemenu_active = 1; - } - - if (piemenu_active) { - int ret = ui_piemenu(ctx, piemenu_pos, 140, &img->menu[0], 6); - if (ret != -1) { - fprintf(stdout, "piemenu selected: %d\n", ret); - piemenu_active = 0; - } - } - ctx->style.font.height = 14; - zr_end(ctx); -} -/* =============================================================== - * - * FILE BROWSER - * - * ===============================================================*/ -/* sorry only works for posix systems right now because of the directory function */ -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_CPP_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 zr_image *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]; -}; - -#define MAX_PATH_LEN 512 -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; - struct media media; -}; - -#ifndef DEMO_DO_NOT_DRAW_IMAGES -#ifdef __unix__ - -#include -#include - -#ifndef _WIN32 -# include -#endif - -static char* -str_duplicate(const char *src) -{ - char *ret; - size_t len = strlen(src); - if (!len) return 0; - ret = (char*)malloc(len+1); - if (!ret) return 0; - memcpy(ret, src, len); - ret[len] = '\0'; - return ret; -} - -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 = (char**)calloc(sizeof(char*), capacity); - } else if (size >= capacity) { - capacity = capacity * 2; - results = (char**)realloc(results, capacity * sizeof(char*)); - } - p = str_duplicate(data->d_name); - results[size++] = p; - } - } while ((data = readdir(z)) != NULL); - } - - if (z) closedir(z); - *count = size; - return results; -} - -static struct file_group -FILE_GROUP(enum file_groups group, const char *name, struct zr_image *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 zr_image* -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, struct icons *icons) -{ - /* file groups */ - media->icons = icons; - 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_CPP_HEADER] = FILE_DEF(FILE_HEADER, "hpp", 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); -} - -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, struct icons *icons) -{ - memset(browser, 0, sizeof(*browser)); - media_init(&browser->media, icons); - { - /* 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; - { - size_t l; - strncpy(browser->home, home, MAX_PATH_LEN); - l = strlen(browser->home); - strcpy(browser->home + l, "/"); - strcpy(browser->directory, browser->home); - } -#endif - { - 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; - memset(browser, 0, sizeof(*browser)); -} - -static int -file_browser_run(struct file_browser *browser, struct zr_context *ctx) -{ - int ret = 0; - struct zr_panel layout; - struct media *media = &browser->media; - struct icons *icons = media->icons; - struct zr_rect total_space; - - if (zr_begin(ctx, &layout, "File Browser", zr_rect(50, 50, 800, 600), - ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_CLOSABLE|ZR_WINDOW_MOVABLE)) - { - struct zr_panel sub; - static float ratio[] = {0.25f, ZR_UNDEFINED}; - float spacing_x = ctx->style.window.spacing.x; - - /* output path directory selector in the menubar */ - ctx->style.window.spacing.x = 0; - zr_menubar_begin(ctx); - { - char *d = browser->directory; - char *begin = d + 1; - zr_layout_row_dynamic(ctx, 25, 6); - while (*d++) { - if (*d == '/') { - *d = '\0'; - if (zr_button_label(ctx, begin, ZR_BUTTON_DEFAULT)) { - *d++ = '/'; *d = '\0'; - file_browser_reload_directory_content(browser, browser->directory); - break; - } - *d = '/'; - begin = d + 1; - } - } - } - zr_menubar_end(ctx); - ctx->style.window.spacing.x = spacing_x; - - /* window layout */ - total_space = zr_window_get_content_region(ctx); - zr_layout_row(ctx, ZR_DYNAMIC, total_space.h, 2, ratio); - zr_group_begin(ctx, &sub, "Special", ZR_WINDOW_NO_SCROLLBAR); - { - struct zr_image home = icons->home; - struct zr_image desktop = icons->desktop; - struct zr_image computer = icons->computer; - - zr_layout_row_dynamic(ctx, 40, 1); - if (zr_button_image_label(ctx, home, "home", ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT)) - file_browser_reload_directory_content(browser, browser->home); - if (zr_button_image_label(ctx,desktop,"desktop",ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT)) - file_browser_reload_directory_content(browser, browser->desktop); - if (zr_button_image_label(ctx,computer,"computer",ZR_TEXT_CENTERED,ZR_BUTTON_DEFAULT)) - file_browser_reload_directory_content(browser, "/"); - zr_group_end(ctx); - } - - /* output directory content window */ - zr_group_begin(ctx, &sub, "Content", 0); - { - 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) { - {size_t n = j + cols; - zr_layout_row_dynamic(ctx, 135, (int)cols); - for (; j < count && j < n; ++j) { - /* draw one row of icons */ - if (j < browser->dir_count) { - /* draw and execute directory buttons */ - if (zr_button_image(ctx,icons->directory,ZR_BUTTON_DEFAULT)) - index = (int)j; - } else { - /* draw and execute files buttons */ - struct zr_image *icon; - size_t fileIndex = ((size_t)j - browser->dir_count); - icon = media_icon_for_file(media,browser->files[fileIndex]); - if (zr_button_image(ctx, *icon, 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); - ret = 1; - } - } - }} - {size_t n = k + cols; - zr_layout_row_dynamic(ctx, 20, (int)cols); - for (; k < count && k < n; k++) { - /* draw one row of labels */ - if (k < browser->dir_count) { - zr_label(ctx, browser->directories[k], ZR_TEXT_CENTERED); - } else { - size_t t = k-browser->dir_count; - zr_label(ctx,browser->files[t],ZR_TEXT_CENTERED); - } - }} - } - - 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_group_end(ctx); - } - } - zr_end(ctx); - return ret; -} -#endif -#endif - -/* =============================================================== - * - * NODE EDITOR - * - * ===============================================================*/ -struct node { - int ID; - char name[32]; - struct zr_rect bounds; - float value; - struct zr_color color; - int input_count; - int output_count; - struct node *next; - struct node *prev; -}; - -struct node_link { - int input_id; - int input_slot; - int output_id; - int output_slot; - struct zr_vec2 in; - struct zr_vec2 out; -}; - -struct node_linking { - int active; - struct node *node; - int input_id; - int input_slot; -}; - -struct node_editor { - struct node node_buf[32]; - struct node_link links[64]; - struct node *begin; - struct node *end; - int node_count; - int link_count; - struct zr_rect bounds; - struct node *selected; - int show_grid; - struct zr_vec2 scrolling; - struct node_linking linking; -}; - -static void -node_editor_push(struct node_editor *editor, struct node *node) -{ - if (!editor->begin) { - node->next = NULL; - node->prev = NULL; - editor->begin = node; - editor->end = node; - } else { - node->prev = editor->end; - if (editor->end) - editor->end->next = node; - node->next = NULL; - editor->end = node; - } -} - -static void -node_editor_pop(struct node_editor *editor, struct node *node) -{ - if (node->next) - node->next->prev = node->prev; - if (node->prev) - node->prev->next = node->next; - if (editor->end == node) - editor->end = node->prev; - if (editor->begin == node) - editor->begin = node->next; - node->next = NULL; - node->prev = NULL; -} - -static struct node* -node_editor_find(struct node_editor *editor, int ID) -{ - struct node *iter = editor->begin; - while (iter) { - if (iter->ID == ID) - return iter; - iter = iter->next; - } - return NULL; -} - -static void -node_editor_add(struct node_editor *editor, const char *name, struct zr_rect bounds, - struct zr_color col, int in_count, int out_count) -{ - static int IDs = 0; - struct node *node; - assert((zr_size)editor->node_count < LEN(editor->node_buf)); - node = &editor->node_buf[editor->node_count++]; - node->ID = IDs++; - node->value = 0; - node->color = zr_rgb(255, 0, 0); - node->input_count = in_count; - node->output_count = out_count; - node->color = col; - node->bounds = bounds; - strcpy(node->name, name); - node_editor_push(editor, node); -} - -static void -node_editor_link(struct node_editor *editor, int in_id, int in_slot, - int out_id, int out_slot) -{ - struct node_link *link; - assert((zr_size)editor->link_count < LEN(editor->links)); - link = &editor->links[editor->link_count++]; - link->input_id = in_id; - link->input_slot = in_slot; - link->output_id = out_id; - link->output_slot = out_slot; -} - -static void -node_editor_demo(struct zr_context *ctx, struct node_editor *nodedit) -{ - int n = 0; - struct zr_rect total_space; - const struct zr_input *in = &ctx->input; - struct zr_command_buffer *canvas; - struct node *updated = 0; - struct zr_panel layout; - - /* This is a simple node editor just to show a simple implementation and that - * it is possible to achieve with this library. While all nodes inside this - * example use a simple color modifier as content you could change them - * to have your custom content depending on the node time. - * Biggest difference to most usual implementation is that this example does - * not has connectors on the right position of the property that it links. - * This is mainly done out of lazyness and could be implemented as well but - * requires calculating the position of all rows and add connectors. - * In addition adding and removing nodes is quite limited at the - * moment since it is based on a simple fixed array. If this is to be converted - * into something more serious it is probably best to extend it.*/ - - if (zr_begin(ctx, &layout, "Node Editor", zr_rect(50, 50, 650, 650), - ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_CLOSABLE|ZR_WINDOW_MOVABLE)) - { - /* allocate complete window space */ - canvas = zr_window_get_canvas(ctx); - total_space = zr_window_get_content_region(ctx); - zr_layout_space_begin(ctx, ZR_STATIC, total_space.h, nodedit->node_count); - { - struct zr_panel node, menu; - struct node *it = nodedit->begin; - struct zr_rect size = zr_layout_space_bounds(ctx); - - if (nodedit->show_grid) { - /* display grid */ - float x, y; - const float grid_size = 32.0f; - const struct zr_color grid_color = zr_rgb(50, 50, 50); - for (x = (float)fmod(size.x - nodedit->scrolling.x, grid_size); x < size.w; x += grid_size) - zr_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, 1.0f, grid_color); - for (y = (float)fmod(size.y - nodedit->scrolling.y, grid_size); y < size.h; y += grid_size) - zr_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, 1.0f, grid_color); - } - - /* execute each node as a moveable group */ - while (it) { - /* calculate scrolled node window position and size */ - zr_layout_space_push(ctx, zr_rect(it->bounds.x - nodedit->scrolling.x, - it->bounds.y - nodedit->scrolling.y, it->bounds.w, it->bounds.h)); - - /* execute node window */ - if (zr_group_begin(ctx, &node, it->name, ZR_WINDOW_MOVABLE|ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_BORDER|ZR_WINDOW_TITLE)) - { - /* always have last selected node on top */ - if (zr_input_mouse_clicked(in, ZR_BUTTON_LEFT, node.bounds) && - (!(it->prev && zr_input_mouse_clicked(in, ZR_BUTTON_LEFT, - zr_layout_space_rect_to_screen(ctx, node.bounds)))) && - nodedit->end != it) - { - updated = it; - } - - /* ================= NODE CONTENT =====================*/ - zr_layout_row_dynamic(ctx, 25, 1); - zr_button_color(ctx, it->color, ZR_BUTTON_DEFAULT); - it->color.r = (zr_byte)zr_propertyi(ctx, "#R:", 0, it->color.r, 255, 1,1); - it->color.g = (zr_byte)zr_propertyi(ctx, "#G:", 0, it->color.g, 255, 1,1); - it->color.b = (zr_byte)zr_propertyi(ctx, "#B:", 0, it->color.b, 255, 1,1); - it->color.a = (zr_byte)zr_propertyi(ctx, "#A:", 0, it->color.a, 255, 1,1); - /* ====================================================*/ - zr_group_end(ctx); - } - { - /* node connector and linking */ - float space; - struct zr_rect bounds; - bounds = zr_layout_space_rect_to_local(ctx, node.bounds); - bounds.x += nodedit->scrolling.x; - bounds.y += nodedit->scrolling.y; - it->bounds = bounds; - - /* output connector */ - space = node.bounds.h / (float)((it->output_count) + 1); - for (n = 0; n < it->output_count; ++n) { - struct zr_rect circle; - circle.x = node.bounds.x + node.bounds.w-4; - circle.y = node.bounds.y + space * (float)(n+1); - circle.w = 8; circle.h = 8; - zr_fill_circle(canvas, circle, zr_rgb(100, 100, 100)); - - /* start linking process */ - if (zr_input_has_mouse_click_down_in_rect(in, ZR_BUTTON_LEFT, circle, zr_true)) { - nodedit->linking.active = zr_true; - nodedit->linking.node = it; - nodedit->linking.input_id = it->ID; - nodedit->linking.input_slot = n; - } - - /* draw curve from linked node slot to mouse position */ - if (nodedit->linking.active && nodedit->linking.node == it && - nodedit->linking.input_slot == n) { - struct zr_vec2 l0 = zr_vec2(circle.x + 3, circle.y + 3); - struct zr_vec2 l1 = in->mouse.pos; - zr_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, - l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, zr_rgb(100, 100, 100)); - } - } - - /* input connector */ - space = node.bounds.h / (float)((it->input_count) + 1); - for (n = 0; n < it->input_count; ++n) { - struct zr_rect circle; - circle.x = node.bounds.x-4; - circle.y = node.bounds.y + space * (float)(n+1); - circle.w = 8; circle.h = 8; - zr_fill_circle(canvas, circle, zr_rgb(100, 100, 100)); - if (zr_input_is_mouse_released(in, ZR_BUTTON_LEFT) && - zr_input_is_mouse_hovering_rect(in, circle) && - nodedit->linking.active && nodedit->linking.node != it) { - nodedit->linking.active = zr_false; - node_editor_link(nodedit, nodedit->linking.input_id, - nodedit->linking.input_slot, it->ID, n); - } - } - } - it = it->next; - } - - /* reset linking connection */ - if (nodedit->linking.active && zr_input_is_mouse_released(in, ZR_BUTTON_LEFT)) { - nodedit->linking.active = zr_false; - nodedit->linking.node = NULL; - fprintf(stdout, "linking failed\n"); - } - - /* draw each link */ - for (n = 0; n < nodedit->link_count; ++n) { - struct node_link *link = &nodedit->links[n]; - struct node *ni = node_editor_find(nodedit, link->input_id); - struct node *no = node_editor_find(nodedit, link->output_id); - float spacei = node.bounds.h / (float)((ni->output_count) + 1); - float spaceo = node.bounds.h / (float)((no->input_count) + 1); - struct zr_vec2 l0 = zr_layout_space_to_screen(ctx, - zr_vec2(ni->bounds.x + ni->bounds.w, 3.0f + ni->bounds.y + spacei * (float)(link->input_slot+1))); - struct zr_vec2 l1 = zr_layout_space_to_screen(ctx, - zr_vec2(no->bounds.x, 3.0f + no->bounds.y + spaceo * (float)(link->output_slot+1))); - - l0.x -= nodedit->scrolling.x; - l0.y -= nodedit->scrolling.y; - l1.x -= nodedit->scrolling.x; - l1.y -= nodedit->scrolling.y; - zr_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, - l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, zr_rgb(100, 100, 100)); - } - - if (updated) { - /* reshuffle nodes to have least recently selected node on top */ - node_editor_pop(nodedit, updated); - node_editor_push(nodedit, updated); - } - - /* node selection */ - if (zr_input_mouse_clicked(in, ZR_BUTTON_LEFT, zr_layout_space_bounds(ctx))) { - it = nodedit->begin; - nodedit->selected = NULL; - nodedit->bounds = zr_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200); - while (it) { - struct zr_rect b = zr_layout_space_rect_to_screen(ctx, it->bounds); - b.x -= nodedit->scrolling.x; - b.y -= nodedit->scrolling.y; - if (zr_input_is_mouse_hovering_rect(in, b)) - nodedit->selected = it; - it = it->next; - } - } - - /* contextual menu */ - if (zr_contextual_begin(ctx, &menu, 0, zr_vec2(100, 220), zr_window_get_bounds(ctx))) { - const char *grid_option[] = {"Show Grid", "Hide Grid"}; - zr_layout_row_dynamic(ctx, 25, 1); - if (zr_contextual_item_label(ctx, "New", ZR_TEXT_CENTERED)) - node_editor_add(nodedit, "New", zr_rect(400, 260, 180, 220), - zr_rgb(255, 255, 255), 1, 2); - if (zr_contextual_item_label(ctx, grid_option[nodedit->show_grid],ZR_TEXT_CENTERED)) - nodedit->show_grid = !nodedit->show_grid; - zr_contextual_end(ctx); - } - } - zr_layout_space_end(ctx); - - /* window content scrolling */ - if (zr_input_is_mouse_hovering_rect(in, zr_window_get_bounds(ctx)) && - zr_input_is_mouse_down(in, ZR_BUTTON_MIDDLE)) { - nodedit->scrolling.x += in->mouse.delta.x; - nodedit->scrolling.y += in->mouse.delta.y; - } - } - zr_end(ctx); -} - -static void -node_editor_init(struct node_editor *editor) -{ - memset(editor, 0, sizeof(*editor)); - editor->begin = NULL; - editor->end = NULL; - node_editor_add(editor, "Source", zr_rect(40, 10, 180, 220), zr_rgb(255, 0, 0), 0, 1); - node_editor_add(editor, "Source", zr_rect(40, 260, 180, 220), zr_rgb(0, 255, 0), 0, 1); - node_editor_add(editor, "Combine", zr_rect(400, 100, 180, 220), zr_rgb(0,0,255), 2, 2); - node_editor_link(editor, 0, 0, 2, 0); - node_editor_link(editor, 1, 0, 2, 1); - editor->show_grid = zr_true; -} - -/* =============================================================== - * - * CONTROL WINDOW - * - * ===============================================================*/ -static void -set_style(struct zr_context *ctx, enum theme theme) -{ - struct zr_color table[ZR_COLOR_COUNT]; - if (theme == THEME_WHITE) { - table[ZR_COLOR_TEXT] = zr_rgba(70, 70, 70, 255); - table[ZR_COLOR_WINDOW] = zr_rgba(175, 175, 175, 255); - table[ZR_COLOR_HEADER] = zr_rgba(175, 175, 175, 255); - table[ZR_COLOR_BORDER] = zr_rgba(0, 0, 0, 255); - table[ZR_COLOR_BUTTON] = zr_rgba(185, 185, 185, 255); - table[ZR_COLOR_BUTTON_HOVER] = zr_rgba(170, 170, 170, 255); - table[ZR_COLOR_BUTTON_ACTIVE] = zr_rgba(160, 160, 160, 255); - table[ZR_COLOR_TOGGLE] = zr_rgba(150, 150, 150, 255); - table[ZR_COLOR_TOGGLE_HOVER] = zr_rgba(120, 120, 120, 255); - table[ZR_COLOR_TOGGLE_CURSOR] = zr_rgba(175, 175, 175, 255); - table[ZR_COLOR_SELECTABLE] = zr_rgba(190, 190, 190, 255); - table[ZR_COLOR_SELECTABLE_HOVER] = zr_rgba(150, 150, 150, 255); - table[ZR_COLOR_SELECTABLE_TEXT] = zr_rgba(70, 70, 70, 255); - table[ZR_COLOR_SLIDER] = zr_rgba(190, 190, 190, 255); - table[ZR_COLOR_SLIDER_CURSOR] = zr_rgba(80, 80, 80, 255); - table[ZR_COLOR_SLIDER_CURSOR_HOVER] = zr_rgba(70, 70, 70, 255); - table[ZR_COLOR_SLIDER_CURSOR_ACTIVE] = zr_rgba(60, 60, 60, 255); - table[ZR_COLOR_PROPERTY] = zr_rgba(175, 175, 175, 255); - table[ZR_COLOR_EDIT] = zr_rgba(150, 150, 150, 255); - table[ZR_COLOR_EDIT_CURSOR] = zr_rgba(0, 0, 0, 255); - table[ZR_COLOR_COMBO] = zr_rgba(175, 175, 175, 255); - table[ZR_COLOR_CHART] = zr_rgba(160, 160, 160, 255); - table[ZR_COLOR_CHART_COLOR] = zr_rgba(45, 45, 45, 255); - table[ZR_COLOR_CHART_COLOR_HIGHLIGHT] = zr_rgba( 255, 0, 0, 255); - table[ZR_COLOR_SCROLLBAR] = zr_rgba(180, 180, 180, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR] = zr_rgba(140, 140, 140, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_HOVER] = zr_rgba(150, 150, 150, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_ACTIVE] = zr_rgba(160, 160, 160, 255); - table[ZR_COLOR_TAB_HEADER] = zr_rgba(180, 180, 180, 255); - zr_style_from_table(ctx, table); - } else if (theme == THEME_RED) { - table[ZR_COLOR_TEXT] = zr_rgba(190, 190, 190, 255); - table[ZR_COLOR_WINDOW] = zr_rgba(30, 33, 40, 215); - table[ZR_COLOR_HEADER] = zr_rgba(181, 45, 69, 220); - table[ZR_COLOR_BORDER] = zr_rgba(51, 55, 67, 255); - table[ZR_COLOR_BUTTON] = zr_rgba(181, 45, 69, 255); - table[ZR_COLOR_BUTTON_HOVER] = zr_rgba(190, 50, 70, 255); - table[ZR_COLOR_BUTTON_ACTIVE] = zr_rgba(195, 55, 75, 255); - table[ZR_COLOR_TOGGLE] = zr_rgba(51, 55, 67, 255); - table[ZR_COLOR_TOGGLE_HOVER] = zr_rgba(45, 60, 60, 255); - table[ZR_COLOR_TOGGLE_CURSOR] = zr_rgba(181, 45, 69, 255); - table[ZR_COLOR_SELECTABLE] = zr_rgba(181, 45, 69, 255); - table[ZR_COLOR_SELECTABLE_HOVER] = zr_rgba(181, 45, 69, 255); - table[ZR_COLOR_SELECTABLE_TEXT] = zr_rgba(190, 190, 190, 255); - table[ZR_COLOR_SLIDER] = zr_rgba(51, 55, 67, 255); - table[ZR_COLOR_SLIDER_CURSOR] = zr_rgba(181, 45, 69, 255); - table[ZR_COLOR_SLIDER_CURSOR_HOVER] = zr_rgba(186, 50, 74, 255); - table[ZR_COLOR_SLIDER_CURSOR_ACTIVE] = zr_rgba(191, 55, 79, 255); - table[ZR_COLOR_PROPERTY] = zr_rgba(51, 55, 67, 255); - table[ZR_COLOR_EDIT] = zr_rgba(51, 55, 67, 225); - table[ZR_COLOR_EDIT_CURSOR] = zr_rgba(190, 190, 190, 255); - table[ZR_COLOR_COMBO] = zr_rgba(51, 55, 67, 255); - table[ZR_COLOR_CHART] = zr_rgba(51, 55, 67, 255); - table[ZR_COLOR_CHART_COLOR] = zr_rgba(170, 40, 60, 255); - table[ZR_COLOR_CHART_COLOR_HIGHLIGHT] = zr_rgba( 255, 0, 0, 255); - table[ZR_COLOR_SCROLLBAR] = zr_rgba(30, 33, 40, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR] = zr_rgba(64, 84, 95, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_HOVER] = zr_rgba(70, 90, 100, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_ACTIVE] = zr_rgba(75, 95, 105, 255); - table[ZR_COLOR_TAB_HEADER] = zr_rgba(181, 45, 69, 220); - zr_style_from_table(ctx, table); - } else if (theme == THEME_BLUE) { - table[ZR_COLOR_TEXT] = zr_rgba(20, 20, 20, 255); - table[ZR_COLOR_WINDOW] = zr_rgba(202, 212, 214, 215); - table[ZR_COLOR_HEADER] = zr_rgba(137, 182, 224, 220); - table[ZR_COLOR_BORDER] = zr_rgba(140, 159, 173, 255); - table[ZR_COLOR_BUTTON] = zr_rgba(137, 182, 224, 255); - table[ZR_COLOR_BUTTON_HOVER] = zr_rgba(142, 187, 229, 255); - table[ZR_COLOR_BUTTON_ACTIVE] = zr_rgba(147, 192, 234, 255); - table[ZR_COLOR_TOGGLE] = zr_rgba(177, 210, 210, 255); - table[ZR_COLOR_TOGGLE_HOVER] = zr_rgba(182, 215, 215, 255); - table[ZR_COLOR_TOGGLE_CURSOR] = zr_rgba(137, 182, 224, 255); - table[ZR_COLOR_SELECTABLE] = zr_rgba(147, 192, 234, 255); - table[ZR_COLOR_SELECTABLE_HOVER] = zr_rgba(150, 150, 150, 255); - table[ZR_COLOR_SELECTABLE_TEXT] = zr_rgba(70, 70, 70, 255); - table[ZR_COLOR_SLIDER] = zr_rgba(177, 210, 210, 255); - table[ZR_COLOR_SLIDER_CURSOR] = zr_rgba(137, 182, 224, 245); - table[ZR_COLOR_SLIDER_CURSOR_HOVER] = zr_rgba(142, 188, 229, 255); - table[ZR_COLOR_SLIDER_CURSOR_ACTIVE] = zr_rgba(147, 193, 234, 255); - table[ZR_COLOR_PROPERTY] = zr_rgba(210, 210, 210, 255); - table[ZR_COLOR_EDIT] = zr_rgba(210, 210, 210, 225); - table[ZR_COLOR_EDIT_CURSOR] = zr_rgba(20, 20, 20, 255); - table[ZR_COLOR_COMBO] = zr_rgba(210, 210, 210, 255); - table[ZR_COLOR_CHART] = zr_rgba(210, 210, 210, 255); - table[ZR_COLOR_CHART_COLOR] = zr_rgba(137, 182, 224, 255); - table[ZR_COLOR_CHART_COLOR_HIGHLIGHT] = zr_rgba( 255, 0, 0, 255); - table[ZR_COLOR_SCROLLBAR] = zr_rgba(190, 200, 200, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR] = zr_rgba(64, 84, 95, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_HOVER] = zr_rgba(70, 90, 100, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_ACTIVE] = zr_rgba(75, 95, 105, 255); - table[ZR_COLOR_TAB_HEADER] = zr_rgba(156, 193, 220, 255); - zr_style_from_table(ctx, table); - } else if (theme == THEME_DARK) { - table[ZR_COLOR_TEXT] = zr_rgba(210, 210, 210, 255); - table[ZR_COLOR_WINDOW] = zr_rgba(57, 67, 71, 215); - table[ZR_COLOR_HEADER] = zr_rgba(51, 51, 56, 220); - table[ZR_COLOR_BORDER] = zr_rgba(46, 46, 46, 255); - table[ZR_COLOR_BUTTON] = zr_rgba(48, 83, 111, 255); - table[ZR_COLOR_BUTTON_HOVER] = zr_rgba(58, 93, 121, 255); - table[ZR_COLOR_BUTTON_ACTIVE] = zr_rgba(63, 98, 126, 255); - table[ZR_COLOR_TOGGLE] = zr_rgba(50, 58, 61, 255); - table[ZR_COLOR_TOGGLE_HOVER] = zr_rgba(45, 53, 56, 255); - table[ZR_COLOR_TOGGLE_CURSOR] = zr_rgba(48, 83, 111, 255); - table[ZR_COLOR_SELECTABLE] = zr_rgba(48, 83, 111, 255); - table[ZR_COLOR_SELECTABLE_HOVER] = zr_rgba(48, 83, 111, 255); - table[ZR_COLOR_SELECTABLE_TEXT] = zr_rgba(210, 210, 210, 255); - table[ZR_COLOR_SLIDER] = zr_rgba(50, 58, 61, 255); - table[ZR_COLOR_SLIDER_CURSOR] = zr_rgba(48, 83, 111, 245); - table[ZR_COLOR_SLIDER_CURSOR_HOVER] = zr_rgba(53, 88, 116, 255); - table[ZR_COLOR_SLIDER_CURSOR_ACTIVE] = zr_rgba(58, 93, 121, 255); - table[ZR_COLOR_PROPERTY] = zr_rgba(50, 58, 61, 255); - table[ZR_COLOR_EDIT] = zr_rgba(50, 58, 61, 225); - table[ZR_COLOR_EDIT_CURSOR] = zr_rgba(210, 210, 210, 255); - table[ZR_COLOR_COMBO] = zr_rgba(50, 58, 61, 255); - table[ZR_COLOR_CHART] = zr_rgba(50, 58, 61, 255); - table[ZR_COLOR_CHART_COLOR] = zr_rgba(48, 83, 111, 255); - table[ZR_COLOR_CHART_COLOR_HIGHLIGHT] = zr_rgba(255, 0, 0, 255); - table[ZR_COLOR_SCROLLBAR] = zr_rgba(50, 58, 61, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR] = zr_rgba(48, 83, 111, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_HOVER] = zr_rgba(53, 88, 116, 255); - table[ZR_COLOR_SCROLLBAR_CURSOR_ACTIVE] = zr_rgba(58, 93, 121, 255); - table[ZR_COLOR_TAB_HEADER] = zr_rgba(48, 83, 111, 255); - zr_style_from_table(ctx, table); - } else { - zr_style_default(ctx); - } -} - -static int -control_window(struct zr_context *ctx, struct demo *gui) -{ - struct zr_panel layout; - if (zr_begin(ctx, &layout, "Control", zr_rect(0, 0, 350, 520), - ZR_WINDOW_CLOSABLE|ZR_WINDOW_MINIMIZABLE|ZR_WINDOW_MOVABLE| - ZR_WINDOW_SCALABLE|ZR_WINDOW_BORDER)) - { - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Windows", ZR_MINIMIZED)) { - zr_layout_row_dynamic(ctx, 25, 2); - gui->show_simple = !zr_window_is_closed(ctx, "Show"); - gui->show_node = !zr_window_is_closed(ctx, "Node Editor"); - gui->show_demo = !zr_window_is_closed(ctx, "Demo"); -#ifndef DEMO_DO_NOT_DRAW_IMAGES - gui->show_filex = !zr_window_is_closed(ctx, "File Browser"); - gui->show_grid = !zr_window_is_closed(ctx, "Grid Demo"); - gui->show_basic = !zr_window_is_closed(ctx, "Basic Demo"); - gui->show_button = !zr_window_is_closed(ctx, "Button Demo"); -#endif - - if (zr_checkbox_label(ctx, "Show", &gui->show_simple) && !gui->show_simple) - zr_window_close(ctx, "Show"); - if (zr_checkbox_label(ctx, "Demo", &gui->show_demo) && !gui->show_demo) - zr_window_close(ctx, "Demo"); - if (zr_checkbox_label(ctx, "Node Editor", &gui->show_node) && !gui->show_node) - zr_window_close(ctx, "Node Editor"); -#ifndef DEMO_DO_NOT_DRAW_IMAGES - if (zr_checkbox_label(ctx, "Grid", &gui->show_grid) && !gui->show_grid) - zr_window_close(ctx, "Grid Demo"); - if (zr_checkbox_label(ctx, "Basic", &gui->show_basic) && !gui->show_basic) - zr_window_close(ctx, "Basic Demo"); - if (zr_checkbox_label(ctx, "Button", &gui->show_button) && !gui->show_button) - zr_window_close(ctx, "Button Demo"); - if (zr_checkbox_label(ctx, "Filex", &gui->show_filex) && !gui->show_filex) - zr_window_close(ctx, "File Browser"); -#endif - zr_layout_pop(ctx); - } - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Metrics", ZR_MINIMIZED)) { - zr_layout_row_dynamic(ctx, 20, 2); - zr_label(ctx,"Total:", ZR_TEXT_LEFT); - zr_labelf(ctx, ZR_TEXT_LEFT, "%lu", gui->status.size); - zr_label(ctx,"Used:", ZR_TEXT_LEFT); - zr_labelf(ctx, ZR_TEXT_LEFT, "%lu", gui->status.allocated); - zr_label(ctx,"Required:", ZR_TEXT_LEFT); - zr_labelf(ctx, ZR_TEXT_LEFT, "%lu", gui->status.needed); - zr_label(ctx,"Calls:", ZR_TEXT_LEFT); - zr_labelf(ctx, ZR_TEXT_LEFT, "%lu", gui->status.calls); - zr_layout_pop(ctx); - } - if (zr_layout_push(ctx, ZR_LAYOUT_TAB, "Color", ZR_MINIMIZED)) - { - struct zr_panel combo; - enum theme old = gui->theme; - static const char *themes[] = {"Black", "White", "Red", "Blue", "Dark", "Grey"}; - - zr_layout_row_dynamic(ctx, 25, 2); - zr_label(ctx, "THEME:", ZR_TEXT_LEFT); - if (zr_combo_begin_label(ctx, &combo, themes[gui->theme], 300)) { - zr_layout_row_dynamic(ctx, 25, 1); - gui->theme = zr_combo_item_label(ctx, themes[THEME_BLACK], ZR_TEXT_CENTERED) ? THEME_BLACK : gui->theme; - gui->theme = zr_combo_item_label(ctx, themes[THEME_WHITE], ZR_TEXT_CENTERED) ? THEME_WHITE : gui->theme; - gui->theme = zr_combo_item_label(ctx, themes[THEME_RED], ZR_TEXT_CENTERED) ? THEME_RED : gui->theme; - gui->theme = zr_combo_item_label(ctx, themes[THEME_BLUE], ZR_TEXT_CENTERED) ? THEME_BLUE : gui->theme; - gui->theme = zr_combo_item_label(ctx, themes[THEME_DARK], ZR_TEXT_CENTERED) ? THEME_DARK : gui->theme; - if (old != gui->theme) set_style(ctx, gui->theme); - zr_combo_end(ctx); - } - zr_layout_pop(ctx); - } - } - zr_end(ctx); - return !zr_window_is_closed(ctx, "Control"); -} - - -/* =============================================================== - * - * DEMO ENTRY - * - * ===============================================================*/ -static int -run_demo(struct demo *gui) -{ - int ret = 1; - static int init = 0; - static struct node_editor nodedit; - static struct file_browser filex; - struct zr_context *ctx = &gui->ctx; - struct zr_style *style = &ctx->style; - - ctx->style.font.height = 14; - if (!init) { - init = 1; - gui->show_demo = 0; - gui->show_node = 0; - gui->show_simple = 0; - - gui->show_grid = 0; - gui->show_basic = 0; - gui->show_button = 0; - - memset(&nodedit, 0, sizeof(nodedit)); - node_editor_init(&nodedit); -#ifndef DEMO_DO_NOT_DRAW_IMAGES -#ifdef __unix__ - file_browser_init(&filex, &gui->icons); -#endif -#endif - } - - ret = control_window(ctx, gui); - if (gui->show_demo) - demo_window(gui, ctx); - if (gui->show_simple) - simple_window(ctx); - if (gui->show_node) - node_editor_demo(ctx, &nodedit); - -#ifndef DEMO_DO_NOT_DRAW_IMAGES -#ifdef __unix__ - if (gui->show_filex) - file_browser_run(&filex, ctx); - if (!ret) file_browser_free(&filex); -#endif - if (gui->show_grid) - grid_demo(ctx); - if (gui->show_button) - button_demo(ctx, &gui->icons); - if (gui->show_basic) - basic_demo(ctx, &gui->icons); -#endif - zr_buffer_info(&gui->status, &gui->ctx.memory); - return ret; -} - diff --git a/demo/glfw/Makefile b/demo/glfw/Makefile index 8dd5f3f..5526b09 100644 --- a/demo/glfw/Makefile +++ b/demo/glfw/Makefile @@ -8,7 +8,7 @@ DCC = gcc # Flags CFLAGS = -std=c99 -pedantic -O2 -SRC = ../../zahnrad.c glfw.c +SRC = main.c OBJ = $(SRC:.c=.o) ifeq ($(OS),Windows_NT) diff --git a/demo/glfw/glfw.c b/demo/glfw/glfw.c deleted file mode 100644 index f7a96ee..0000000 --- a/demo/glfw/glfw.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - Copyright (c) 2016 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 -#include - -#include -#include -#include -#include - -/* macros */ -#define MAX_VERTEX_MEMORY 512 * 1024 -#define MAX_ELEMENT_MEMORY 128 * 1024 - -#include "../../zahnrad.h" -#include "../demo.c" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#pragma clang diagnostic ignored "-Wfloat-equal" -#pragma clang diagnostic ignored "-Wbad-function-cast" -#pragma clang diagnostic ignored "-Wcast-qual" -#pragma clang diagnostic ignored "-Wshadow" -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#pragma clang diagnostic ignored "-Wdeclaration-after-statement" -#pragma clang diagnostic ignored "-Wunused-function" -#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wbad-function-cast" -#pragma GCC diagnostic ignored "-Wcast-qual" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" -#pragma GCC diagnostic ignored "-Wtype-limits" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wunused-function" -#elif _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4456) -#endif - -#define STB_IMAGE_IMPLEMENTATION -#include "../stb_image.h" - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic pop -#elif _MSC_VER -#pragma warning (pop) -#endif - -static int mouse_pos_x = 0; -static int mouse_pos_y = 0; -static struct demo gui; - -/* ============================================================== - * - * Utility - * - * ===============================================================*/ -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 char* -file_load(const char* path, size_t* siz) -{ - char *buf; - FILE *fd = fopen(path, "rb"); - if (!fd) die("Failed to open file: %s\n", path); - fseek(fd, 0, SEEK_END); - *siz = (size_t)ftell(fd); - fseek(fd, 0, SEEK_SET); - buf = (char*)calloc(*siz, 1); - fread(buf, *siz, 1, fd); - fclose(fd); - return buf; -} - -static struct zr_image -icon_load(const char *filename) -{ - int x,y,n; - GLuint tex; - unsigned char *data = stbi_load(filename, &x, &y, &n, 0); - if (!data) die("[SDL]: failed to load image: %s", filename); - - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); - stbi_image_free(data); - return zr_image_id((int)tex); -} - -struct device { - struct zr_buffer cmds; - struct zr_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; -}; - -static void -device_init(struct device *dev) -{ - GLint status; - static const GLchar *vertex_shader = - "#version 300 es\n" - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 TexCoord;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "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 = - "#version 300 es\n" - "precision mediump float;\n" - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main(){\n" - " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" - "}\n"; - - 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 zr_draw_vertex); - size_t vp = offsetof(struct zr_draw_vertex, position); - size_t vt = offsetof(struct zr_draw_vertex, uv); - size_t vc = offsetof(struct zr_draw_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); - glBindVertexArray(0); -} - -static void -device_upload_atlas(struct device *dev, const void *image, int width, int height) -{ - 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); -} - -static void -device_shutdown(struct device *dev) -{ - 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); -} - -static void -device_draw(struct device *dev, struct zr_context *ctx, int width, int height, - enum zr_anti_aliasing AA) -{ - GLint last_prog, last_tex; - GLint last_ebo, last_vbo, last_vao; - 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)width; - ortho[1][1] /= (GLfloat)height; - - /* save previous opengl state */ - glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); - - /* 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]); - - { - /* convert from command queue into draw list and draw to screen */ - const struct zr_draw_command *cmd; - void *vertices, *elements; - const zr_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_MEMORY, NULL, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); - - /* load draw vertices & elements directly into vertex + element buffer */ - vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); - { - struct zr_buffer vbuf, ebuf; - - /* fill converting configuration */ - struct zr_convert_config config; - memset(&config, 0, sizeof(config)); - config.global_alpha = 1.0f; - config.shape_AA = AA; - config.line_AA = AA; - config.circle_segment_count = 22; - config.curve_segment_count = 22; - config.arc_segment_count = 22; - config.null = dev->null; - - /* setup buffers to load vertices and elements */ - zr_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); - zr_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); - zr_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config); - } - glUnmapBuffer(GL_ARRAY_BUFFER); - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - - /* iterate over and execute each draw command */ - zr_draw_foreach(cmd, ctx, &dev->cmds) { - if (!cmd->elem_count) continue; - glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); - glScissor((GLint)cmd->clip_rect.x, - height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), - (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); - glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); - offset += cmd->elem_count; - } - zr_clear(ctx); - } - - /* restore old state */ - glUseProgram((GLuint)last_prog); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); - glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); - glBindVertexArray((GLuint)last_vao); - glDisable(GL_SCISSOR_TEST); -} - -static void -error_callback(int error, const char *description) -{ - fprintf(stderr, "Error %d: %s\n", error, description); -} - -static void -input_key(GLFWwindow *window, int key, int scancode, int action, int mods) -{ - int down = action == GLFW_PRESS; - UNUSED(window); - UNUSED(scancode); - if (key == GLFW_KEY_RIGHT_SHIFT || key == GLFW_KEY_LEFT_SHIFT) - zr_input_key(&gui.ctx, ZR_KEY_SHIFT, down); - else if (key == GLFW_KEY_TAB) - zr_input_key(&gui.ctx, ZR_KEY_TAB, down); - else if (key == GLFW_KEY_DELETE) - zr_input_key(&gui.ctx, ZR_KEY_DEL, down); - else if (key == GLFW_KEY_ENTER) - zr_input_key(&gui.ctx, ZR_KEY_ENTER, down); - else if (key == GLFW_KEY_BACKSPACE) - zr_input_key(&gui.ctx, ZR_KEY_BACKSPACE, down); - else if (key == GLFW_KEY_LEFT) - zr_input_key(&gui.ctx, ZR_KEY_LEFT, down); - else if (key == GLFW_KEY_RIGHT) - zr_input_key(&gui.ctx, ZR_KEY_RIGHT, down); - else if (key == GLFW_KEY_C) - zr_input_key(&gui.ctx, ZR_KEY_COPY, down && (mods & GLFW_MOD_CONTROL)); - else if (key == GLFW_KEY_V) - zr_input_key(&gui.ctx, ZR_KEY_PASTE, down && (mods & GLFW_MOD_CONTROL)); - else if (key == GLFW_KEY_X) - zr_input_key(&gui.ctx, ZR_KEY_CUT, down && (mods & GLFW_MOD_CONTROL)); -} - -static void -input_motion(GLFWwindow *window, double xpos, double ypos) -{ - const int x = (int)xpos; - const int y = (int)ypos; - UNUSED(window); - mouse_pos_x = x; - mouse_pos_y = y; - zr_input_motion(&gui.ctx, x, y); -} - -static void -input_button(GLFWwindow *window, int button, int action, int mods) -{ - int x = mouse_pos_x; - int y = mouse_pos_y; - UNUSED(window); - UNUSED(mods); - if (button == 0) - zr_input_button(&gui.ctx, ZR_BUTTON_LEFT, x, y, action == GLFW_PRESS); - if (button == 1) - zr_input_button(&gui.ctx, ZR_BUTTON_RIGHT, x, y, action == GLFW_PRESS); - if (button == 2) - zr_input_button(&gui.ctx, ZR_BUTTON_MIDDLE, x, y, action == GLFW_PRESS); -} - -static void -input_text(GLFWwindow *window, unsigned int codepoint) -{ - UNUSED(window); - zr_input_unicode(&gui.ctx, codepoint); -} - -static void -input_scroll(GLFWwindow *window, double xoffset, double yoffset) -{ - UNUSED(window); - UNUSED(xoffset); - zr_input_scroll(&gui.ctx, (float)yoffset); -} - -int -main(int argc, char *argv[]) -{ - /* Platform */ - int i; - static GLFWwindow *win; - const char *font_path; - int width = 0, height = 0; - int running = 1; - - /* GUI */ - struct device device; - struct zr_font *font; - struct zr_font_atlas atlas; - font_path = (argc > 1) ? argv[1]: 0; - - glfwSetErrorCallback(error_callback); - if (!glfwInit()) { - fprintf(stdout, "[GFLW] failed to init!\n"); - return 1; - } - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); -#ifdef __APPLE__ - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); -#endif - win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); - glfwMakeContextCurrent(win); - - glfwSetCursorPosCallback(win, input_motion); - glfwSetMouseButtonCallback(win, input_button); - glfwSetKeyCallback(win, input_key); - glfwSetCharCallback(win, input_text); - glfwSetScrollCallback(win, input_scroll); - - /* OpenGL */ - glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - glewExperimental = 1; - if (glewInit() != GLEW_OK) - die("Failed to setup GLEW\n"); - - device_init(&device); - { - /* Font */ - const void *image; - int w, h; - - zr_font_atlas_init_default(&atlas); - zr_font_atlas_begin(&atlas); - if (font_path) font = zr_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); - else font = zr_font_atlas_add_default(&atlas, 14.0f, NULL); - image = zr_font_atlas_bake(&atlas, &w, &h, ZR_FONT_ATLAS_RGBA32); - device_upload_atlas(&device, image, w, h); - zr_font_atlas_end(&atlas, zr_handle_id((int)device.font_tex), &device.null); - - /* GUI */ - memset(&gui, 0, sizeof(gui)); - zr_buffer_init_default(&device.cmds); - zr_init_default(&gui.ctx, &font->handle); - } - - /* icons */ - glEnable(GL_TEXTURE_2D); - gui.icons.unchecked = icon_load("../../icon/unchecked.png"); - gui.icons.checked = icon_load("../../icon/checked.png"); - gui.icons.rocket = icon_load("../../icon/rocket.png"); - gui.icons.cloud = icon_load("../../icon/cloud.png"); - gui.icons.pen = icon_load("../../icon/pen.png"); - gui.icons.play = icon_load("../../icon/play.png"); - gui.icons.pause = icon_load("../../icon/pause.png"); - gui.icons.stop = icon_load("../../icon/stop.png"); - gui.icons.next = icon_load("../../icon/next.png"); - gui.icons.prev = icon_load("../../icon/prev.png"); - gui.icons.tools = icon_load("../../icon/tools.png"); - gui.icons.dir = icon_load("../../icon/directory.png"); - gui.icons.copy = icon_load("../../icon/copy.png"); - gui.icons.convert = icon_load("../../icon/export.png"); - gui.icons.del = icon_load("../../icon/delete.png"); - gui.icons.edit = icon_load("../../icon/edit.png"); - gui.icons.menu[0] = icon_load("../../icon/home.png"); - gui.icons.menu[1] = icon_load("../../icon/phone.png"); - gui.icons.menu[2] = icon_load("../../icon/plane.png"); - gui.icons.menu[3] = icon_load("../../icon/wifi.png"); - gui.icons.menu[4] = icon_load("../../icon/settings.png"); - gui.icons.menu[5] = icon_load("../../icon/volume.png"); - - gui.icons.home = icon_load("../../icon/home.png"); - gui.icons.directory = icon_load("../../icon/directory.png"); - gui.icons.computer = icon_load("../../icon/computer.png"); - gui.icons.desktop = icon_load("../../icon/desktop.png"); - gui.icons.default_file = icon_load("../../icon/default.png"); - gui.icons.text_file = icon_load("../../icon/text.png"); - gui.icons.music_file = icon_load("../../icon/music.png"); - gui.icons.font_file = icon_load("../../icon/font.png"); - gui.icons.img_file = icon_load("../../icon/img.png"); - gui.icons.movie_file = icon_load("../../icon/movie.png"); - - for (i = 0; i < 9; ++i) { - char buffer[256]; - sprintf(buffer, "../../images/image%d.png", (i+1)); - gui.icons.images[i] = icon_load(buffer); - } - - while (!glfwWindowShouldClose(win) && running) { - /* Input */ - zr_input_begin(&gui.ctx); - glfwPollEvents(); - zr_input_end(&gui.ctx); - - /* GUI */ - glfwGetWindowSize(win, &width, &height); - running = run_demo(&gui); - - /* Draw */ - glViewport(0, 0, width, height); - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(0.2f, 0.2f, 0.2f, 1.0f); - device_draw(&device, &gui.ctx, width, height, ZR_ANTI_ALIASING_ON); - glfwSwapBuffers(win); - } - - /* Cleanup */ - glDeleteTextures(1,(const GLuint*)&gui.icons.unchecked.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.checked.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.rocket.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.cloud.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.pen.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.play.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.pause.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.stop.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.next.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.prev.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.tools.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.dir.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.del.handle.id); - - glDeleteTextures(1,(const GLuint*)&gui.icons.home.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.computer.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.desktop.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.default_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.text_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.music_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.font_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.img_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.movie_file.handle.id); - - for (i = 0; i < 9; ++i) - glDeleteTextures(1, (const GLuint*)&gui.icons.images[i].handle.id); - for (i = 0; i < 6; ++i) - glDeleteTextures(1, (const GLuint*)&gui.icons.menu[i].handle.id); - - zr_font_atlas_clear(&atlas); - zr_free(&gui.ctx); - zr_buffer_free(&device.cmds); - device_shutdown(&device); - glfwTerminate(); - return 0; -} - diff --git a/demo/glfw/main.c b/demo/glfw/main.c new file mode 100644 index 0000000..4a00fe5 --- /dev/null +++ b/demo/glfw/main.c @@ -0,0 +1,139 @@ +/* nuklear - v1.00 - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* these defines are both needed for the header + * and source file. So if you split them remember + * to copy them as well. */ +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#include "nuklear_glfw.h" +#include "nuklear_glfw.c" + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_ELEMENT_BUFFER 128 * 1024 + +static void error_callback(int e, const char *d){ + printf("Error %d: %s\n", e, d); +} + +int main(void) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + struct nk_context *ctx; + struct nk_color background; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + ctx = nk_glfw3_init(win, NK_GLFW3_INSTALL_CALLBACKS); + /* Load Fonts: if none of these are loaded a default font will be used */ + {struct nk_font_atlas *atlas; + nk_glfw3_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *robot = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Robot-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_glfw3_font_stash_end(); + /*nk_style_set_font(ctx, &droid->handle)*/;} + + background = nk_rgb(28,48,62); + while (!glfwWindowShouldClose(win)) + { + /* Input */ + glfwPollEvents(); + nk_glfw3_new_frame(); + + /* GUI */ + {struct nk_panel layout; + if (nk_begin(ctx, &layout, "Demo", nk_rect(50, 50, 230, 250), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button", NK_BUTTON_DEFAULT)) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + + nk_layout_row_dynamic(ctx, 22, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + {struct nk_panel combo; + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, &combo, background, 400)) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + }} + } + nk_end(ctx);} + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + nk_glfw3_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + glfwSwapBuffers(win);} + } + nk_glfw3_shutdown(); + glfwTerminate(); + return 0; +} + diff --git a/demo/glfw/nuklear_glfw.c b/demo/glfw/nuklear_glfw.c new file mode 100644 index 0000000..6982ec3 --- /dev/null +++ b/demo/glfw/nuklear_glfw.c @@ -0,0 +1,374 @@ +#include + +#define NK_IMPLEMENTATION +#include "nuklear_glfw.h" +#include "../../nuklear.h" + +#define NK_GLFW_TEXT_MAX 256 +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; +}; + +static struct nk_glfw { + GLFWwindow *win; + struct nk_glfw_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; + unsigned int text[NK_GLFW_TEXT_MAX]; + int text_len; + float scroll; +} glfw; + +NK_API void +nk_glfw3_device_create(void) +{ + GLint status; + static const GLchar *vertex_shader = + "#version 300 es\n" + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "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 = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\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_draw_vertex); + size_t vp = offsetof(struct nk_draw_vertex, position); + size_t vt = offsetof(struct nk_draw_vertex, uv); + size_t vc = offsetof(struct nk_draw_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); + glBindVertexArray(0); +} + +NK_INTERN void +nk_glfw3_device_upload_atlas(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(void) +{ + 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(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + struct nk_glfw_device *dev = &glfw.ogl; + int width, height; + GLint last_prog, last_tex; + GLint last_ebo, last_vbo, last_vao; + 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}, + }; + glfwGetWindowSize(glfw.win, &width, &height); + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* save previous opengl state */ + glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); + + /* 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]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + 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 */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill converting configuration */ + struct nk_convert_config config; + memset(&config, 0, sizeof(config)); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.null = dev->null; + + /* 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);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor((GLint)cmd->clip_rect.x, + height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), + (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&glfw.ctx); + } + + /* restore old state */ + glUseProgram((GLuint)last_prog); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); + glBindVertexArray((GLuint)last_vao); + glDisable(GL_SCISSOR_TEST); +} + +NK_API void +nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) +{ + (void)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) +{ + (void)win; (void)xoff; + glfw.scroll += (float)yoff; +} + +static void +nk_glfw3_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + const char *text = glfwGetClipboardString(glfw.win); + if (text) nk_textedit_paste(edit, text, nk_strlen(text)); + (void)usr; +} + +static void +nk_glfw3_clipbard_copy(nk_handle usr, const char *text, int len) +{ + char *str = 0; + (void)usr; + if (!len) return; + str = 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(GLFWwindow *win, enum nk_glfw_init_state init_state) +{ + glfw.win = win; + if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { + glfwSetScrollCallback(win, nk_gflw3_scroll_callback); + glfwSetCharCallback(win, nk_glfw3_char_callback); + } + + nk_init_default(&glfw.ctx, 0); + glfw.ctx.clip.copy = nk_glfw3_clipbard_copy; + glfw.ctx.clip.paste = nk_glfw3_clipbard_paste; + glfw.ctx.clip.userdata = nk_handle_ptr(0); + nk_glfw3_device_create(); + return &glfw.ctx; +} + +NK_API void +nk_glfw3_font_stash_begin(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(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_glfw3_device_upload_atlas(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_API void +nk_glfw3_new_frame(void) +{ + int i; + double x, y; + struct nk_context *ctx = &glfw.ctx; + struct GLFWwindow *win = glfw.win; + nk_input_begin(ctx); + for (i = 0; i < glfw.text_len; ++i) + nk_input_unicode(ctx, glfw.text[i]); + + 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_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)) { + 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_P) == 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); + } + + glfwGetCursorPos(win, &x, &y); + nk_input_motion(ctx, (int)x, (int)y); + 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_scroll(ctx, glfw.scroll); + nk_input_end(&glfw.ctx); + + glfw.text_len = 0; + glfw.scroll = 0; +} + +NK_API +void nk_glfw3_shutdown(void) +{ + nk_font_atlas_clear(&glfw.atlas); + nk_free(&glfw.ctx); + nk_glfw3_device_destroy(); +} + diff --git a/demo/glfw/nuklear_glfw.h b/demo/glfw/nuklear_glfw.h new file mode 100644 index 0000000..3eab084 --- /dev/null +++ b/demo/glfw/nuklear_glfw.h @@ -0,0 +1,23 @@ +#ifndef NK_GLFW_H_ +#define NK_GLFW_H_ + +#include "../../nuklear.h" + +#include + +enum nk_glfw_init_state{NK_GLFW3_DEFAULT=0, NK_GLFW3_INSTALL_CALLBACKS}; +NK_API struct nk_context *nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state); +NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_glfw3_font_stash_end(void); + +NK_API void nk_glfw3_new_frame(void); +NK_API void nk_glfw3_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer); +NK_API void nk_glfw3_shutdown(void); + +NK_API void nk_glfw3_device_destroy(void); +NK_API void nk_glfw3_device_create(void); + +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); + +#endif diff --git a/demo/linuxgl/Makefile b/demo/linuxgl/Makefile deleted file mode 100644 index f08d4d3..0000000 --- a/demo/linuxgl/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Install -BIN = zahnrad - -# Compiler -CC = clang -DCC = gcc - -# Flags -CFLAGS = -std=c99 -pedantic -O2 - -SRC = linuxgl.c ../../zahnrad.c -OBJ = $(SRC:.c=.o) - -# Modes -.PHONY: gcc -gcc: CC = gcc -gcc: $(BIN) - -.PHONY: clang -clang: CC = clang -clang: $(BIN) - -$(BIN): - @mkdir -p bin - rm -f bin/$(BIN) $(OBJS) - $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) -lX11 -lm -lGL -lm -lGLU diff --git a/demo/linuxgl/linuxgl.c b/demo/linuxgl/linuxgl.c deleted file mode 100644 index 24f6131..0000000 --- a/demo/linuxgl/linuxgl.c +++ /dev/null @@ -1,963 +0,0 @@ -/* - Copyright (c) 2016 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 -#include - -#include -#include -#include -#include - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -#include "../../zahnrad.h" -#include "../demo.c" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#pragma clang diagnostic ignored "-Wfloat-equal" -#pragma clang diagnostic ignored "-Wbad-function-cast" -#pragma clang diagnostic ignored "-Wcast-qual" -#pragma clang diagnostic ignored "-Wshadow" -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#pragma clang diagnostic ignored "-Wdeclaration-after-statement" -#pragma clang diagnostic ignored "-Wunused-function" -#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wbad-function-cast" -#pragma GCC diagnostic ignored "-Wcast-qual" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" -#pragma GCC diagnostic ignored "-Wtype-limits" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wunused-function" -#elif _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4456) -#endif - -#define STB_IMAGE_IMPLEMENTATION -#include "../stb_image.h" - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic pop -#elif _MSC_VER -#pragma warning (pop) -#endif - -/* prefered OpenGL version */ -#define OGL_MAJOR_VERSION 3 -#define OGL_MINOR_VERSION 0 - -#define MAX_VERTEX_MEMORY 512 * 1024 -#define MAX_ELEMENT_MEMORY 128 * 1024 -#define UNUSED(a) ((void)(a)) - -typedef GLXContext(*glxCreateContext)(Display*, GLXFBConfig, GLXContext, Bool, const int*); -/* GL_ARB_vertex_buffer_object */ -typedef void(*qglGenBuffers)(GLsizei, GLuint*); -typedef void(*qglBindBuffer)(GLenum, GLuint); -typedef void(*qglBufferData)(GLenum, GLsizeiptr, const GLvoid*, GLenum); -typedef void(*qglBufferSubData)(GLenum, GLintptr, GLsizeiptr, const GLvoid*); -typedef void*(*qglMapBuffer)(GLenum, GLenum); -typedef GLboolean(*qglUnmapBuffer)(GLenum); -typedef void(*qglDeleteBuffers)(GLsizei, GLuint*); -/* GL_ARB_vertex_array_object */ -typedef void (*qglGenVertexArrays)(GLsizei, GLuint*); -typedef void (*qglBindVertexArray)(GLuint); -typedef void (*qglDeleteVertexArrays)(GLsizei, const GLuint*); -/* GL_ARB_vertex_program / GL_ARB_fragment_program */ -typedef void(*qglVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*); -typedef void(*qglEnableVertexAttribArray)(GLuint); -typedef void(*qglDisableVertexAttribArray)(GLuint); -/* GL_ARB_framebuffer_object */ -typedef void(*qglGenerateMipmap)(GLenum target); -/* GLSL/OpenGL 2.0 core */ -typedef GLuint(*qglCreateShader)(GLenum); -typedef void(*qglShaderSource)(GLuint, GLsizei, const GLchar**, const GLint*); -typedef void(*qglCompileShader)(GLuint); -typedef void(*qglGetShaderiv)(GLuint, GLenum, GLint*); -typedef void(*qglGetShaderInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*); -typedef void(*qglDeleteShader)(GLuint); -typedef GLuint(*qglCreateProgram)(void); -typedef void(*qglAttachShader)(GLuint, GLuint); -typedef void(*qglDetachShader)(GLuint, GLuint); -typedef void(*qglLinkProgram)(GLuint); -typedef void(*qglUseProgram)(GLuint); -typedef void(*qglGetProgramiv)(GLuint, GLenum, GLint*); -typedef void(*qglGetProgramInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*); -typedef void(*qglDeleteProgram)(GLuint); -typedef GLint(*qglGetUniformLocation)(GLuint, const GLchar*); -typedef GLint(*qglGetAttribLocation)(GLuint, const GLchar*); -typedef void(*qglUniform1i)(GLint, GLint); -typedef void(*qglUniform1f)(GLint, GLfloat); -typedef void(*qglUniformMatrix3fv)(GLint, GLsizei, GLboolean, const GLfloat*); -typedef void(*qglUniformMatrix4fv)(GLint, GLsizei, GLboolean, const GLfloat*); - -static qglGenBuffers glGenBuffers; -static qglBindBuffer glBindBuffer; -static qglBufferData glBufferData; -static qglBufferSubData glBufferSubData; -static qglMapBuffer glMapBuffer; -static qglUnmapBuffer glUnmapBuffer; -static qglDeleteBuffers glDeleteBuffers; -static qglGenVertexArrays glGenVertexArrays; -static qglBindVertexArray glBindVertexArray; -static qglDeleteVertexArrays glDeleteVertexArrays; -static qglVertexAttribPointer glVertexAttribPointer; -static qglEnableVertexAttribArray glEnableVertexAttribArray; -static qglDisableVertexAttribArray glDisableVertexAttribArray; -static qglGenerateMipmap glGenerateMipmap; -static qglCreateShader glCreateShader; -static qglShaderSource glShaderSource; -static qglCompileShader glCompileShader; -static qglGetShaderiv glGetShaderiv; -static qglGetShaderInfoLog glGetShaderInfoLog; -static qglDeleteShader glDeleteShader; -static qglCreateProgram glCreateProgram; -static qglAttachShader glAttachShader; -static qglDetachShader glDetachShader; -static qglLinkProgram glLinkProgram; -static qglUseProgram glUseProgram; -static qglGetProgramiv glGetProgramiv; -static qglGetProgramInfoLog glGetProgramInfoLog; -static qglDeleteProgram glDeleteProgram; -static qglGetUniformLocation glGetUniformLocation; -static qglGetAttribLocation glGetAttribLocation; -static qglUniform1i glUniform1i; -static qglUniform1f glUniform1f; -static qglUniformMatrix3fv glUniformMatrix3fv; -static qglUniformMatrix4fv glUniformMatrix4fv; - -enum graphics_card_vendors { - VENDOR_UNKNOWN, - VENDOR_NVIDIA, - VENDOR_AMD, - VENDOR_INTEL -}; - -struct opengl { - /* context */ - GLXContext ctx; - glxCreateContext create_context; - /* info */ - const char *vendor_str; - const char *version_str; - const char *extensions_str; - const char *renderer_str; - const char *glsl_version_str; - enum graphics_card_vendors vendor; - /* version */ - float version; - int major_version; - int minor_version; - /* extensions */ - int glsl_available; - int vertex_buffer_obj_available; - int vertex_array_obj_available; - int map_buffer_range_available; - int fragment_program_available; - int frame_buffer_object_available; -}; - -struct device { - 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 zr_draw_null_texture null; - struct zr_buffer cmds; -}; - -struct XWindow { - Display *dpy; - Window win; - XVisualInfo *vis; - Colormap cmap; - XSetWindowAttributes swa; - XWindowAttributes attr; - GLXFBConfig fbc; - int width, height; -}; -static int gl_err = FALSE; - -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 struct zr_image -icon_load(const char *filename) -{ - int x,y,n; - GLuint tex; - unsigned char *data = stbi_load(filename, &x, &y, &n, 0); - if (!data) die("[SDL]: failed to load image: %s", filename); - - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); - stbi_image_free(data); - return zr_image_id((int)tex); -} - -static int -gl_check_extension(struct opengl *GL, const char *ext) -{ - const char *start, *where, *term; - where = strchr(ext, ' '); - if (where || *ext == '\0') - return FALSE; - - for (start = GL->extensions_str;;) { - where = strstr((const char*)start, ext); - if (!where) break; - term = where + strlen(ext); - if (where == start || *(where - 1) == ' ') { - if (*term == ' ' || *term == '\0') - return TRUE; - } - start = term; - } - return FALSE; -} - -#define GL_EXT(name) (q##name)gl_ext(#name) -static __GLXextFuncPtr -gl_ext(const char *name) -{ - __GLXextFuncPtr func; - func = glXGetProcAddress((const GLubyte*)name); - if (!func) { - fprintf(stdout, "[GL]: failed to load extension: %s", name); - return NULL; - } - return func; -} - -static int -gl_error_handler(Display *dpy, XErrorEvent *ev) -{ - UNUSED((dpy, ev)); - gl_err = TRUE; - return 0; -} - -static int -stricmpn(const char *a, const char *b, int len) -{ - int i = 0; - for (i = 0; i < len && a[i] && b[i]; ++i) - if (a[i] != b[i]) return 1; - if (i != len) return 1; - return 0; -} - -static void -device_init(struct device *dev) -{ - GLint status; - static const GLchar *vertex_shader = - "#version 300 es\n" - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 TexCoord;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "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 = - "#version 300 es\n" - "precision mediump float;\n" - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main(){\n" - " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" - "}\n"; - - 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 zr_draw_vertex); - size_t vp = offsetof(struct zr_draw_vertex, position); - size_t vt = offsetof(struct zr_draw_vertex, uv); - size_t vc = offsetof(struct zr_draw_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); - glBindVertexArray(0); -} - -static void -device_upload_atlas(struct device *dev, const void *image, int width, int height) -{ - 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); -} - -static void -device_shutdown(struct device *dev) -{ - 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); - glDeleteVertexArrays(1, &dev->vao); -} - -static void -device_draw(struct device *dev, struct zr_context *ctx, int width, int height, - enum zr_anti_aliasing AA) -{ - GLint last_prog, last_tex; - GLint last_ebo, last_vbo, last_vao; - 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)width; - ortho[1][1] /= (GLfloat)height; - - /* save previous opengl state */ - glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); - - /* 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]); - - { - /* convert from command queue into draw list and draw to screen */ - void *vertices, *elements; - const struct zr_draw_command *cmd; - const zr_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_MEMORY, NULL, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); - - /* load draw vertices & elements directly into vertex + element buffer */ - vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); - { - struct zr_buffer vbuf, ebuf; - - /* fill converting configuration */ - struct zr_convert_config config; - memset(&config, 0, sizeof(config)); - config.global_alpha = 1.0f; - config.shape_AA = AA; - config.line_AA = AA; - config.circle_segment_count = 22; - config.arc_segment_count = 22; - config.curve_segment_count = 22; - config.null = dev->null; - - /* setup buffers to load vertices and elements */ - zr_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); - zr_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); - zr_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config); - } - glUnmapBuffer(GL_ARRAY_BUFFER); - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - - /* iterate over and execute each draw command */ - zr_draw_foreach(cmd, ctx, &dev->cmds) { - if (!cmd->elem_count) continue; - glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); - glScissor((GLint)cmd->clip_rect.x, - height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), - (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); - glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); - offset += cmd->elem_count; - } - zr_clear(ctx); - } - - /* restore old state */ - glUseProgram((GLuint)last_prog); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); - glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); - glBindVertexArray((GLuint)last_vao); - glDisable(GL_SCISSOR_TEST); -} - - -static void -input_key(struct XWindow *xw, struct zr_context *ctx, XEvent *evt, int down) -{ - int ret; - KeySym *code = XGetKeyboardMapping(xw->dpy, (KeyCode)evt->xkey.keycode, 1, &ret); - if (*code == XK_Shift_L || *code == XK_Shift_R) - zr_input_key(ctx, ZR_KEY_SHIFT, down); - else if (*code == XK_Delete) - zr_input_key(ctx, ZR_KEY_DEL, down); - else if (*code == XK_Return) - zr_input_key(ctx, ZR_KEY_ENTER, down); - else if (*code == XK_Tab) - zr_input_key(ctx, ZR_KEY_TAB, down); - else if (*code == XK_space && !down) - zr_input_char(ctx, ' '); - else if (*code == XK_Left) - zr_input_key(ctx, ZR_KEY_LEFT, down); - else if (*code == XK_Right) - zr_input_key(ctx, ZR_KEY_RIGHT, down); - else if (*code == XK_BackSpace) - zr_input_key(ctx, ZR_KEY_BACKSPACE, down); - else if (*code > 32 && *code < 128) { - if (*code == 'c') - zr_input_key(ctx, ZR_KEY_COPY, down && (evt->xkey.state & ControlMask)); - else if (*code == 'v') - zr_input_key(ctx, ZR_KEY_PASTE, down && (evt->xkey.state & ControlMask)); - else if (*code == 'x') - zr_input_key(ctx, ZR_KEY_CUT, down && (evt->xkey.state & ControlMask)); - if (!down) - zr_input_unicode(ctx, (zr_rune)*code); - } - XFree(code); -} - -static void -input_motion(struct zr_context *ctx, XEvent *evt) -{ - const int x = evt->xmotion.x; - const int y = evt->xmotion.y; - zr_input_motion(ctx, x, y); -} - -static void -input_button(struct zr_context *ctx, XEvent *evt, int down) -{ - const int x = evt->xbutton.x; - const int y = evt->xbutton.y; - if (evt->xbutton.button == Button1) - zr_input_button(ctx, ZR_BUTTON_LEFT, x, y, down); - else if (evt->xbutton.button == Button2) - zr_input_button(ctx, ZR_BUTTON_MIDDLE, x, y, down); - else if (evt->xbutton.button == Button3) - zr_input_button(ctx, ZR_BUTTON_RIGHT, x, y, down); - else if (evt->xbutton.button == Button4) - zr_input_scroll(ctx, 1.0f); - else if (evt->xbutton.button == Button5) - zr_input_scroll(ctx, -1.0f); -} - -int main(int argc, char **argv) -{ - int i; - int running = 1; - const char *font_path; - struct opengl gl; - struct device device; - struct demo gui; - struct zr_font *font; - struct zr_font_atlas atlas; - struct XWindow win; - - memset(&gl, 0, sizeof(gl)); - memset(&win, 0, sizeof(win)); - memset(&gui, 0, sizeof(gui)); - font_path = (argc > 1) ? argv[1]: 0; - - win.dpy = XOpenDisplay(NULL); - if (!win.dpy) die("Failed to open X display\n"); - { - /* check glx version */ - int glx_major, glx_minor; - if (!glXQueryVersion(win.dpy, &glx_major, &glx_minor)) - die("[X11]: Error: Failed to query OpenGL version\n"); - if ((glx_major == 1 && glx_minor < 3) || (glx_major < 1)) - die("[X11]: Error: Invalid GLX version!\n"); - fprintf(stdout, "[X11]: OpenGL version %d.%d\n", glx_major, glx_minor); - } - { - /* find and pick matching framebuffer visual */ - int fb_count; - static GLint attr[] = { - GLX_X_RENDERABLE, True, - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, - GLX_RED_SIZE, 8, - GLX_GREEN_SIZE, 8, - GLX_BLUE_SIZE, 8, - GLX_ALPHA_SIZE, 8, - GLX_DEPTH_SIZE, 24, - GLX_STENCIL_SIZE, 8, - GLX_DOUBLEBUFFER, True, - None - }; - GLXFBConfig *fbc; - fprintf(stdout, "[X11]: Query matching framebuffer configurations\n"); - fbc = glXChooseFBConfig(win.dpy, DefaultScreen(win.dpy), attr, &fb_count); - if (!fbc) die("[X11]: Error: failed to retrieve framebuffer configuration\n"); - fprintf(stdout, "[X11]: Found %d matching framebuffer configurations\n", fb_count); - { - /* pick framebuffer with most samples per pixel */ - int fb_best = -1, best_num_samples = -1; - for (i = 0; i < fb_count; ++i) { - XVisualInfo *vi = glXGetVisualFromFBConfig(win.dpy, fbc[i]); - if (vi) { - int sample_buffer, samples; - glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLE_BUFFERS, &sample_buffer); - glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLES, &samples); - fprintf(stdout, "\tFramebuffer Config %d: Visual ID 0x%2x: " - "(SAMPLE_BUFFER: %d, SAMPLES: %d)\n", i, (unsigned int)vi->visualid, - sample_buffer, samples); - if ((fb_best < 0) || (sample_buffer && samples > best_num_samples)) - fb_best = i; best_num_samples = samples; - } - } - win.fbc = fbc[fb_best]; - XFree(fbc); - win.vis = glXGetVisualFromFBConfig(win.dpy, win.fbc); - fprintf(stdout, "[X11]: Chosen visual id: 0x%x\n", (unsigned)win.vis->visualid); - } - } - { - /* create window */ - fprintf(stdout, "[X11]: Creating colormap\n"); - win.cmap = XCreateColormap(win.dpy, RootWindow(win.dpy, win.vis->screen), win.vis->visual, AllocNone); - win.swa.colormap = win.cmap; - win.swa.background_pixmap = None; - win.swa.border_pixel = 0; - win.swa.event_mask = - ExposureMask | KeyPressMask | KeyReleaseMask | - ButtonPress | ButtonReleaseMask| ButtonMotionMask | - Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| - PointerMotionMask| StructureNotifyMask; - fprintf(stdout, "[X11]: Creating window\n"); - win.win = XCreateWindow(win.dpy, RootWindow(win.dpy, win.vis->screen), 0, 0, - WINDOW_WIDTH, WINDOW_HEIGHT, 0, win.vis->depth, InputOutput, - win.vis->visual, CWBorderPixel|CWColormap|CWEventMask, &win.swa); - if (!win.win) die("[X11]: Failed to create window\n"); - XFree(win.vis); - XStoreName(win.dpy, win.win, "Zahnrad"); - fprintf(stdout, "[X11]: Mapping window\n"); - XMapWindow(win.dpy, win.win); - } - { - /* create opengl context */ - int(*old_handler)(Display*, XErrorEvent*) = XSetErrorHandler(gl_error_handler); - gl.extensions_str = glXQueryExtensionsString(win.dpy, DefaultScreen(win.dpy)); - gl.create_context = (glxCreateContext) - glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); - - gl_err = FALSE; - if (!gl_check_extension(&gl, "GLX_ARB_create_context") || !gl.create_context) { - fprintf(stdout, "[X11]: glXCreateContextAttribARB() not found...\n"); - fprintf(stdout, "[X11]: ... using old-style GLX context\n"); - gl.ctx = glXCreateNewContext(win.dpy, win.fbc, GLX_RGBA_TYPE, 0, True); - } else { - GLint attr[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, OGL_MAJOR_VERSION, - GLX_CONTEXT_MINOR_VERSION_ARB, OGL_MINOR_VERSION, - None - }; - fprintf(stdout, "[X11]: Creating Context...\n"); - gl.ctx = gl.create_context(win.dpy, win.fbc, 0, True, attr); - XSync(win.dpy, False); - if (gl_err || !gl.ctx) { - /* Could not create GL 3.0 context. Fallback to old 2.x context. - * If a version below 3.0 is requested, implementations will - * return the newest context version compatible with OpenGL - * version less than version 3.0.*/ - attr[1] = 1; attr[3] = 0; - gl_err = FALSE; - fprintf(stdout, "[X11] Failed to create OpenGL 3.0 context\n"); - fprintf(stdout, "[X11] ... using old-style GLX context!\n"); - gl.ctx = gl.create_context(win.dpy, win.fbc, 0, True, attr); - } else fprintf(stdout, "[X11] OpenGL 3.0 Context created\n"); - } - XSync(win.dpy, False); - XSetErrorHandler(old_handler); - if (gl_err || !gl.ctx) - die("[X11]: Failed to create an OpenGL context\n"); - - if (!glXIsDirect(win.dpy, gl.ctx)) - fprintf(stdout, "[X11] Optained indirect GLX rendering context\n"); - else fprintf(stdout, "[X11] Optained direct GLX rendering context\n"); - glXMakeCurrent(win.dpy, win.win, gl.ctx); - } - { - int failed = FALSE; - gl.version_str = (const char*)glGetString(GL_VERSION); - glGetIntegerv(GL_MAJOR_VERSION, &gl.major_version); - glGetIntegerv(GL_MINOR_VERSION, &gl.minor_version); - if (gl.major_version < 2) - die("[GL]: Graphics card does not fullfill minimum OpenGL 2.0 support\n"); - gl.version = (float)gl.major_version + (float)gl.minor_version * 0.1f; - - gl.renderer_str = (const char*)glGetString(GL_RENDERER); - gl.extensions_str = (const char*)glGetString(GL_EXTENSIONS); - gl.glsl_version_str = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); - - gl.vendor_str = (const char*)glGetString(GL_VENDOR); - if (!stricmpn(gl.vendor_str, "ATI", 4) || - !stricmpn(gl.vendor_str, "AMD", 4)) - gl.vendor = VENDOR_AMD; - else if (!stricmpn(gl.vendor_str, "NVIDIA", 6)) - gl.vendor = VENDOR_NVIDIA; - else if (!stricmpn(gl.vendor_str, "Intel", 5)) - gl.vendor = VENDOR_INTEL; - else gl.vendor = VENDOR_UNKNOWN; - - fprintf(stdout, "[GL] OpenGL\n"); - fprintf(stdout, "\tVersion: %d.%d\n", gl.major_version, gl.minor_version); - fprintf(stdout, "\tVendor: %s\n", gl.vendor_str); - fprintf(stdout, "\tRenderer: %s\n", gl.renderer_str); - fprintf(stdout, "\tGLSL: %s\n\n", gl.glsl_version_str); - - /* Extensions */ - fprintf(stdout, "[GL] Loading extensions...\n"); - gl.glsl_available = (gl.version >= 2.0f); - if (gl.glsl_available) { - /* GLSL core in OpenGL > 2 */ - glCreateShader = GL_EXT(glCreateShader); - glShaderSource = GL_EXT(glShaderSource); - glCompileShader = GL_EXT(glCompileShader); - glGetShaderiv = GL_EXT(glGetShaderiv); - glGetShaderInfoLog = GL_EXT(glGetShaderInfoLog); - glDeleteShader = GL_EXT(glDeleteShader); - glCreateProgram = GL_EXT(glCreateProgram); - glAttachShader = GL_EXT(glAttachShader); - glDetachShader = GL_EXT(glDetachShader); - glLinkProgram = GL_EXT(glLinkProgram); - glUseProgram = GL_EXT(glUseProgram); - glGetProgramiv = GL_EXT(glGetProgramiv); - glGetProgramInfoLog = GL_EXT(glGetProgramInfoLog); - glDeleteProgram = GL_EXT(glDeleteProgram); - glGetUniformLocation = GL_EXT(glGetUniformLocation); - glGetAttribLocation = GL_EXT(glGetAttribLocation); - glUniform1i = GL_EXT(glUniform1i); - glUniform1f = GL_EXT(glUniform1f); - glUniformMatrix3fv = GL_EXT(glUniformMatrix3fv); - glUniformMatrix4fv = GL_EXT(glUniformMatrix4fv); - } - gl.vertex_buffer_obj_available = gl_check_extension(&gl, "GL_ARB_vertex_buffer_object"); - if (gl.vertex_buffer_obj_available) { - /* GL_ARB_vertex_buffer_object */ - glGenBuffers = GL_EXT(glGenBuffers); - glBindBuffer = GL_EXT(glBindBuffer); - glBufferData = GL_EXT(glBufferData); - glBufferSubData = GL_EXT(glBufferSubData); - glMapBuffer = GL_EXT(glMapBuffer); - glUnmapBuffer = GL_EXT(glUnmapBuffer); - glDeleteBuffers = GL_EXT(glDeleteBuffers); - } - gl.fragment_program_available = gl_check_extension(&gl, "GL_ARB_fragment_program"); - if (gl.fragment_program_available) { - /* GL_ARB_vertex_program / GL_ARB_fragment_program */ - glVertexAttribPointer = GL_EXT(glVertexAttribPointer); - glEnableVertexAttribArray = GL_EXT(glEnableVertexAttribArray); - glDisableVertexAttribArray = GL_EXT(glDisableVertexAttribArray); - } - gl.vertex_array_obj_available = gl_check_extension(&gl, "GL_ARB_vertex_array_object"); - if (gl.vertex_array_obj_available) { - /* GL_ARB_vertex_array_object */ - glGenVertexArrays = GL_EXT(glGenVertexArrays); - glBindVertexArray = GL_EXT(glBindVertexArray); - glDeleteVertexArrays = GL_EXT(glDeleteVertexArrays); - } - gl.frame_buffer_object_available = gl_check_extension(&gl, "GL_ARB_framebuffer_object"); - if (gl.frame_buffer_object_available) { - /* GL_ARB_framebuffer_object */ - glGenerateMipmap = GL_EXT(glGenerateMipmap); - } - if (!gl.vertex_buffer_obj_available) { - fprintf(stdout, "[GL] Error: GL_ARB_vertex_buffer_object is not available!\n"); - failed = TRUE; - } - if (!gl.fragment_program_available) { - fprintf(stdout, "[GL] Error: GL_ARB_fragment_program is not available!\n"); - failed = TRUE; - } - if (!gl.vertex_array_obj_available) { - fprintf(stdout, "[GL] Error: GL_ARB_vertex_array_object is not available!\n"); - failed = TRUE; - } - if (!gl.frame_buffer_object_available) { - fprintf(stdout, "[GL] Error: GL_ARB_framebuffer_object is not available!\n"); - failed = TRUE; - } - if (failed) goto cleanup; - fprintf(stdout, "[GL] Extensions successfully loaded\n"); - } - - /* screen */ - XGetWindowAttributes(win.dpy, win.win, &win.attr); - win.width = win.attr.width; - win.height = win.attr.height; - - device_init(&device); - { - /* Font */ - const void *image; - int width, height; - - zr_font_atlas_init_default(&atlas); - zr_font_atlas_begin(&atlas); - if (font_path) font = zr_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); - else font = zr_font_atlas_add_default(&atlas, 14.0f, NULL); - image = zr_font_atlas_bake(&atlas, &width, &height, ZR_FONT_ATLAS_RGBA32); - device_upload_atlas(&device, image, width, height); - zr_font_atlas_end(&atlas, zr_handle_id((int)device.font_tex), &device.null); - - /* GUI */ - memset(&gui, 0, sizeof(gui)); - zr_buffer_init_default(&device.cmds); - zr_init_default(&gui.ctx, &font->handle); - } - - - device_init(&device); - glEnable(GL_TEXTURE_2D); - gui.icons.unchecked = icon_load("../../icon/unchecked.png"); - gui.icons.checked = icon_load("../../icon/checked.png"); - gui.icons.rocket = icon_load("../../icon/rocket.png"); - gui.icons.cloud = icon_load("../../icon/cloud.png"); - gui.icons.pen = icon_load("../../icon/pen.png"); - gui.icons.play = icon_load("../../icon/play.png"); - gui.icons.pause = icon_load("../../icon/pause.png"); - gui.icons.stop = icon_load("../../icon/stop.png"); - gui.icons.next = icon_load("../../icon/next.png"); - gui.icons.prev = icon_load("../../icon/prev.png"); - gui.icons.tools = icon_load("../../icon/tools.png"); - gui.icons.dir = icon_load("../../icon/directory.png"); - gui.icons.copy = icon_load("../../icon/copy.png"); - gui.icons.convert = icon_load("../../icon/export.png"); - gui.icons.del = icon_load("../../icon/delete.png"); - gui.icons.edit = icon_load("../../icon/edit.png"); - gui.icons.menu[0] = icon_load("../../icon/home.png"); - gui.icons.menu[1] = icon_load("../../icon/phone.png"); - gui.icons.menu[2] = icon_load("../../icon/plane.png"); - gui.icons.menu[3] = icon_load("../../icon/wifi.png"); - gui.icons.menu[4] = icon_load("../../icon/settings.png"); - gui.icons.menu[5] = icon_load("../../icon/volume.png"); - - gui.icons.home = icon_load("../../icon/home.png"); - gui.icons.directory = icon_load("../../icon/directory.png"); - gui.icons.computer = icon_load("../../icon/computer.png"); - gui.icons.desktop = icon_load("../../icon/desktop.png"); - gui.icons.default_file = icon_load("../../icon/default.png"); - gui.icons.text_file = icon_load("../../icon/text.png"); - gui.icons.music_file = icon_load("../../icon/music.png"); - gui.icons.font_file = icon_load("../../icon/font.png"); - gui.icons.img_file = icon_load("../../icon/img.png"); - gui.icons.movie_file = icon_load("../../icon/movie.png"); - - for (i = 0; i < 9; ++i) { - char buffer[256]; - sprintf(buffer, "../../images/image%d.png", (i+1)); - gui.icons.images[i] = icon_load(buffer); - } - - while (running) { - /* input */ - XEvent evt; - zr_input_begin(&gui.ctx); - while (XCheckWindowEvent(win.dpy, win.win, win.swa.event_mask, &evt)) { - if (evt.type == KeyPress) - input_key(&win, &gui.ctx, &evt, zr_true); - else if (evt.type == KeyRelease) - input_key(&win, &gui.ctx, &evt, zr_false); - else if (evt.type == ButtonPress) - input_button(&gui.ctx, &evt, zr_true); - else if (evt.type == ButtonRelease) - input_button(&gui.ctx, &evt, zr_false); - else if (evt.type == MotionNotify) - input_motion(&gui.ctx, &evt); - else if (evt.type == Expose || evt.type == ConfigureNotify) { - XGetWindowAttributes(win.dpy, win.win, &win.attr); - win.width = win.attr.width; - win.height = win.attr.height; - } - } - zr_input_end(&gui.ctx); - - /* GUI */ - XGetWindowAttributes(win.dpy, win.win, &win.attr); - running = run_demo(&gui); - - /* Draw */ - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(0.2f, 0.2f, 0.2f, 1.0f); - glViewport(0, 0, win.width, win.height); - device_draw(&device, &gui.ctx, win.width, win.height, ZR_ANTI_ALIASING_ON); - glXSwapBuffers(win.dpy, win.win); - } - -cleanup: - zr_font_atlas_clear(&atlas); - zr_free(&gui.ctx); - zr_buffer_free(&device.cmds); - device_shutdown(&device); - - glDeleteTextures(1,(const GLuint*)&gui.icons.unchecked.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.checked.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.rocket.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.cloud.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.pen.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.play.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.pause.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.stop.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.next.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.prev.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.tools.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.dir.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.del.handle.id); - - glDeleteTextures(1,(const GLuint*)&gui.icons.home.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.computer.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.desktop.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.default_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.text_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.music_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.font_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.img_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.movie_file.handle.id); - - for (i = 0; i < 9; ++i) - glDeleteTextures(1, (const GLuint*)&gui.icons.images[i].handle.id); - for (i = 0; i < 6; ++i) - glDeleteTextures(1, (const GLuint*)&gui.icons.menu[i].handle.id); - - glXMakeCurrent(win.dpy, 0, 0); - glXDestroyContext(win.dpy, gl.ctx); - XUnmapWindow(win.dpy, win.win); - XFreeColormap(win.dpy, win.cmap); - XDestroyWindow(win.dpy, win.win); - XCloseDisplay(win.dpy); - return 0; -} - diff --git a/demo/sdl/Makefile b/demo/sdl/Makefile index 9f73d17..abf4477 100644 --- a/demo/sdl/Makefile +++ b/demo/sdl/Makefile @@ -8,7 +8,7 @@ DCC = gcc # Flags CFLAGS = -std=c99 -pedantic -O2 -SRC = ../../zahnrad.c sdl.c +SRC = main.c OBJ = $(SRC:.c=.o) ifeq ($(OS),Windows_NT) diff --git a/demo/sdl/main.c b/demo/sdl/main.c new file mode 100644 index 0000000..cac485d --- /dev/null +++ b/demo/sdl/main.c @@ -0,0 +1,142 @@ +/* nuklear - v1.00 - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* these defines are both needed for the header + * and source file. So if you split them remember + * to copy them as well. */ +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#include "nuklear_sdl.h" +#include "nuklear_sdl.c" + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +int +main(void) +{ + /* Platform */ + SDL_Window *win; + SDL_GLContext glContext; + struct nk_color background; + int win_width, win_height; + int running = 1; + + /* GUI */ + struct nk_context *ctx; + + /* SDL setup */ + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + win = SDL_CreateWindow("Demo", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN); + glContext = SDL_GL_CreateContext(win); + SDL_GetWindowSize(win, &win_width, &win_height); + + /* OpenGL setup */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + ctx = nk_sdl_init(win); + /* Load Fonts: if none of these are loaded a default font will be used */ + {struct nk_font_atlas *atlas; + nk_sdl_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *robot = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Robot-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_sdl_font_stash_end(); + /*nk_style_set_font(ctx, &droid->handle)*/;} + + background = nk_rgb(28,48,62); + while (running) + { + /* Input */ + SDL_Event evt; + nk_input_begin(ctx); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_QUIT) goto cleanup; + nk_sdl_handle_event(&evt); + } + nk_input_end(ctx); + + /* GUI */ + {struct nk_panel layout; + if (nk_begin(ctx, &layout, "Demo", nk_rect(50, 50, 210, 250), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button", NK_BUTTON_DEFAULT)) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 22, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + {struct nk_panel combo; + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, &combo, background, 400)) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + }} + } + nk_end(ctx);} + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + SDL_GetWindowSize(win, &win_width, &win_height); + glViewport(0, 0, win_width, win_height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + nk_sdl_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_MEMORY, MAX_ELEMENT_MEMORY); + SDL_GL_SwapWindow(win);} + } + +cleanup: + nk_sdl_shutdown(); + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(win); + SDL_Quit(); + return 0; +} + diff --git a/demo/sdl/nuklear_sdl.c b/demo/sdl/nuklear_sdl.c new file mode 100644 index 0000000..8a89aca --- /dev/null +++ b/demo/sdl/nuklear_sdl.c @@ -0,0 +1,369 @@ +#include +#include + +#include "nuklear_sdl.h" +#define NK_IMPLEMENTATION +#include "../../nuklear.h" + +struct nk_sdl_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; +}; + +static struct nk_sdl { + SDL_Window *win; + struct nk_sdl_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; +} sdl; + +NK_API void +nk_sdl_device_create(void) +{ + GLint status; + static const GLchar *vertex_shader = + "#version 300 es\n" + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "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 = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + struct nk_sdl_device *dev = &sdl.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_draw_vertex); + size_t vp = offsetof(struct nk_draw_vertex, position); + size_t vt = offsetof(struct nk_draw_vertex, uv); + size_t vc = offsetof(struct nk_draw_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); + glBindVertexArray(0); +} + +NK_INTERN void +nk_sdl_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_sdl_device *dev = &sdl.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_sdl_device_destroy(void) +{ + struct nk_sdl_device *dev = &sdl.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_sdl_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + struct nk_sdl_device *dev = &sdl.ogl; + int width, height; + GLint last_prog, last_tex; + GLint last_ebo, last_vbo, last_vao; + 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}, + }; + SDL_GetWindowSize(sdl.win, &width, &height); + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* save previous opengl state */ + glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); + + /* 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]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + 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 */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill converting configuration */ + struct nk_convert_config config; + memset(&config, 0, sizeof(config)); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.null = dev->null; + + /* 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(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor((GLint)cmd->clip_rect.x, + height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), + (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&sdl.ctx); + } + + /* restore old state */ + glUseProgram((GLuint)last_prog); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); + glBindVertexArray((GLuint)last_vao); + glDisable(GL_SCISSOR_TEST); +} + +static void +nk_sdl_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + const char *text = SDL_GetClipboardText(); + if (text) nk_textedit_paste(edit, text, nk_strlen(text)); + (void)usr; +} + +static void +nk_sdl_clipbard_copy(nk_handle usr, const char *text, int len) +{ + char *str = 0; + (void)usr; + if (!len) return; + str = malloc((size_t)len+1); + if (!str) return; + memcpy(str, text, (size_t)len); + str[len] = '\0'; + SDL_SetClipboardText(str); + free(str); +} + +NK_API struct nk_context* +nk_sdl_init(SDL_Window *win) +{ + sdl.win = win; + nk_init_default(&sdl.ctx, 0); + sdl.ctx.clip.copy = nk_sdl_clipbard_copy; + sdl.ctx.clip.paste = nk_sdl_clipbard_paste; + sdl.ctx.clip.userdata = nk_handle_ptr(0); + nk_sdl_device_create(); + return &sdl.ctx; +} + +NK_API void +nk_sdl_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&sdl.atlas); + nk_font_atlas_begin(&sdl.atlas); + *atlas = &sdl.atlas; +} + +NK_API void +nk_sdl_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_sdl_device_upload_atlas(image, w, h); + nk_font_atlas_end(&sdl.atlas, nk_handle_id((int)sdl.ogl.font_tex), &sdl.ogl.null); + if (sdl.atlas.default_font) + nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle); + +} + +NK_API void +nk_sdl_handle_event(SDL_Event *evt) +{ + struct nk_context *ctx = &sdl.ctx; + if (evt->type == SDL_WINDOWEVENT) { + /* handle window resizing */ + if (evt->window.event != SDL_WINDOWEVENT_RESIZED) return; + glViewport(0, 0, evt->window.data1, evt->window.data2); + } else if (evt->type == SDL_KEYUP || evt->type == SDL_KEYDOWN) { + /* key events */ + int down = evt->type == SDL_KEYDOWN; + const Uint8* state = SDL_GetKeyboardState(0); + SDL_Keycode sym = evt->key.keysym.sym; + if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT) + nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (sym == SDLK_DELETE) + nk_input_key(ctx, NK_KEY_DEL, down); + else if (sym == SDLK_RETURN) + nk_input_key(ctx, NK_KEY_ENTER, down); + else if (sym == SDLK_TAB) + nk_input_key(ctx, NK_KEY_TAB, down); + else if (sym == SDLK_BACKSPACE) + nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (sym == SDLK_HOME) + nk_input_key(ctx, NK_KEY_TEXT_START, down); + else if (sym == SDLK_END) + nk_input_key(ctx, NK_KEY_TEXT_END, down); + else if (sym == SDLK_z) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_r) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_c) + nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_v) + nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_x) + nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_b) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_e) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_LEFT) { + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else nk_input_key(ctx, NK_KEY_LEFT, down); + } else if (sym == SDLK_RIGHT) { + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else nk_input_key(ctx, NK_KEY_RIGHT, down); + } + } else if (evt->type == SDL_MOUSEBUTTONDOWN || evt->type == SDL_MOUSEBUTTONUP) { + /* mouse button */ + int down = evt->type == SDL_MOUSEBUTTONDOWN; + const int x = evt->button.x, y = evt->button.y; + if (evt->button.button == SDL_BUTTON_LEFT) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->button.button == SDL_BUTTON_MIDDLE) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + if (evt->button.button == SDL_BUTTON_RIGHT) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + } else if (evt->type == SDL_MOUSEMOTION) { + nk_input_motion(ctx, evt->motion.x, evt->motion.y); + } else if (evt->type == SDL_TEXTINPUT) { + nk_glyph glyph; + memcpy(glyph, evt->text.text, NK_UTF_SIZE); + nk_input_glyph(ctx, glyph); + } else if (evt->type == SDL_MOUSEWHEEL) { + nk_input_scroll(ctx,(float)evt->wheel.y); + } +} + +NK_API +void nk_sdl_shutdown(void) +{ + nk_font_atlas_clear(&sdl.atlas); + nk_free(&sdl.ctx); + nk_sdl_device_destroy(); +} + diff --git a/demo/sdl/nuklear_sdl.h b/demo/sdl/nuklear_sdl.h new file mode 100644 index 0000000..d942325 --- /dev/null +++ b/demo/sdl/nuklear_sdl.h @@ -0,0 +1,18 @@ +#ifndef NK_SDL_H_ +#define NK_SDL_H_ + +#include "../../nuklear.h" + +#include + +NK_API struct nk_context *nk_sdl_init(SDL_Window *win); +NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_sdl_font_stash_end(void); +NK_API void nk_sdl_handle_event(SDL_Event *evt); +NK_API void nk_sdl_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer); +NK_API void nk_sdl_shutdown(void); + +NK_API void nk_sdl_device_destroy(void); +NK_API void nk_sdl_device_create(void); + +#endif diff --git a/demo/sdl/sdl.c b/demo/sdl/sdl.c deleted file mode 100644 index 43bae65..0000000 --- a/demo/sdl/sdl.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - Copyright (c) 2016 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 -#include - -#include -#include -#include -#include - -/* macros */ -#define MAX_VERTEX_MEMORY 512 * 1024 -#define MAX_ELEMENT_MEMORY 128 * 1024 - -#include "../../zahnrad.h" -#include "../demo.c" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#pragma clang diagnostic ignored "-Wfloat-equal" -#pragma clang diagnostic ignored "-Wbad-function-cast" -#pragma clang diagnostic ignored "-Wcast-qual" -#pragma clang diagnostic ignored "-Wshadow" -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#pragma clang diagnostic ignored "-Wdeclaration-after-statement" -#pragma clang diagnostic ignored "-Wunused-function" -#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wbad-function-cast" -#pragma GCC diagnostic ignored "-Wcast-qual" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" -#pragma GCC diagnostic ignored "-Wtype-limits" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wunused-function" -#elif _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4456) -#endif - -#define STB_IMAGE_IMPLEMENTATION -#include "../stb_image.h" - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic pop -#elif _MSC_VER -#pragma warning (pop) -#endif - - -/* ============================================================== - * - * Utility - * - * ===============================================================*/ -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 struct zr_image -icon_load(const char *filename) -{ - int x,y,n; - GLuint tex; - unsigned char *data = stbi_load(filename, &x, &y, &n, 0); - if (!data) die("[SDL]: failed to load image: %s", filename); - - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); - stbi_image_free(data); - return zr_image_id((int)tex); -} - -struct device { - 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 zr_draw_null_texture null; - struct zr_buffer cmds; -}; - -static void -device_init(struct device *dev) -{ - GLint status; - static const GLchar *vertex_shader = - "#version 300 es\n" - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 TexCoord;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "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 = - "#version 300 es\n" - "precision mediump float;\n" - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main(){\n" - " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" - "}\n"; - - 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 zr_draw_vertex); - size_t vp = offsetof(struct zr_draw_vertex, position); - size_t vt = offsetof(struct zr_draw_vertex, uv); - size_t vc = offsetof(struct zr_draw_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); - glBindVertexArray(0); -} - -static void -device_upload_atlas(struct device *dev, const void *image, int width, int height) -{ - 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); -} - -static void -device_shutdown(struct device *dev) -{ - 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); - glDeleteVertexArrays(1, &dev->vao); -} - -static void -device_draw(struct device *dev, struct zr_context *ctx, int width, int height, - enum zr_anti_aliasing AA) -{ - GLint last_prog, last_tex; - GLint last_ebo, last_vbo, last_vao; - 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)width; - ortho[1][1] /= (GLfloat)height; - - /* save previous opengl state */ - glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); - - /* 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]); - { - /* convert from command queue into draw list and draw to screen */ - const struct zr_draw_command *cmd; - void *vertices, *elements; - const zr_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_MEMORY, NULL, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); - - /* load draw vertices & elements directly into vertex + element buffer */ - vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); - { - struct zr_buffer vbuf, ebuf; - - /* fill converting configuration */ - struct zr_convert_config config; - memset(&config, 0, sizeof(config)); - config.circle_segment_count = 22; - config.arc_segment_count = 22; - config.curve_segment_count = 22; - config.global_alpha = 1.0f; - config.null = dev->null; - config.shape_AA = AA; - config.line_AA = AA; - - /* setup buffers to load vertices and elements */ - zr_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); - zr_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); - zr_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config); - } - glUnmapBuffer(GL_ARRAY_BUFFER); - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - - /* iterate over and execute each draw command */ - zr_draw_foreach(cmd, ctx, &dev->cmds) - { - if (!cmd->elem_count) continue; - glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); - glScissor((GLint)cmd->clip_rect.x, - height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), - (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); - glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); - offset += cmd->elem_count; - } - zr_clear(ctx); - } - /* restore old state */ - glUseProgram((GLuint)last_prog); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); - glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); - glBindVertexArray((GLuint)last_vao); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_BLEND); -} - -static void -input_key(struct zr_context *ctx, SDL_Event *evt, int down) -{ - const Uint8* state = SDL_GetKeyboardState(NULL); - SDL_Keycode sym = evt->key.keysym.sym; - if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT) - zr_input_key(ctx, ZR_KEY_SHIFT, down); - else if (sym == SDLK_DELETE) - zr_input_key(ctx, ZR_KEY_DEL, down); - else if (sym == SDLK_RETURN) - zr_input_key(ctx, ZR_KEY_ENTER, down); - else if (sym == SDLK_TAB) - zr_input_key(ctx, ZR_KEY_TAB, down); - else if (sym == SDLK_BACKSPACE) - zr_input_key(ctx, ZR_KEY_BACKSPACE, down); - else if (sym == SDLK_LEFT) - zr_input_key(ctx, ZR_KEY_LEFT, down); - else if (sym == SDLK_RIGHT) - zr_input_key(ctx, ZR_KEY_RIGHT, down); - else if (sym == SDLK_c) - zr_input_key(ctx, ZR_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); - else if (sym == SDLK_v) - zr_input_key(ctx, ZR_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); - else if (sym == SDLK_x) - zr_input_key(ctx, ZR_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); -} - -static void -input_motion(struct zr_context *ctx, SDL_Event *evt) -{ - const int x = evt->motion.x; - const int y = evt->motion.y; - zr_input_motion(ctx, x, y); -} - -static void -input_button(struct zr_context *ctx, SDL_Event *evt, int down) -{ - const int x = evt->button.x; - const int y = evt->button.y; - if (evt->button.button == SDL_BUTTON_LEFT) - zr_input_button(ctx, ZR_BUTTON_LEFT, x, y, down); - if (evt->button.button == SDL_BUTTON_MIDDLE) - zr_input_button(ctx, ZR_BUTTON_MIDDLE, x, y, down); - if (evt->button.button == SDL_BUTTON_RIGHT) - zr_input_button(ctx, ZR_BUTTON_RIGHT, x, y, down); -} - -static void -input_text(struct zr_context *ctx, SDL_Event *evt) -{ - zr_glyph glyph; - memcpy(glyph, evt->text.text, ZR_UTF_SIZE); - zr_input_glyph(ctx, 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[]) -{ - /* Platform */ - int i; - const char *font_path; - SDL_Window *win; - SDL_GLContext glContext; - int win_width, win_height; - int running = 1; - - /* GUI */ - struct device device; - struct demo gui; - struct zr_font *font; - struct zr_font_atlas atlas; - font_path = (argc > 1) ? argv[1]: 0; - - /* SDL */ - SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - win = SDL_CreateWindow("Demo", - SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN); - glContext = SDL_GL_CreateContext(win); - SDL_GetWindowSize(win, &win_width, &win_height); - - /* OpenGL */ - glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - glewExperimental = 1; - if (glewInit() != GLEW_OK) - die("Failed to setup GLEW\n"); - - device_init(&device); - { - /* Font */ - const void *image; - int width, height; - - zr_font_atlas_init_default(&atlas); - zr_font_atlas_begin(&atlas); - if (font_path) font = zr_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); - else font = zr_font_atlas_add_default(&atlas, 14.0f, NULL); - image = zr_font_atlas_bake(&atlas, &width, &height, ZR_FONT_ATLAS_RGBA32); - device_upload_atlas(&device, image, width, height); - zr_font_atlas_end(&atlas, zr_handle_id((int)device.font_tex), &device.null); - - /* GUI */ - memset(&gui, 0, sizeof(gui)); - zr_buffer_init_default(&device.cmds); - zr_init_default(&gui.ctx, &font->handle); - } - - /* icons */ - glEnable(GL_TEXTURE_2D); - gui.icons.unchecked = icon_load("../../icon/unchecked.png"); - gui.icons.checked = icon_load("../../icon/checked.png"); - gui.icons.rocket = icon_load("../../icon/rocket.png"); - gui.icons.cloud = icon_load("../../icon/cloud.png"); - gui.icons.pen = icon_load("../../icon/pen.png"); - gui.icons.play = icon_load("../../icon/play.png"); - gui.icons.pause = icon_load("../../icon/pause.png"); - gui.icons.stop = icon_load("../../icon/stop.png"); - gui.icons.next = icon_load("../../icon/next.png"); - gui.icons.prev = icon_load("../../icon/prev.png"); - gui.icons.tools = icon_load("../../icon/tools.png"); - gui.icons.dir = icon_load("../../icon/directory.png"); - gui.icons.copy = icon_load("../../icon/copy.png"); - gui.icons.convert = icon_load("../../icon/export.png"); - gui.icons.del = icon_load("../../icon/delete.png"); - gui.icons.edit = icon_load("../../icon/edit.png"); - gui.icons.menu[0] = icon_load("../../icon/home.png"); - gui.icons.menu[1] = icon_load("../../icon/phone.png"); - gui.icons.menu[2] = icon_load("../../icon/plane.png"); - gui.icons.menu[3] = icon_load("../../icon/wifi.png"); - gui.icons.menu[4] = icon_load("../../icon/settings.png"); - gui.icons.menu[5] = icon_load("../../icon/volume.png"); - - gui.icons.home = icon_load("../../icon/home.png"); - gui.icons.directory = icon_load("../../icon/directory.png"); - gui.icons.computer = icon_load("../../icon/computer.png"); - gui.icons.desktop = icon_load("../../icon/desktop.png"); - gui.icons.default_file = icon_load("../../icon/default.png"); - gui.icons.text_file = icon_load("../../icon/text.png"); - gui.icons.music_file = icon_load("../../icon/music.png"); - gui.icons.font_file = icon_load("../../icon/font.png"); - gui.icons.img_file = icon_load("../../icon/img.png"); - gui.icons.movie_file = icon_load("../../icon/movie.png"); - - for (i = 0; i < 9; ++i) { - char buffer[256]; - sprintf(buffer, "../../images/image%d.png", (i+1)); - gui.icons.images[i] = icon_load(buffer); - } - - while (running) { - /* Input */ - SDL_Event evt; - zr_input_begin(&gui.ctx); - 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) - input_key(&gui.ctx, &evt, zr_false); - else if (evt.type == SDL_KEYDOWN) - input_key(&gui.ctx, &evt, zr_true); - else if (evt.type == SDL_MOUSEBUTTONDOWN) - input_button(&gui.ctx, &evt, zr_true); - else if (evt.type == SDL_MOUSEBUTTONUP) - input_button(&gui.ctx, &evt, zr_false); - else if (evt.type == SDL_MOUSEMOTION) - input_motion(&gui.ctx, &evt); - else if (evt.type == SDL_TEXTINPUT) - input_text(&gui.ctx, &evt); - else if (evt.type == SDL_MOUSEWHEEL) - zr_input_scroll(&gui.ctx,(float)evt.wheel.y); - } - zr_input_end(&gui.ctx); - - /* GUI */ - SDL_GetWindowSize(win, &win_width, &win_height); - running = run_demo(&gui); - - /* Draw */ - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(0.3f, 0.3f, 0.3f, 1.0f); - device_draw(&device, &gui.ctx, win_width, win_height, ZR_ANTI_ALIASING_ON); - SDL_GL_SwapWindow(win); - } - -cleanup: - /* Cleanup */ - glDeleteTextures(1,(const GLuint*)&gui.icons.unchecked.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.checked.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.rocket.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.cloud.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.pen.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.play.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.pause.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.stop.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.next.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.prev.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.tools.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.dir.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.del.handle.id); - - glDeleteTextures(1,(const GLuint*)&gui.icons.home.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.computer.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.desktop.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.default_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.text_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.music_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.font_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.img_file.handle.id); - glDeleteTextures(1,(const GLuint*)&gui.icons.movie_file.handle.id); - - for (i = 0; i < 9; ++i) - glDeleteTextures(1, (const GLuint*)&gui.icons.images[i].handle.id); - for (i = 0; i < 6; ++i) - glDeleteTextures(1, (const GLuint*)&gui.icons.menu[i].handle.id); - - zr_free(&gui.ctx); - zr_font_atlas_clear(&atlas); - zr_buffer_free(&device.cmds); - device_shutdown(&device); - SDL_GL_DeleteContext(glContext); - SDL_DestroyWindow(win); - SDL_Quit(); - return 0; -} - diff --git a/demo/x11/Makefile b/demo/x11/Makefile index f5dcdc0..19a2c27 100644 --- a/demo/x11/Makefile +++ b/demo/x11/Makefile @@ -8,7 +8,7 @@ DCC = gcc # Flags CFLAGS = -std=c89 -pedantic -O2 -SRC = xlib.c ../../zahnrad.c +SRC = main.c OBJ = $(SRC:.c=.o) # Modes diff --git a/demo/x11/main.c b/demo/x11/main.c new file mode 100644 index 0000000..7f50f6e --- /dev/null +++ b/demo/x11/main.c @@ -0,0 +1,167 @@ +/* nuklear - v1.00 - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DTIME 20 +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#include "nuklear_xlib.h" +#include "nuklear_xlib.c" + +typedef struct XWindow XWindow; +struct XWindow { + Display *dpy; + Window root; + Visual *vis; + Colormap cmap; + XWindowAttributes attr; + XSetWindowAttributes swa; + Window win; + int screen; + XFont *font; + unsigned int width; + unsigned int height; +}; + +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 void* +xcalloc(size_t siz, size_t n) +{ + void *ptr = calloc(siz, n); + if (!ptr) die("Out of memory\n"); + return ptr; +} + +static long +timestamp(void) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) return 0; + return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000); +} + +static void +sleep_for(long t) +{ + struct timespec req; + const time_t sec = (int)(t/1000); + const long ms = t - (sec * 1000); + req.tv_sec = sec; + req.tv_nsec = ms * 1000000L; + while(-1 == nanosleep(&req, &req)); +} + +int +main(void) +{ + long dt; + long started; + int running = 1; + XWindow xw; + struct nk_context *ctx; + + /* X11 */ + memset(&xw, 0, sizeof xw); + xw.dpy = XOpenDisplay(NULL); + xw.root = DefaultRootWindow(xw.dpy); + xw.screen = XDefaultScreen(xw.dpy); + xw.vis = XDefaultVisual(xw.dpy, xw.screen); + xw.cmap = XCreateColormap(xw.dpy,xw.root,xw.vis,AllocNone); + xw.swa.colormap = xw.cmap; + xw.swa.event_mask = + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPress | ButtonReleaseMask| ButtonMotionMask | + Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| + PointerMotionMask | KeymapStateMask; + xw.win = XCreateWindow(xw.dpy, xw.root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, + XDefaultDepth(xw.dpy, xw.screen), InputOutput, + xw.vis, CWEventMask | CWColormap, &xw.swa); + XStoreName(xw.dpy, xw.win, "X11"); + XMapWindow(xw.dpy, xw.win); + XGetWindowAttributes(xw.dpy, xw.win, &xw.attr); + xw.width = (unsigned int)xw.attr.width; + xw.height = (unsigned int)xw.attr.height; + + + /* GUI */ + xw.font = nk_xfont_create(xw.dpy, "fixed"); + ctx = nk_xlib_init(xw.font, xw.dpy, xw.screen, xw.win, xw.width, xw.height); + while (running) + { + /* Input */ + XEvent evt; + started = timestamp(); + nk_input_begin(ctx); + while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &evt)){ + if (XFilterEvent(&evt, xw.win)) continue; + nk_xlib_handle_event(xw.dpy, xw.screen, xw.win, &evt); + } + nk_input_end(ctx); + + /* GUI */ + {struct nk_panel layout; + if (nk_begin(ctx, &layout, "Demo", nk_rect(50, 50, 200, 300), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button", NK_BUTTON_DEFAULT)) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 22, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + nk_layout_row_dynamic(ctx, 100, 1); + } + nk_end(ctx);} + if (nk_window_is_closed(ctx, "Demo")) break; + + /* Draw */ + XClearWindow(xw.dpy, xw.win); + nk_xlib_render(xw.win, nk_rgb(30,30,30)); + XFlush(xw.dpy); + + /* Timing */ + dt = timestamp() - started; + if (dt < DTIME) + sleep_for(DTIME - dt); + } + + nk_xfont_del(xw.dpy, xw.font); + nk_xlib_shutdown(); + XUnmapWindow(xw.dpy, xw.win); + XFreeColormap(xw.dpy, xw.cmap); + XDestroyWindow(xw.dpy, xw.win); + XCloseDisplay(xw.dpy); + return 0; +} + diff --git a/demo/x11/nuklear_xlib.c b/demo/x11/nuklear_xlib.c new file mode 100644 index 0000000..18d5e04 --- /dev/null +++ b/demo/x11/nuklear_xlib.c @@ -0,0 +1,611 @@ +#include + +#include +#include +#include +#include + +#define NK_IMPLEMENTATION +#include "nuklear_xlib.h" +#include "../../nuklear.h" + +typedef struct XSurface XSurface; +struct XFont { + int ascent; + int descent; + int height; + XFontSet set; + XFontStruct *xfont; +}; +struct XSurface { + GC gc; + Display *dpy; + int screen; + Window root; + Drawable drawable; + unsigned int w, h; +}; +static struct { + struct nk_context ctx; + struct XSurface *surf; +} xlib; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +static unsigned long +color_from_byte(const nk_byte *c) +{ + unsigned long res = 0; + res |= (unsigned long)c[0] << 16; + res |= (unsigned long)c[1] << 8; + res |= (unsigned long)c[2] << 0; + return (res); +} + +static XSurface* +nk_xsurf_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + XSurface *surface = (XSurface*)calloc(1, sizeof(XSurface)); + surface->w = w; + surface->h = h; + surface->dpy = dpy; + surface->screen = screen; + surface->root = root; + surface->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, surface->gc, 1, LineSolid, CapButt, JoinMiter); + surface->drawable = XCreatePixmap(dpy, root, w, h, 32); + return surface; +} + +static void +nk_xsurf_resize(XSurface *surf, unsigned int w, unsigned int h) +{ + if(!surf) return; + if (surf->w == w && surf->h == h) return; + surf->w = w; surf->h = h; + if(surf->drawable) XFreePixmap(surf->dpy, surf->drawable); + surf->drawable = XCreatePixmap(surf->dpy, surf->root, w, h, + (unsigned int)DefaultDepth(surf->dpy, surf->screen)); +} + +static void +nk_xsurf_scissor(XSurface *surf, float x, float y, float w, float h) +{ + XRectangle clip_rect; + clip_rect.x = (short)(x-1); + clip_rect.y = (short)(y-1); + clip_rect.width = (unsigned short)(w+2); + clip_rect.height = (unsigned short)(h+2); + XSetClipRectangles(surf->dpy, surf->gc, 0, 0, &clip_rect, 1, Unsorted); +} + +static void +nk_xsurf_stroke_line(XSurface *surf, short x0, short y0, short x1, + short y1, unsigned int line_thickness, struct nk_color col) +{ + unsigned long c = color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XDrawLine(surf->dpy, surf->drawable, surf->gc, (int)x0, (int)y0, (int)x1, (int)y1); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_stroke_rect(XSurface* surf, short x, short y, unsigned short w, + unsigned short h, unsigned short r, unsigned short line_thickness, struct nk_color col) +{ + unsigned long c = color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + if (r == 0) { + XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h); + } else { + short xc = x + r; + short yc = y + r; + short wc = (short)(w - 2 * r); + short hc = (short)(h - 2 * r); + + XDrawLine(surf->dpy, surf->drawable, surf->gc, xc, y, xc+wc, y); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x+w, yc, x+w, yc+wc); + XDrawLine(surf->dpy, surf->drawable, surf->gc, xc, y+h, xc+wc, y+h); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x, yc, yc+hc, x); + + XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, y, + (unsigned)r*2, (unsigned)r*2, 0 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, x, y, + (unsigned)r*2, (unsigned)r*2, 90 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, x, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, 180 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, -90 * 64, 90 * 64); + } + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_fill_rect(XSurface* surf, short x, short y, unsigned short w, + unsigned short h, unsigned short r, struct nk_color col) +{ + unsigned long c = color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + if (r == 0) { + XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h); + } else { + short xc = x + r; + short yc = y + r; + short wc = (short)(w - 2 * r); + short hc = (short)(h - 2 * r); + + XPoint pnts[12]; + pnts[0].x = x; + pnts[0].y = yc; + pnts[1].x = xc; + pnts[1].y = yc; + pnts[2].x = xc; + pnts[2].y = y; + + pnts[3].x = xc + wc; + pnts[3].y = y; + pnts[4].x = xc + wc; + pnts[4].y = yc; + pnts[5].x = x + w; + pnts[5].y = yc; + + pnts[6].x = x + w; + pnts[6].y = yc + hc; + pnts[7].x = xc + wc; + pnts[7].y = yc + hc; + pnts[8].x = xc + wc; + pnts[8].y = y + h; + + pnts[9].x = xc; + pnts[9].y = y + h; + pnts[10].x = xc; + pnts[10].y = yc + hc; + pnts[11].x = x; + pnts[11].y = yc + hc; + + XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 12, Convex, CoordModeOrigin); + XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, y, + (unsigned)r*2, (unsigned)r*2, 0 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, x, y, + (unsigned)r*2, (unsigned)r*2, 90 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, x, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, 180 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, -90 * 64, 90 * 64); + } +} + +static void +nk_xsurf_fill_triangle(XSurface *surf, short x0, short y0, short x1, + short y1, short x2, short y2, struct nk_color col) +{ + XPoint pnts[3]; + unsigned long c = color_from_byte(&col.r); + pnts[0].x = (short)x0; + pnts[0].y = (short)y0; + pnts[1].x = (short)x1; + pnts[1].y = (short)y1; + pnts[2].x = (short)x2; + pnts[2].y = (short)y2; + XSetForeground(surf->dpy, surf->gc, c); + XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 3, Convex, CoordModeOrigin); +} + +static void +nk_xsurf_stroke_triangle(XSurface *surf, short x0, short y0, short x1, + short y1, short x2, short y2, unsigned short line_thickness, struct nk_color col) +{ + XPoint pnts[3]; + unsigned long c = color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x0, y0, x1, y1); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x1, y1, x2, y2); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x2, y2, x0, y0); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_fill_polygon(XSurface *surf, const struct nk_vec2i *pnts, int count, + struct nk_color col) +{ + int i = 0; + #define MAX_POINTS 64 + XPoint xpnts[MAX_POINTS]; + unsigned long c = color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + for (i = 0; i < count && i < MAX_POINTS; ++i) { + xpnts[i].x = pnts[i].x; + xpnts[i].y = pnts[i].y; + } + XFillPolygon(surf->dpy, surf->drawable, surf->gc, xpnts, count, Convex, CoordModeOrigin); + #undef MAX_POINTS +} + +static void +nk_xsurf_stroke_polygon(XSurface *surf, const struct nk_vec2i *pnts, int count, + unsigned short line_thickness, struct nk_color col) +{ + int i = 0; + unsigned long c = color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + for (i = 1; i < count; ++i) + XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[i-1].x, pnts[i-1].y, pnts[i].x, pnts[i].y); + XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[count-1].x, pnts[count-1].y, pnts[0].x, pnts[0].y); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_stroke_polyline(XSurface *surf, const struct nk_vec2i *pnts, + int count, unsigned short line_thickness, struct nk_color col) +{ + int i = 0; + unsigned long c = color_from_byte(&col.r); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XSetForeground(surf->dpy, surf->gc, c); + for (i = 0; i < count-1; ++i) + XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[i].x, pnts[i].y, pnts[i+1].x, pnts[i+1].y); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_fill_circle(XSurface *surf, short x, short y, unsigned short w, + unsigned short h, struct nk_color col) +{ + unsigned long c = color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XFillArc(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, + (unsigned)w, (unsigned)h, 0, 360 * 64); +} + +static void +nk_xsurf_stroke_circle(XSurface *surf, short x, short y, unsigned short w, + unsigned short h, unsigned short line_thickness, struct nk_color col) +{ + unsigned long c = color_from_byte(&col.r); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XSetForeground(surf->dpy, surf->gc, c); + XDrawArc(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, + (unsigned)w, (unsigned)h, 0, 360 * 64); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_stroke_curve(XSurface *surf, struct nk_vec2i p1, + struct nk_vec2i p2, struct nk_vec2i p3, struct nk_vec2i p4, + unsigned int num_segments, unsigned short line_thickness, struct nk_color col) +{ + unsigned int i_step; + float t_step; + struct nk_vec2i last = p1; + + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + num_segments = MAX(num_segments, 1); + t_step = 1.0f/(float)num_segments; + for (i_step = 1; i_step <= num_segments; ++i_step) { + float t = t_step * (float)i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t * t *t; + float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; + float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; + nk_xsurf_stroke_line(surf, last.x, last.y, (short)x, (short)y, line_thickness,col); + last.x = (short)x; last.y = (short)y; + } + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_draw_text(XSurface *surf, short x, short y, unsigned short w, unsigned short h, + const char *text, int len, XFont *font, struct nk_color cbg, struct nk_color cfg) +{ + int tx, ty; + unsigned long bg = color_from_byte(&cbg.r); + unsigned long fg = color_from_byte(&cfg.r); + + XSetForeground(surf->dpy, surf->gc, bg); + XFillRectangle(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, (unsigned)w, (unsigned)h); + if(!text || !font || !len) return; + + tx = (int)x; + ty = (int)y + font->ascent; + XSetForeground(surf->dpy, surf->gc, fg); + if(font->set) + XmbDrawString(surf->dpy,surf->drawable,font->set,surf->gc,tx,ty,(const char*)text,(int)len); + else + XDrawString(surf->dpy, surf->drawable, surf->gc, tx, ty, (const char*)text, (int)len); +} + +static void +nk_xsurf_clear(XSurface *surf, unsigned long color) +{ + XSetForeground(surf->dpy, surf->gc, color); + XFillRectangle(surf->dpy, surf->drawable, surf->gc, 0, 0, surf->w, surf->h); +} + +static void +nk_xsurf_blit(Drawable target, XSurface *surf, unsigned int w, unsigned int h) +{ + XCopyArea(surf->dpy, surf->drawable, target, surf->gc, 0, 0, w, h, 0, 0); +} + +static void +nk_xsurf_del(XSurface *surf) +{ + XFreePixmap(surf->dpy, surf->drawable); + XFreeGC(surf->dpy, surf->gc); + free(surf); +} + +XFont* +nk_xfont_create(Display *dpy, const char *name) +{ + int n; + char *def, **missing; + XFont *font = (XFont*)calloc(1, sizeof(XFont)); + font->set = XCreateFontSet(dpy, name, &missing, &n, &def); + if(missing) { + while(n--) + fprintf(stderr, "missing fontset: %s\n", missing[n]); + XFreeStringList(missing); + } + + if(font->set) { + XFontStruct **xfonts; + char **font_names; + XExtentsOfFontSet(font->set); + n = XFontsOfFontSet(font->set, &xfonts, &font_names); + while(n--) { + font->ascent = MAX(font->ascent, (*xfonts)->ascent); + font->descent = MAX(font->descent,(*xfonts)->descent); + xfonts++; + } + } else { + if(!(font->xfont = XLoadQueryFont(dpy, name)) + && !(font->xfont = XLoadQueryFont(dpy, "fixed"))) { + free(font); + return 0; + } + font->ascent = font->xfont->ascent; + font->descent = font->xfont->descent; + } + font->height = font->ascent + font->descent; + return font; +} + +static float +nk_xfont_get_text_width(nk_handle handle, float height, const char *text, int len) +{ + XFont *font = (XFont*)handle.ptr; + XRectangle r; + if(!font || !text) + return 0; + + if(font->set) { + XmbTextExtents(font->set, (const char*)text, len, NULL, &r); + return (float)r.width; + } else{ + int w = XTextWidth(font->xfont, (const char*)text, len); + return (float)w; + } +} + +void +nk_xfont_del(Display *dpy, XFont *font) +{ + if(!font) return; + if(font->set) + XFreeFontSet(dpy, font->set); + else + XFreeFont(dpy, font->xfont); + free(font); +} + +NK_API struct nk_context* +nk_xlib_init(XFont *xfont, Display *dpy, int screen, Window root, + unsigned int w, unsigned int h) +{ + struct nk_user_font font; + font.userdata = nk_handle_ptr(xfont); + font.height = (float)xfont->height; + font.width = nk_xfont_get_text_width; + + if (!setlocale(LC_ALL,"")) return 0; + if (!XSupportsLocale()) return 0; + if (!XSetLocaleModifiers("@im=none")) return 0; + + xlib.surf = nk_xsurf_create(dpy, screen, root, w, h); + nk_init_default(&xlib.ctx, &font); + return &xlib.ctx; +} + +NK_API void +nk_xlib_set_font(XFont *xfont) +{ + struct nk_user_font font; + font.userdata = nk_handle_ptr(xfont); + font.height = (float)xfont->height; + font.width = nk_xfont_get_text_width; + nk_style_set_font(&xlib.ctx, &font); +} + +NK_API void +nk_xlib_handle_event(Display *dpy, int screen, Window win, XEvent *evt) +{ + struct nk_context *ctx = &xlib.ctx; + if (evt->type == KeyPress || evt->type == KeyRelease) + { + /* Key handler */ + int ret, down = (evt->type == KeyPress); + KeySym *code = XGetKeyboardMapping(xlib.surf->dpy, (KeyCode)evt->xkey.keycode, 1, &ret); + if (*code == XK_Shift_L || *code == XK_Shift_R) nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (*code == XK_Delete) nk_input_key(ctx, NK_KEY_DEL, down); + else if (*code == XK_Return) nk_input_key(ctx, NK_KEY_ENTER, down); + else if (*code == XK_Tab) nk_input_key(ctx, NK_KEY_TAB, down); + else if (*code == XK_Left) nk_input_key(ctx, NK_KEY_LEFT, down); + else if (*code == XK_Right) nk_input_key(ctx, NK_KEY_RIGHT, down); + else if (*code == XK_BackSpace) nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (*code == XK_Home) nk_input_key(ctx, NK_KEY_TEXT_START, down); + else if (*code == XK_End) nk_input_key(ctx, NK_KEY_TEXT_END, down); + else if (*code == XK_space && !down) nk_input_char(ctx, ' '); + else { + if (*code == 'c' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_COPY, down); + else if (*code == 'v' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_PASTE, down); + else if (*code == 'x' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_CUT, down); + else if (*code == 'z' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down); + else if (*code == 'r' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down); + else if (*code == XK_Left && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else if (*code == XK_Right && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else if (*code == 'b' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down); + else if (*code == 'e' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down); + else if (!down) { + char buf[32]; + if ((*code >= 'a' && *code <= 'z') || (*code >= 'A' && *code <= 'Z')) { + KeySym keysym = 0; + if (XLookupString((XKeyEvent*)evt, buf, 32, &keysym, NULL) != NoSymbol) + nk_input_glyph(ctx, buf); + } + } + } + XFree(code); + } else if (evt->type == ButtonPress || evt->type == ButtonRelease) { + /* Button handler */ + int down = (evt->type == ButtonPress); + const int x = evt->xbutton.x, y = evt->xbutton.y; + if (evt->xbutton.button == Button1) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->xbutton.button == Button2) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + else if (evt->xbutton.button == Button3) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + else if (evt->xbutton.button == Button4) + nk_input_scroll(ctx, 1.0f); + else if (evt->xbutton.button == Button5) + nk_input_scroll(ctx, -1.0f); + + } else if (evt->type == MotionNotify) { + /* Mouse motion handler */ + const int x = evt->xmotion.x, y = evt->xmotion.y; + nk_input_motion(ctx, x, y); + } else if (evt->type == Expose || evt->type == ConfigureNotify) { + /* Window resize handler */ + unsigned int width, height; + XWindowAttributes attr; + XGetWindowAttributes(dpy, win, &attr); + width = (unsigned int)attr.width; + height = (unsigned int)attr.height; + nk_xsurf_resize(xlib.surf, width, height); + } else if (evt->type == KeymapNotify) + XRefreshKeyboardMapping(&evt->xmapping); +} + +NK_API void +nk_xlib_shutdown(void) +{ + nk_xsurf_del(xlib.surf); + nk_free(&xlib.ctx); +} + +NK_API void +nk_xlib_render(Drawable screen, struct nk_color clear) +{ + const struct nk_command *cmd; + struct nk_context *ctx = &xlib.ctx; + XSurface *surf = xlib.surf; + + nk_xsurf_clear(xlib.surf, color_from_byte(&clear.r)); + nk_foreach(cmd, &xlib.ctx) + { + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s =(const struct nk_command_scissor*)cmd; + nk_xsurf_scissor(surf, s->x, s->y, s->w, s->h); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line *)cmd; + nk_xsurf_stroke_line(surf, l->begin.x, l->begin.y, l->end.x, + l->end.y, l->line_thickness, l->color); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect *)cmd; + nk_xsurf_stroke_rect(surf, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->line_thickness, r->color); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd; + nk_xsurf_fill_rect(surf, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->color); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle *)cmd; + nk_xsurf_stroke_circle(surf, c->x, c->y, c->w, c->h, c->line_thickness, c->color); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_xsurf_fill_circle(surf, c->x, c->y, c->w, c->h, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle*t = (const struct nk_command_triangle*)cmd; + nk_xsurf_stroke_triangle(surf, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->line_thickness, t->color); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd; + nk_xsurf_fill_triangle(surf, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->color); + } break; + case NK_COMMAND_POLYGON: { + const struct nk_command_polygon *p =(const struct nk_command_polygon*)cmd; + nk_xsurf_stroke_polygon(surf, p->points, p->point_count, p->line_thickness,p->color); + } break; + case NK_COMMAND_POLYGON_FILLED: { + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled *)cmd; + nk_xsurf_fill_polygon(surf, p->points, p->point_count, p->color); + } break; + case NK_COMMAND_POLYLINE: { + const struct nk_command_polyline *p = (const struct nk_command_polyline *)cmd; + nk_xsurf_stroke_polyline(surf, p->points, p->point_count, p->line_thickness, p->color); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_xsurf_draw_text(surf, t->x, t->y, t->w, t->h, + (const char*)t->string, t->length, + (XFont*)t->font->userdata.ptr, + t->background, t->foreground); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve *)cmd; + nk_xsurf_stroke_curve(surf, q->begin, q->ctrl[0], q->ctrl[1], + q->end, 22, q->line_thickness, q->color); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: + case NK_COMMAND_IMAGE: + case NK_COMMAND_ARC: + case NK_COMMAND_ARC_FILLED: + default: break; + } + } + nk_clear(ctx); + nk_xsurf_blit(screen, surf, surf->w, surf->h); +} + diff --git a/demo/x11/nuklear_xlib.h b/demo/x11/nuklear_xlib.h new file mode 100644 index 0000000..9d4957e --- /dev/null +++ b/demo/x11/nuklear_xlib.h @@ -0,0 +1,17 @@ +#ifndef NK_XLIB_H_ +#define NK_XLIB_H_ + +#include "../../nuklear.h" + +typedef struct XFont XFont; +NK_API struct nk_context* nk_xlib_init(XFont *font, Display *dpy, int screen, Window root, unsigned int w, unsigned int h); +NK_API void nk_xlib_set_font(XFont *font); +NK_API void nk_xlib_handle_event(Display *dpy, int screen, Window win, XEvent *evt); +NK_API void nk_xlib_render(Drawable screen, struct nk_color clear); +NK_API void nk_xlib_shutdown(void); + +/* font */ +NK_API XFont* nk_xfont_create(Display *dpy, const char *name); +NK_API void nk_xfont_del(Display *dpy, XFont *font); + +#endif diff --git a/demo/x11/xlib.c b/demo/x11/xlib.c deleted file mode 100644 index 88a9d26..0000000 --- a/demo/x11/xlib.c +++ /dev/null @@ -1,794 +0,0 @@ -/* - Copyright (c) 2016 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 -#include -#include - -#include -#include -#include -#include - -/* macros */ -#define DTIME 16 -#include "../../zahnrad.h" - -#define DEMO_DO_NOT_DRAW_IMAGES -#define DEMO_DO_NOT_USE_COLOR_PICKER -#include "../demo.c" - -typedef struct XFont XFont; -typedef struct XSurface XSurface; -typedef struct XWindow XWindow; - -struct XFont { - int ascent; - int descent; - int height; - XFontSet set; - XFontStruct *xfont; -}; - -struct XSurface { - GC gc; - Display *dpy; - int screen; - Window root; - Drawable drawable; - unsigned int w, h; -}; - -struct XWindow { - Display *dpy; - Window root; - Visual *vis; - XFont *font; - XSurface *surf; - Colormap cmap; - XWindowAttributes attr; - XSetWindowAttributes swa; - Window win; - int screen; - unsigned int width; - unsigned int height; -}; - -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 void* -xcalloc(size_t siz, size_t n) -{ - void *ptr = calloc(siz, n); - if (!ptr) die("Out of memory\n"); - return ptr; -} - -static long -timestamp(void) -{ - struct timeval tv; - if (gettimeofday(&tv, NULL) < 0) return 0; - return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000); -} - -static void -sleep_for(long t) -{ - struct timespec req; - const time_t sec = (int)(t/1000); - const long ms = t - (sec * 1000); - req.tv_sec = sec; - req.tv_nsec = ms * 1000000L; - while(-1 == nanosleep(&req, &req)); -} - -static XFont* -font_create(Display *dpy, const char *name) -{ - int n; - char *def, **missing; - XFont *font = (XFont*)xcalloc(1, sizeof(XFont)); - font->set = XCreateFontSet(dpy, name, &missing, &n, &def); - if(missing) { - while(n--) - fprintf(stderr, "missing fontset: %s\n", missing[n]); - XFreeStringList(missing); - } - - if(font->set) { - XFontStruct **xfonts; - char **font_names; - XExtentsOfFontSet(font->set); - n = XFontsOfFontSet(font->set, &xfonts, &font_names); - while(n--) { - font->ascent = MAX(font->ascent, (*xfonts)->ascent); - font->descent = MAX(font->descent,(*xfonts)->descent); - xfonts++; - } - } else { - if(!(font->xfont = XLoadQueryFont(dpy, name)) - && !(font->xfont = XLoadQueryFont(dpy, "fixed"))) - die("error, cannot load font: '%s'\n", name); - font->ascent = font->xfont->ascent; - font->descent = font->xfont->descent; - } - font->height = font->ascent + font->descent; - return font; -} - -static zr_size -font_get_text_width(zr_handle handle, float height, const char *text, zr_size len) -{ - XFont *font = (XFont*)handle.ptr; - XRectangle r; - if(!font || !text) - return 0; - - UNUSED(height); - if(font->set) { - XmbTextExtents(font->set, (const char*)text, (int)len, NULL, &r); - return r.width; - } else return (zr_size)XTextWidth(font->xfont, (const char*)text, (int)len); -} - -static void -font_del(Display *dpy, XFont *font) -{ - if(!font) return; - if(font->set) - XFreeFontSet(dpy, font->set); - else - XFreeFont(dpy, font->xfont); - free(font); -} - -static unsigned long -color_from_byte(const zr_byte *c) -{ - unsigned long res = 0; - res |= (unsigned long)c[0] << 16; - res |= (unsigned long)c[1] << 8; - res |= (unsigned long)c[2] << 0; - return (res); -} - -static void -color_to_byte(unsigned char *c, unsigned long pixel) -{ - c[0] = (pixel & ((unsigned long)0xFF << 16)) >> 16; - c[1] = (pixel & ((unsigned long)0xFF << 8)) >> 8; - c[2] = (pixel & ((unsigned long)0xFF << 0)) >> 0; - c[3] = (pixel & ((unsigned long)0xFF << 24)) >> 24; -} - -static XSurface* -surface_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) -{ - XSurface *surface = (XSurface*)xcalloc(1, sizeof(XSurface)); - surface->w = w; - surface->h = h; - surface->dpy = dpy; - surface->screen = screen; - surface->root = root; - surface->gc = XCreateGC(dpy, root, 0, NULL); - XSetLineAttributes(dpy, surface->gc, 1, LineSolid, CapButt, JoinMiter); - surface->drawable = XCreatePixmap(dpy, root, w, h, 32); - return surface; -} - -static void -surface_resize(XSurface *surf, unsigned int w, unsigned int h) -{ - if(!surf) return; - if (surf->w == w && surf->h == h) return; - surf->w = w; surf->h = h; - if(surf->drawable) XFreePixmap(surf->dpy, surf->drawable); - surf->drawable = XCreatePixmap(surf->dpy, surf->root, w, h, - (unsigned int)DefaultDepth(surf->dpy, surf->screen)); -} - -static void -surface_scissor(XSurface *surf, float x, float y, float w, float h) -{ - XRectangle clip_rect; - clip_rect.x = (short)(x-1); - clip_rect.y = (short)(y-1); - clip_rect.width = (unsigned short)(w+2); - clip_rect.height = (unsigned short)(h+2); - XSetClipRectangles(surf->dpy, surf->gc, 0, 0, &clip_rect, 1, Unsorted); -} - -static void -surface_stroke_line(XSurface *surf, short x0, short y0, short x1, - short y1, unsigned int line_thickness, struct zr_color col) -{ - unsigned long c = color_from_byte(&col.r); - XSetForeground(surf->dpy, surf->gc, c); - XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); - XDrawLine(surf->dpy, surf->drawable, surf->gc, (int)x0, (int)y0, (int)x1, (int)y1); - XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); -} - -static void -surface_stroke_rect(XSurface* surf, short x, short y, unsigned short w, - unsigned short h, unsigned short r, unsigned short line_thickness, struct zr_color col) -{ - unsigned long c = color_from_byte(&col.r); - XSetForeground(surf->dpy, surf->gc, c); - XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); - if (r == 0) { - XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h); - } else { - short xc = x + r; - short yc = y + r; - short wc = (short)(w - 2 * r); - short hc = (short)(h - 2 * r); - - XDrawLine(surf->dpy, surf->drawable, surf->gc, xc, y, xc+wc, y); - XDrawLine(surf->dpy, surf->drawable, surf->gc, x+w, yc, x+w, yc+wc); - XDrawLine(surf->dpy, surf->drawable, surf->gc, xc, y+h, xc+wc, y+h); - XDrawLine(surf->dpy, surf->drawable, surf->gc, x, yc, yc+hc, x); - - XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, y, - (unsigned)r*2, (unsigned)r*2, 0 * 64, 90 * 64); - XFillArc(surf->dpy, surf->drawable, surf->gc, x, y, - (unsigned)r*2, (unsigned)r*2, 90 * 64, 90 * 64); - XFillArc(surf->dpy, surf->drawable, surf->gc, x, yc + hc - r, - (unsigned)r*2, (unsigned)2*r, 180 * 64, 90 * 64); - XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, yc + hc - r, - (unsigned)r*2, (unsigned)2*r, -90 * 64, 90 * 64); - } - XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); -} - -static void -surface_fill_rect(XSurface* surf, short x, short y, unsigned short w, - unsigned short h, unsigned short r, struct zr_color col) -{ - unsigned long c = color_from_byte(&col.r); - XSetForeground(surf->dpy, surf->gc, c); - if (r == 0) { - XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h); - } else { - short xc = x + r; - short yc = y + r; - short wc = (short)(w - 2 * r); - short hc = (short)(h - 2 * r); - - XPoint pnts[12]; - pnts[0].x = x; - pnts[0].y = yc; - pnts[1].x = xc; - pnts[1].y = yc; - pnts[2].x = xc; - pnts[2].y = y; - - pnts[3].x = xc + wc; - pnts[3].y = y; - pnts[4].x = xc + wc; - pnts[4].y = yc; - pnts[5].x = x + w; - pnts[5].y = yc; - - pnts[6].x = x + w; - pnts[6].y = yc + hc; - pnts[7].x = xc + wc; - pnts[7].y = yc + hc; - pnts[8].x = xc + wc; - pnts[8].y = y + h; - - pnts[9].x = xc; - pnts[9].y = y + h; - pnts[10].x = xc; - pnts[10].y = yc + hc; - pnts[11].x = x; - pnts[11].y = yc + hc; - - XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 12, Convex, CoordModeOrigin); - XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, y, - (unsigned)r*2, (unsigned)r*2, 0 * 64, 90 * 64); - XFillArc(surf->dpy, surf->drawable, surf->gc, x, y, - (unsigned)r*2, (unsigned)r*2, 90 * 64, 90 * 64); - XFillArc(surf->dpy, surf->drawable, surf->gc, x, yc + hc - r, - (unsigned)r*2, (unsigned)2*r, 180 * 64, 90 * 64); - XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, yc + hc - r, - (unsigned)r*2, (unsigned)2*r, -90 * 64, 90 * 64); - } -} - -static void -surface_fill_triangle(XSurface *surf, short x0, short y0, short x1, - short y1, short x2, short y2, struct zr_color col) -{ - XPoint pnts[3]; - unsigned long c = color_from_byte(&col.r); - pnts[0].x = (short)x0; - pnts[0].y = (short)y0; - pnts[1].x = (short)x1; - pnts[1].y = (short)y1; - pnts[2].x = (short)x2; - pnts[2].y = (short)y2; - XSetForeground(surf->dpy, surf->gc, c); - XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 3, Convex, CoordModeOrigin); -} - -static void -surface_stroke_triangle(XSurface *surf, short x0, short y0, short x1, - short y1, short x2, short y2, unsigned short line_thickness, struct zr_color col) -{ - XPoint pnts[3]; - unsigned long c = color_from_byte(&col.r); - XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); - XDrawLine(surf->dpy, surf->drawable, surf->gc, x0, y0, x1, y1); - XDrawLine(surf->dpy, surf->drawable, surf->gc, x1, y1, x2, y2); - XDrawLine(surf->dpy, surf->drawable, surf->gc, x2, y2, x0, y0); - XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); -} - -static void -surface_fill_polygon(XSurface *surf, const struct zr_vec2i *pnts, int count, - struct zr_color col) -{ - int i = 0; - #define MAX_POINTS 64 - XPoint xpnts[MAX_POINTS]; - unsigned long c = color_from_byte(&col.r); - XSetForeground(surf->dpy, surf->gc, c); - for (i = 0; i < count && i < MAX_POINTS; ++i) { - xpnts[i].x = pnts[i].x; - xpnts[i].y = pnts[i].y; - } - XFillPolygon(surf->dpy, surf->drawable, surf->gc, xpnts, count, Convex, CoordModeOrigin); - #undef MAX_POINTS -} - -static void -surface_stroke_polygon(XSurface *surf, const struct zr_vec2i *pnts, int count, - unsigned short line_thickness, struct zr_color col) -{ - int i = 0; - unsigned long c = color_from_byte(&col.r); - XSetForeground(surf->dpy, surf->gc, c); - XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); - for (i = 1; i < count; ++i) - XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[i-1].x, pnts[i-1].y, pnts[i].x, pnts[i].y); - XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[count-1].x, pnts[count-1].y, pnts[0].x, pnts[0].y); - XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); -} - -static void -surface_stroke_polyline(XSurface *surf, const struct zr_vec2i *pnts, - int count, unsigned short line_thickness, struct zr_color col) -{ - int i = 0; - unsigned long c = color_from_byte(&col.r); - XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); - XSetForeground(surf->dpy, surf->gc, c); - for (i = 0; i < count-1; ++i) - XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[i].x, pnts[i].y, pnts[i+1].x, pnts[i+1].y); - XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); -} - -static void -surface_fill_circle(XSurface *surf, short x, short y, unsigned short w, - unsigned short h, struct zr_color col) -{ - unsigned long c = color_from_byte(&col.r); - XSetForeground(surf->dpy, surf->gc, c); - XFillArc(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, - (unsigned)w, (unsigned)h, 0, 360 * 64); -} - -static void -surface_stroke_circle(XSurface *surf, short x, short y, unsigned short w, - unsigned short h, unsigned short line_thickness, struct zr_color col) -{ - unsigned long c = color_from_byte(&col.r); - XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); - XSetForeground(surf->dpy, surf->gc, c); - XDrawArc(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, - (unsigned)w, (unsigned)h, 0, 360 * 64); - XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); -} - -static void -surface_stroke_curve(XSurface *surf, struct zr_vec2i p1, - struct zr_vec2i p2, struct zr_vec2i p3, struct zr_vec2i p4, - unsigned int num_segments, unsigned short line_thickness, struct zr_color col) -{ - unsigned int i_step; - float t_step; - struct zr_vec2i last = p1; - - XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); - num_segments = MAX(num_segments, 1); - t_step = 1.0f/(float)num_segments; - for (i_step = 1; i_step <= num_segments; ++i_step) { - float t = t_step * (float)i_step; - float u = 1.0f - t; - float w1 = u*u*u; - float w2 = 3*u*u*t; - float w3 = 3*u*t*t; - float w4 = t * t *t; - float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; - float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; - surface_stroke_line(surf, last.x, last.y, (short)x, (short)y, line_thickness,col); - last.x = (short)x; last.y = (short)y; - } - XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); -} - -static void -surface_draw_text(XSurface *surf, short x, short y, unsigned short w, unsigned short h, - const char *text, size_t len, XFont *font, struct zr_color cbg, struct zr_color cfg) -{ - int tx, ty, th; - unsigned long bg = color_from_byte(&cbg.r); - unsigned long fg = color_from_byte(&cfg.r); - - XSetForeground(surf->dpy, surf->gc, bg); - XFillRectangle(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, (unsigned)w, (unsigned)h); - if(!text || !font || !len) return; - - tx = (int)x; - th = font->ascent + font->descent; - ty = (int)y + font->ascent; - XSetForeground(surf->dpy, surf->gc, fg); - if(font->set) - XmbDrawString(surf->dpy,surf->drawable,font->set,surf->gc,tx,ty,(const char*)text,(int)len); - else - XDrawString(surf->dpy, surf->drawable, surf->gc, tx, ty, (const char*)text, (int)len); -} - -static void -surface_clear(XSurface *surf, unsigned long color) -{ - XSetForeground(surf->dpy, surf->gc, color); - XFillRectangle(surf->dpy, surf->drawable, surf->gc, 0, 0, surf->w, surf->h); -} - -static void -surface_blit(XSurface *dst_surf, XSurface *src_surf, - int dst_x, int dst_y, int dst_w, int dst_h, - int src_x, int src_y, int src_w, int src_h) -{ - GC gc; - XImage *dst, *src; - XGCValues gcvalues; - int x = 0, y = 0; - - /*@optimize: there is probably a more performant way to do this since this is slow as FUCK */ - src = XGetImage(src_surf->dpy, src_surf->drawable, 0, 0, src_surf->w, src_surf->h, AllPlanes, ZPixmap); - dst = XGetImage(dst_surf->dpy, dst_surf->drawable, 0, 0, dst_surf->w, dst_surf->h, AllPlanes, ZPixmap); - for (y = 0; y < dst_h; ++y) { - int dst_off_y = dst_y + y; - int src_off_y = src_y + (int)((float)y * (float)src_h/(float)dst_h); - for (x = 0; x < dst_w; ++x) { - unsigned long dpx, spx, pixel; - struct zr_color dst_col_b, src_col_b, res; - float dst_col[4], src_col[4], res_col[4]; - int dst_off_x = dst_x + x; - int src_off_x = src_x + (int)((float)x * (float)src_w/(float)dst_w); - - /* acquire both source and destination pixel */ - spx = XGetPixel(src, src_off_x, src_off_y); - dpx = XGetPixel(dst, dst_off_x, dst_off_y); - - /* convert from 32-bit integer to byte */ - color_to_byte(&dst_col_b.r, dpx); - color_to_byte(&src_col_b.r, spx); - - /* convert from byte to float */ - zr_color_f(&dst_col[0], &dst_col[1], &dst_col[2], &dst_col[3], dst_col_b); - zr_color_f(&src_col[0], &src_col[1], &src_col[2], &src_col[3], src_col_b); - - /* perform simple alpha-blending */ - res_col[0] = (1.0f - src_col[3]) * dst_col[0] + src_col[3] * src_col[0]; - res_col[1] = (1.0f - src_col[3]) * dst_col[1] + src_col[3] * src_col[1]; - res_col[2] = (1.0f - src_col[3]) * dst_col[2] + src_col[3] * src_col[2]; - res_col[3] = 255; - - /* convert from float to byte */ - res = zr_rgb_f(res_col[0], res_col[1], res_col[2]); - pixel = color_from_byte(&res.r); - - /* finally write pixel to surface */ - XPutPixel(dst, dst_off_x, dst_off_y, pixel); - } - } - gc = XCreateGC(dst_surf->dpy, dst_surf->drawable, 0, &gcvalues); - XPutImage(dst_surf->dpy, dst_surf->drawable, gc, dst, 0, 0, 0, 0, - (unsigned int)dst_surf->w, (unsigned int)dst_surf->h); - XDestroyImage(src); - XDestroyImage(dst); -} - -static void -surface_blit_to_screen(Drawable target, XSurface *surf, unsigned int width, unsigned int height) -{ - XCopyArea(surf->dpy, surf->drawable, target, surf->gc, 0, 0, width, height, 0, 0); -} - -static void -surface_del(XSurface *surf) -{ - XFreePixmap(surf->dpy, surf->drawable); - XFreeGC(surf->dpy, surf->gc); - free(surf); -} - -static void -input_key(struct XWindow *xw, struct zr_context *ctx, XEvent *evt, int down) -{ - int ret; - KeySym *code = XGetKeyboardMapping(xw->dpy, (KeyCode)evt->xkey.keycode, 1, &ret); - if (*code == XK_Shift_L || *code == XK_Shift_R) - zr_input_key(ctx, ZR_KEY_SHIFT, down); - else if (*code == XK_Delete) - zr_input_key(ctx, ZR_KEY_DEL, down); - else if (*code == XK_Return) - zr_input_key(ctx, ZR_KEY_ENTER, down); - else if (*code == XK_Tab) { - zr_input_key(ctx, ZR_KEY_TAB, down); - } else if (*code == XK_space && !down) - zr_input_char(ctx, ' '); - else if (*code == XK_Left) - zr_input_key(ctx, ZR_KEY_LEFT, down); - else if (*code == XK_Right) - zr_input_key(ctx, ZR_KEY_RIGHT, down); - else if (*code == XK_BackSpace) - zr_input_key(ctx, ZR_KEY_BACKSPACE, down); - else { - if (*code == 'c' && (evt->xkey.state & ControlMask)) - zr_input_key(ctx, ZR_KEY_COPY, down); - else if (*code == 'v' && (evt->xkey.state & ControlMask)) - zr_input_key(ctx, ZR_KEY_PASTE, down); - else if (*code == 'x' && (evt->xkey.state & ControlMask)) - zr_input_key(ctx, ZR_KEY_CUT, down); - else if (!down) { - KeySym keysym = 0; - char buf[32]; - XLookupString((XKeyEvent*)evt, buf, 32, &keysym, NULL); - zr_input_glyph(ctx, buf); - } - } - XFree(code); -} - -static void -input_motion(struct zr_context *ctx, XEvent *evt) -{ - const int x = evt->xmotion.x; - const int y = evt->xmotion.y; - zr_input_motion(ctx, x, y); -} - -static void -input_button(struct zr_context *ctx, XEvent *evt, int down) -{ - const int x = evt->xbutton.x; - const int y = evt->xbutton.y; - if (evt->xbutton.button == Button1) - zr_input_button(ctx, ZR_BUTTON_LEFT, x, y, down); - if (evt->xbutton.button == Button2) - zr_input_button(ctx, ZR_BUTTON_MIDDLE, x, y, down); - else if (evt->xbutton.button == Button3) - zr_input_button(ctx, ZR_BUTTON_RIGHT, x, y, down); - else if (evt->xbutton.button == Button4) - zr_input_scroll(ctx, 1.0f); - else if (evt->xbutton.button == Button5) - zr_input_scroll(ctx, -1.0f); -} - -static void -resize(struct XWindow *xw, XSurface *surf) -{ - XGetWindowAttributes(xw->dpy, xw->win, &xw->attr); - xw->width = (unsigned int)xw->attr.width; - xw->height = (unsigned int)xw->attr.height; - surface_resize(surf, xw->width, xw->height); -} - -int -main(int argc, char *argv[]) -{ - long dt; - long started; - XWindow xw; - struct demo gui; - struct zr_user_font font; - int running = 1; - - /* X11 */ - UNUSED(argc); UNUSED(argv); - memset(&xw, 0, sizeof xw); - if (setlocale(LC_ALL, "") == NULL) return 9; - if (!XSupportsLocale()) return 10; - if (!XSetLocaleModifiers("@im=none")) return 11; - - xw.dpy = XOpenDisplay(NULL); - xw.root = DefaultRootWindow(xw.dpy); - xw.screen = XDefaultScreen(xw.dpy); - xw.vis = XDefaultVisual(xw.dpy, xw.screen); - xw.cmap = XCreateColormap(xw.dpy,xw.root,xw.vis,AllocNone); - xw.swa.colormap = xw.cmap; - xw.swa.event_mask = - ExposureMask | KeyPressMask | KeyReleaseMask | - ButtonPress | ButtonReleaseMask| ButtonMotionMask | - Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| - PointerMotionMask | KeymapStateMask; - xw.win = XCreateWindow(xw.dpy, xw.root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, - XDefaultDepth(xw.dpy, xw.screen), InputOutput, - xw.vis, CWEventMask | CWColormap, &xw.swa); - XStoreName(xw.dpy, xw.win, "X11"); - XMapWindow(xw.dpy, xw.win); - - XGetWindowAttributes(xw.dpy, xw.win, &xw.attr); - xw.width = (unsigned int)xw.attr.width; - xw.height = (unsigned int)xw.attr.height; - xw.surf = surface_create(xw.dpy, xw.screen, xw.win, xw.width, xw.height); - xw.font = font_create(xw.dpy, "fixed"); - - /* GUI */ - font.userdata = zr_handle_ptr(xw.font); - font.height = (float)xw.font->height; - font.width = font_get_text_width; - memset(&gui, 0, sizeof gui); - gui.memory = calloc(MAX_MEMORY, 1); - zr_init_fixed(&gui.ctx, gui.memory, MAX_MEMORY, &font); - - while (running) { - /* Input */ - XEvent evt; - started = timestamp(); - zr_input_begin(&gui.ctx); - while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &evt)) { - if (XFilterEvent(&evt, xw.win)) continue; - if (evt.type == KeyPress) - input_key(&xw, &gui.ctx, &evt, zr_true); - else if (evt.type == KeyRelease) - input_key(&xw, &gui.ctx, &evt, zr_false); - else if (evt.type == ButtonPress) - input_button(&gui.ctx, &evt, zr_true); - else if (evt.type == ButtonRelease) - input_button(&gui.ctx, &evt, zr_false); - else if (evt.type == MotionNotify) - input_motion(&gui.ctx, &evt); - else if (evt.type == Expose || evt.type == ConfigureNotify) - resize(&xw, xw.surf); - else if (evt.type == KeymapNotify) - XRefreshKeyboardMapping(&evt.xmapping); - } - zr_input_end(&gui.ctx); - - /* GUI */ - running = run_demo(&gui); - - /* Draw */ - XClearWindow(xw.dpy, xw.win); - surface_clear(xw.surf, 0x00303030); - { - const struct zr_command *cmd; - zr_foreach(cmd, &gui.ctx) { - switch (cmd->type) { - case ZR_COMMAND_NOP: break; - case ZR_COMMAND_SCISSOR: { - const struct zr_command_scissor *s = zr_command(scissor, cmd); - surface_scissor(xw.surf, s->x, s->y, s->w, s->h); - } break; - case ZR_COMMAND_LINE: { - const struct zr_command_line *l = zr_command(line, cmd); - surface_stroke_line(xw.surf, l->begin.x, l->begin.y, l->end.x, - l->end.y, l->line_thickness, l->color); - } break; - case ZR_COMMAND_RECT: { - const struct zr_command_rect *r = zr_command(rect, cmd); - surface_stroke_rect(xw.surf, r->x, r->y, r->w, r->h, - (uint16_t)r->rounding, r->line_thickness, r->color); - } break; - case ZR_COMMAND_RECT_FILLED: { - const struct zr_command_rect_filled *r = zr_command(rect_filled, cmd); - surface_fill_rect(xw.surf, r->x, r->y, r->w, r->h, - (uint16_t)r->rounding, r->color); - } break; - case ZR_COMMAND_CIRCLE: { - const struct zr_command_circle *c = zr_command(circle, cmd); - surface_stroke_circle(xw.surf, c->x, c->y, c->w, c->h, c->line_thickness, c->color); - } break; - case ZR_COMMAND_CIRCLE_FILLED: { - const struct zr_command_circle_filled *c = zr_command(circle_filled, cmd); - surface_fill_circle(xw.surf, c->x, c->y, c->w, c->h, c->color); - } break; - case ZR_COMMAND_TRIANGLE: { - const struct zr_command_triangle*t = zr_command(triangle, cmd); - surface_stroke_triangle(xw.surf, t->a.x, t->a.y, t->b.x, t->b.y, - t->c.x, t->c.y, t->line_thickness, t->color); - } break; - case ZR_COMMAND_TRIANGLE_FILLED: { - const struct zr_command_triangle_filled *t = zr_command(triangle_filled, cmd); - surface_fill_triangle(xw.surf, t->a.x, t->a.y, t->b.x, t->b.y, - t->c.x, t->c.y, t->color); - } break; - case ZR_COMMAND_POLYGON: { - const struct zr_command_polygon *p = zr_command(polygon, cmd); - surface_stroke_polygon(xw.surf, p->points, p->point_count, p->line_thickness,p->color); - } break; - case ZR_COMMAND_POLYGON_FILLED: { - const struct zr_command_polygon_filled *p = zr_command(polygon_filled, cmd); - surface_fill_polygon(xw.surf, p->points, p->point_count, p->color); - } break; - case ZR_COMMAND_POLYLINE: { - const struct zr_command_polyline *p = zr_command(polyline, cmd); - surface_stroke_polyline(xw.surf, p->points, p->point_count, p->line_thickness, p->color); - } break; - case ZR_COMMAND_TEXT: { - const struct zr_command_text *t = zr_command(text, cmd); - surface_draw_text(xw.surf, t->x, t->y, t->w, t->h, - (const char*)t->string, t->length, - (XFont*)t->font->userdata.ptr, - t->background, t->foreground); - } break; - case ZR_COMMAND_CURVE: { - const struct zr_command_curve *q = zr_command(curve, cmd); - surface_stroke_curve(xw.surf, q->begin, q->ctrl[0], q->ctrl[1], - q->end, 22, q->line_thickness, q->color); - } break; - case ZR_COMMAND_RECT_MULTI_COLOR: - case ZR_COMMAND_IMAGE: - case ZR_COMMAND_ARC: - case ZR_COMMAND_ARC_FILLED: - default: break; - } - } - zr_clear(&gui.ctx); - } - surface_blit_to_screen(xw.win, xw.surf, xw.width, xw.height); - XFlush(xw.dpy); - - /* Timing */ - dt = timestamp() - started; - if (dt < DTIME) - sleep_for(DTIME - dt); - } - free(gui.memory); - font_del(xw.dpy, xw.font); - surface_del(xw.surf); - XUnmapWindow(xw.dpy, xw.win); - XFreeColormap(xw.dpy, xw.cmap); - XDestroyWindow(xw.dpy, xw.win); - XCloseDisplay(xw.dpy); - return 0; -} - diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..01f7285 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,34 @@ +# Compiler +CC = clang + +# Flags +CFLAGS = -std=c99 -pedantic -O2 + +ifeq ($(OS),Windows_NT) +BIN := $(BIN).exe +LIBS = -lglfw3 -lopengl32 -lm -lGLU32 -lGLEW32 +else +LIBS = -lglfw -lGL -lm -lGLU -lGLEW +endif + +all: node_editor file_browser overview extended +node_editor: + @mkdir -p bin + rm -f bin/node_editor $(OBJS) + $(CC) $(CFLAGS) -o bin/node_editor node_editor.c $(LIBS) + +file_browser: + @mkdir -p bin + rm -f bin/file_browser $(OBJS) + $(CC) $(CFLAGS) -o bin/file_browser file_browser.c $(LIBS) + +overview: + @mkdir -p bin + rm -f bin/overview $(OBJS) + $(CC) $(CFLAGS) -o bin/overview overview.c $(LIBS) + +extended: + @mkdir -p bin + rm -f bin/extended $(OBJS) + $(CC) $(CFLAGS) -o bin/extended extended.c $(LIBS) + diff --git a/example/extended.c b/example/extended.c new file mode 100644 index 0000000..3304464 --- /dev/null +++ b/example/extended.c @@ -0,0 +1,882 @@ +/* nuklear - v1.00 - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +/* macros */ +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +struct icons { + struct nk_image unchecked; + struct nk_image checked; + struct nk_image rocket; + struct nk_image cloud; + struct nk_image pen; + struct nk_image play; + struct nk_image pause; + struct nk_image stop; + struct nk_image prev; + struct nk_image next; + struct nk_image tools; + struct nk_image dir; + struct nk_image copy; + struct nk_image convert; + struct nk_image del; + struct nk_image edit; + struct nk_image images[9]; + struct nk_image menu[6]; +}; + +/* =============================================================== + * + * CUSTOM WIDGET + * + * ===============================================================*/ +static int +ui_piemenu(struct nk_context *ctx, struct nk_vec2 pos, float radius, + struct nk_image *icons, int item_count) +{ + int ret = -1; + struct nk_rect total_space; + struct nk_panel popup; + struct nk_rect bounds; + int active_item = 0; + + /* pie menu popup */ + struct nk_color border = ctx->style.window.border_color; + struct nk_style_item background = ctx->style.window.fixed_background; + ctx->style.window.fixed_background = nk_style_item_hide(); + ctx->style.window.border_color = nk_rgba(0,0,0,0); + + total_space = nk_window_get_content_region(ctx); + nk_popup_begin(ctx, &popup, NK_POPUP_STATIC, "piemenu", NK_WINDOW_NO_SCROLLBAR, + nk_rect(pos.x - total_space.x - radius, pos.y - radius - total_space.y, + 2*radius,2*radius)); + + total_space = nk_window_get_content_region(ctx); + nk_layout_row_dynamic(ctx, total_space.h, 1); + { + int i = 0; + struct nk_command_buffer* out = nk_window_get_canvas(ctx); + const struct nk_input *in = &ctx->input; + { + /* allocate complete popup space for the menu */ + enum nk_widget_layout_states state; + total_space = nk_window_get_content_region(ctx); + total_space.x = total_space.y = 0; + state = nk_widget(&bounds, ctx); + } + + /* outer circle */ + nk_fill_circle(out, bounds, nk_rgb(50,50,50)); + { + /* circle buttons */ + float step = (2 * 3.141592654f) / (float)(MAX(1,item_count)); + float a_min = 0; float a_max = step; + + struct nk_vec2 center = nk_vec2(bounds.x + bounds.w / 2.0f, bounds.y + bounds.h / 2.0f); + struct nk_vec2 drag = nk_vec2(in->mouse.pos.x - center.x, in->mouse.pos.y - center.y); + float angle = (float)atan2(drag.y, drag.x); + if (angle < -0.0f) angle += 2.0f * 3.141592654f; + active_item = (int)(angle/step); + + for (i = 0; i < item_count; ++i) { + struct nk_rect content; + float rx, ry, dx, dy, a; + nk_fill_arc(out, center.x, center.y, (bounds.w/2.0f), + a_min, a_max, (active_item == i) ? nk_rgb(45,100,255): nk_rgb(60,60,60)); + + /* seperator line */ + rx = bounds.w/2.0f; ry = 0; + dx = rx * (float)cos(a_min) - ry * (float)sin(a_min); + dy = rx * (float)sin(a_min) + ry * (float)cos(a_min); + nk_stroke_line(out, center.x, center.y, + center.x + dx, center.y + dy, 1.0f, nk_rgb(50,50,50)); + + /* button content */ + a = a_min + (a_max - a_min)/2.0f; + rx = bounds.w/2.5f; ry = 0; + content.w = 30; content.h = 30; + content.x = center.x + ((rx * (float)cos(a) - ry * (float)sin(a)) - content.w/2.0f); + content.y = center.y + (rx * (float)sin(a) + ry * (float)cos(a) - content.h/2.0f); + nk_draw_image(out, content, &icons[i]); + a_min = a_max; a_max += step; + } + } + { + /* inner circle */ + struct nk_rect inner; + inner.x = bounds.x + bounds.w/2 - bounds.w/4; + inner.y = bounds.y + bounds.h/2 - bounds.h/4; + inner.w = bounds.w/2; inner.h = bounds.h/2; + nk_fill_circle(out, inner, nk_rgb(45,45,45)); + + /* active icon content */ + bounds.w = inner.w / 2.0f; + bounds.h = inner.h / 2.0f; + bounds.x = inner.x + inner.w/2 - bounds.w/2; + bounds.y = inner.y + inner.h/2 - bounds.h/2; + nk_draw_image(out, bounds, &icons[active_item]); + } + } + nk_layout_space_end(ctx); + nk_popup_end(ctx); + + ctx->style.window.fixed_background = background; + ctx->style.window.border_color = border; + + if (!nk_input_is_mouse_down(&ctx->input, NK_BUTTON_RIGHT)) + return active_item; + else return ret; +} + +/* =============================================================== + * + * GRID + * + * ===============================================================*/ +static void +grid_demo(struct nk_context *ctx) +{ + static char text[3][64]; + static int text_len[3]; + static const char *items[] = {"Item 0","item 1","item 2"}; + static int selected_item = 0; + static int check = 1; + struct nk_panel layout; + + int i; + struct nk_panel combo; + ctx->style.font.height = 20; + if (nk_begin(ctx, &layout, "Grid Demo", nk_rect(600, 350, 275, 250), + NK_WINDOW_TITLE|NK_WINDOW_BORDER|NK_WINDOW_MOVABLE| + NK_WINDOW_BORDER_HEADER|NK_WINDOW_NO_SCROLLBAR)) + { + ctx->style.font.height = 18; + nk_layout_row_dynamic(ctx, 30, 2); + nk_label(ctx, "Floating point:", NK_TEXT_RIGHT); + nk_edit_string(ctx, NK_EDIT_FIELD, text[0], &text_len[0], 64, nk_filter_float); + nk_label(ctx, "Hexadeximal:", NK_TEXT_RIGHT); + nk_edit_string(ctx, NK_EDIT_FIELD, text[1], &text_len[1], 64, nk_filter_hex); + nk_label(ctx, "Binary:", NK_TEXT_RIGHT); + nk_edit_string(ctx, NK_EDIT_FIELD, text[2], &text_len[2], 64, nk_filter_binary); + nk_label(ctx, "Checkbox:", NK_TEXT_RIGHT); + nk_checkbox_label(ctx, "Check me", &check); + nk_label(ctx, "Combobox:", NK_TEXT_RIGHT); + + if (nk_combo_begin_label(ctx, &combo, items[selected_item], 200)) { + nk_layout_row_dynamic(ctx, 30, 1); + for (i = 0; i < 3; ++i) + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected_item = i; + nk_combo_end(ctx); + } + } + nk_end(ctx); + ctx->style.font.height = 14; +} + +/* =============================================================== + * + * BUTTON DEMO + * + * ===============================================================*/ +static void +ui_header(struct nk_context *ctx, const char *title) +{ + ctx->style.font.height = 18; + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, title, NK_TEXT_LEFT); +} + +static void +ui_widget(struct nk_context *ctx, float height, float font_height) +{ + static const float ratio[] = {0.15f, 0.85f}; + ctx->style.font.height = font_height; + nk_layout_row(ctx, NK_DYNAMIC, height, 2, ratio); + nk_spacing(ctx, 1); +} + +static void +ui_widget_centered(struct nk_context *ctx, float height, float font_height) +{ + static const float ratio[] = {0.15f, 0.50f, 0.35f}; + ctx->style.font.height = font_height; + nk_layout_row(ctx, NK_DYNAMIC, height, 3, ratio); + nk_spacing(ctx, 1); +} + +static void +button_demo(struct nk_context *ctx, struct icons *img) +{ + struct nk_panel layout; + struct nk_panel menu; + static int option = 1; + static int toggle0 = 1; + static int toggle1 = 0; + static int toggle2 = 1; + + ctx->style.font.height = 20; + nk_begin(ctx, &layout, "Button Demo", nk_rect(50,50,255,610), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_BORDER_HEADER|NK_WINDOW_TITLE); + + /*------------------------------------------------ + * MENU + *------------------------------------------------*/ + nk_menubar_begin(ctx); + { + /* toolbar */ + nk_layout_row_static(ctx, 40, 40, 4); + if (nk_menu_begin_image(ctx, &menu, "Music", img->play, 120)) + { + /* settings */ + nk_layout_row_dynamic(ctx, 25, 1); + nk_menu_item_image_label(ctx, img->play, "Play", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, img->stop, "Stop", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, img->pause, "Pause", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, img->next, "Next", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, img->prev, "Prev", NK_TEXT_RIGHT); + nk_menu_end(ctx); + } + nk_button_image(ctx, img->tools, NK_BUTTON_DEFAULT); + nk_button_image(ctx, img->cloud, NK_BUTTON_DEFAULT); + nk_button_image(ctx, img->pen, NK_BUTTON_DEFAULT); + } + nk_menubar_end(ctx); + + /*------------------------------------------------ + * BUTTON + *------------------------------------------------*/ + ui_header(ctx, "Push buttons"); + ui_widget(ctx, 35, 22); + if (nk_button_label(ctx, "Push me", NK_BUTTON_DEFAULT)) + fprintf(stdout, "pushed!\n"); + ui_widget(ctx, 35, 22); + if (nk_button_image_label(ctx, img->rocket, "Styled", NK_TEXT_CENTERED, NK_BUTTON_DEFAULT)) + fprintf(stdout, "rocket!\n"); + + /*------------------------------------------------ + * REPEATER + *------------------------------------------------*/ + ui_header(ctx, "Repeater"); + ui_widget(ctx, 35, 22); + if (nk_button_label(ctx, "Press me", NK_BUTTON_REPEATER)) + fprintf(stdout, "pressed!\n"); + + /*------------------------------------------------ + * TOGGLE + *------------------------------------------------*/ + ui_header(ctx, "Toggle buttons"); + ui_widget(ctx, 35, 22); + if (nk_button_image_label(ctx, (toggle0) ? img->checked: img->unchecked, + "Toggle", NK_TEXT_LEFT, NK_BUTTON_DEFAULT)) toggle0 = !toggle0; + + ui_widget(ctx, 35, 22); + if (nk_button_image_label(ctx, (toggle1) ? img->checked: img->unchecked, + "Toggle", NK_TEXT_LEFT, NK_BUTTON_DEFAULT)) toggle1 = !toggle1; + + ui_widget(ctx, 35, 22); + if (nk_button_image_label(ctx, (toggle2) ? img->checked: img->unchecked, + "Toggle", NK_TEXT_LEFT, NK_BUTTON_DEFAULT)) toggle2 = !toggle2; + + /*------------------------------------------------ + * RADIO + *------------------------------------------------*/ + ui_header(ctx, "Radio buttons"); + ui_widget(ctx, 35, 22); + if (nk_button_symbol_label(ctx, (option == 0)?NK_SYMBOL_CIRCLE_FILLED:NK_SYMBOL_CIRCLE, + "Select", NK_TEXT_LEFT, NK_BUTTON_DEFAULT)) option = 0; + ui_widget(ctx, 35, 22); + if (nk_button_symbol_label(ctx, (option == 1)?NK_SYMBOL_CIRCLE_FILLED:NK_SYMBOL_CIRCLE, + "Select", NK_TEXT_LEFT, NK_BUTTON_DEFAULT)) option = 1; + ui_widget(ctx, 35, 22); + if (nk_button_symbol_label(ctx, (option == 2)?NK_SYMBOL_CIRCLE_FILLED:NK_SYMBOL_CIRCLE, + "Select", NK_TEXT_LEFT, NK_BUTTON_DEFAULT)) option = 2; + + /*------------------------------------------------ + * CONTEXTUAL + *------------------------------------------------*/ + if (nk_contextual_begin(ctx, &menu, NK_WINDOW_NO_SCROLLBAR, nk_vec2(120, 200), nk_window_get_bounds(ctx))) { + ctx->style.font.height = 18; + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_contextual_item_image_label(ctx, img->copy, "Clone", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed clone!\n"); + if (nk_contextual_item_image_label(ctx, img->del, "Delete", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed delete!\n"); + if (nk_contextual_item_image_label(ctx, img->convert, "Convert", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed convert!\n"); + if (nk_contextual_item_image_label(ctx, img->edit, "Edit", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed edit!\n"); + nk_contextual_end(ctx); + } + ctx->style.font.height = 14; + nk_end(ctx); +} + +/* =============================================================== + * + * BASIC DEMO + * + * ===============================================================*/ +static void +basic_demo(struct nk_context *ctx, struct icons *img) +{ + static int image_active; + static int check0 = 1; + static int check1 = 0; + static size_t prog = 80; + static int selected_item = 0; + static int selected_image = 3; + static int selected_icon = 0; + static const char *items[] = {"Item 0","item 1","item 2"}; + static int piemenu_active = 0; + static struct nk_vec2 piemenu_pos; + + int i = 0; + struct nk_panel layout; + struct nk_panel combo; + ctx->style.font.height = 20; + nk_begin(ctx, &layout, "Basic Demo", nk_rect(320, 50, 275, 610), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_BORDER_HEADER|NK_WINDOW_TITLE); + + /*------------------------------------------------ + * POPUP BUTTON + *------------------------------------------------*/ + ui_header(ctx, "Popup & Scrollbar & Images"); + ui_widget(ctx, 35, 22); + if (nk_button_image_label(ctx, img->dir, + "Images", NK_TEXT_CENTERED, NK_BUTTON_DEFAULT)) + image_active = !image_active; + + /*------------------------------------------------ + * SELECTED IMAGE + *------------------------------------------------*/ + ui_header(ctx, "Selected Image"); + ui_widget_centered(ctx, 100, 22); + nk_image(ctx, img->images[selected_image]); + + /*------------------------------------------------ + * IMAGE POPUP + *------------------------------------------------*/ + if (image_active) { + struct nk_panel popup; + if (nk_popup_begin(ctx, &popup, NK_POPUP_STATIC, "Image Popup", 0, nk_rect(265, 0, 320, 220))) { + nk_layout_row_static(ctx, 82, 82, 3); + for (i = 0; i < 9; ++i) { + if (nk_button_image(ctx, img->images[i], NK_BUTTON_DEFAULT)) { + selected_image = i; + image_active = 0; + nk_popup_close(ctx); + } + } + nk_popup_end(ctx); + } + } + /*------------------------------------------------ + * COMBOBOX + *------------------------------------------------*/ + ui_header(ctx, "Combo box"); + ui_widget(ctx, 40, 22); + if (nk_combo_begin_label(ctx, &combo, items[selected_item], 200)) { + nk_layout_row_dynamic(ctx, 35, 1); + for (i = 0; i < 3; ++i) + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected_item = i; + nk_combo_end(ctx); + } + + ui_widget(ctx, 40, 22); + if (nk_combo_begin_image_label(ctx, &combo, items[selected_icon], img->images[selected_icon], 200)) { + nk_layout_row_dynamic(ctx, 35, 1); + for (i = 0; i < 3; ++i) + if (nk_combo_item_image_label(ctx, img->images[i], items[i], NK_TEXT_RIGHT)) + selected_icon = i; + nk_combo_end(ctx); + } + + /*------------------------------------------------ + * CHECKBOX + *------------------------------------------------*/ + ui_header(ctx, "Checkbox"); + ui_widget(ctx, 30, 22); + nk_checkbox_label(ctx, "Flag 1", &check0); + ui_widget(ctx, 30, 22); + nk_checkbox_label(ctx, "Flag 2", &check1); + + /*------------------------------------------------ + * PROGRESSBAR + *------------------------------------------------*/ + ui_header(ctx, "Progressbar"); + ui_widget(ctx, 35, 22); + nk_progress(ctx, &prog, 100, nk_true); + + /*------------------------------------------------ + * PIEMENU + *------------------------------------------------*/ + if (nk_input_is_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_RIGHT, + layout.bounds,nk_true)){ + piemenu_pos = ctx->input.mouse.pos; + piemenu_active = 1; + } + + if (piemenu_active) { + int ret = ui_piemenu(ctx, piemenu_pos, 140, &img->menu[0], 6); + if (ret != -1) { + fprintf(stdout, "piemenu selected: %d\n", ret); + piemenu_active = 0; + } + } + ctx->style.font.height = 14; + nk_end(ctx); +} + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct 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; +}; + +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 struct nk_image +icon_load(const char *filename) +{ + int x,y,n; + GLuint tex; + unsigned char *data = stbi_load(filename, &x, &y, &n, 0); + if (!data) die("[SDL]: failed to load image: %s", filename); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(data); + return nk_image_id((int)tex); +} + + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + "#version 300 es\n" + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "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 = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + 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_draw_vertex); + size_t vp = offsetof(struct nk_draw_vertex, position); + size_t vt = offsetof(struct nk_draw_vertex, uv); + size_t vc = offsetof(struct nk_draw_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); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + 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); +} + +static void +device_shutdown(struct device *dev) +{ + 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); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + enum nk_anti_aliasing AA) +{ + GLint last_prog, last_tex; + GLint last_ebo, last_vbo, last_vao; + 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)width; + ortho[1][1] /= (GLfloat)height; + + /* save previous opengl state */ + glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); + + /* 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]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + 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_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill converting configuration */ + struct nk_convert_config config; + memset(&config, 0, sizeof(config)); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.null = dev->null; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor((GLint)cmd->clip_rect.x, + height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), + (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* restore old state */ + glUseProgram((GLuint)last_prog); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); + glBindVertexArray((GLuint)last_vao); + glDisable(GL_SCISSOR_TEST); +} + + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + + /* GUI */ + struct device device; + struct nk_font *font; + struct nk_font_atlas atlas; + struct icons icons; + struct nk_context ctx; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + {/* GUI */ + device_init(&device); + {const void *image; int w, h; + const char *font_path = (argc > 1) ? argv[1]: 0; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); + else font = nk_font_atlas_add_default(&atlas, 14.0f, NULL); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);} + nk_init_default(&ctx, &font->handle);} + + /* icons */ + glEnable(GL_TEXTURE_2D); + icons.unchecked = icon_load("../icon/unchecked.png"); + icons.checked = icon_load("../icon/checked.png"); + icons.rocket = icon_load("../icon/rocket.png"); + icons.cloud = icon_load("../icon/cloud.png"); + icons.pen = icon_load("../icon/pen.png"); + icons.play = icon_load("../icon/play.png"); + icons.pause = icon_load("../icon/pause.png"); + icons.stop = icon_load("../icon/stop.png"); + icons.next = icon_load("../icon/next.png"); + icons.prev = icon_load("../icon/prev.png"); + icons.tools = icon_load("../icon/tools.png"); + icons.dir = icon_load("../icon/directory.png"); + icons.copy = icon_load("../icon/copy.png"); + icons.convert = icon_load("../icon/export.png"); + icons.del = icon_load("../icon/delete.png"); + icons.edit = icon_load("../icon/edit.png"); + icons.menu[0] = icon_load("../icon/home.png"); + icons.menu[1] = icon_load("../icon/phone.png"); + icons.menu[2] = icon_load("../icon/plane.png"); + icons.menu[3] = icon_load("../icon/wifi.png"); + icons.menu[4] = icon_load("../icon/settings.png"); + icons.menu[5] = icon_load("../icon/volume.png"); + + {int i; + for (i = 0; i < 9; ++i) { + char buffer[256]; + sprintf(buffer, "../images/image%d.png", (i+1)); + icons.images[i] = icon_load(buffer); + }} + + while (!glfwWindowShouldClose(win)) + { + /* Input */ + {double x, y; + nk_input_begin(&ctx); + glfwPollEvents(); + 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_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_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL)) { + 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_P) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_SHIFT, 1); + } else { + 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); + } + glfwGetCursorPos(win, &x, &y); + nk_input_motion(&ctx, (int)x, (int)y); + 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_end(&ctx);} + + /* GUI */ + basic_demo(&ctx, &icons); + button_demo(&ctx, &icons); + grid_demo(&ctx); + + /* Draw */ + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + device_draw(&device, &ctx, width, height, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + } + + glDeleteTextures(1,(const GLuint*)&icons.unchecked.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.checked.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.rocket.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.cloud.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.pen.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.play.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.pause.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.stop.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.next.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.prev.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.tools.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.dir.handle.id); + glDeleteTextures(1,(const GLuint*)&icons.del.handle.id); + + nk_font_atlas_clear(&atlas); + nk_free(&ctx); + device_shutdown(&device); + glfwTerminate(); + return 0; +} + + diff --git a/example/file_browser.c b/example/file_browser.c new file mode 100644 index 0000000..e6e1e89 --- /dev/null +++ b/example/file_browser.c @@ -0,0 +1,889 @@ +/* nuklear - v1.00 - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +/* macros */ +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +struct icons { + struct nk_image desktop; + struct nk_image home; + struct nk_image computer; + struct nk_image directory; + + struct nk_image default_file; + struct nk_image text_file; + struct nk_image music_file; + struct nk_image font_file; + struct nk_image img_file; + struct nk_image 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_CPP_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 nk_image *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]; +}; + +#define MAX_PATH_LEN 512 +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; + struct media *media; +}; + +#ifdef __unix__ +#include +#include +#endif + +#ifndef _WIN32 +# include +#endif + +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 char* +file_load(const char* path, size_t* siz) +{ + char *buf; + FILE *fd = fopen(path, "rb"); + if (!fd) die("Failed to open file: %s\n", path); + fseek(fd, 0, SEEK_END); + *siz = (size_t)ftell(fd); + fseek(fd, 0, SEEK_SET); + buf = (char*)calloc(*siz, 1); + fread(buf, *siz, 1, fd); + fclose(fd); + return buf; +} + +static char* +str_duplicate(const char *src) +{ + char *ret; + size_t len = strlen(src); + if (!len) return 0; + ret = (char*)malloc(len+1); + if (!ret) return 0; + memcpy(ret, src, len); + ret[len] = '\0'; + return ret; +} + +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 = (char**)calloc(sizeof(char*), capacity); + } else if (size >= capacity) { + capacity = capacity * 2; + results = (char**)realloc(results, capacity * sizeof(char*)); + } + p = str_duplicate(data->d_name); + results[size++] = p; + } + } while ((data = readdir(z)) != NULL); + } + + if (z) closedir(z); + *count = size; + return results; +} + +static struct file_group +FILE_GROUP(enum file_groups group, const char *name, struct nk_image *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 nk_image* +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) +{ + /* file groups */ + struct icons *icons = &media->icons; + 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_CPP_HEADER] = FILE_DEF(FILE_HEADER, "hpp", 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); +} + +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, struct media *media) +{ + memset(browser, 0, sizeof(*browser)); + browser->media = media; + { + /* 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; + { + size_t l; + strncpy(browser->home, home, MAX_PATH_LEN); + l = strlen(browser->home); + strcpy(browser->home + l, "/"); + strcpy(browser->directory, browser->home); + } +#endif + { + 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; + memset(browser, 0, sizeof(*browser)); +} + +static int +file_browser_run(struct file_browser *browser, struct nk_context *ctx) +{ + int ret = 0; + struct nk_panel layout; + struct media *media = browser->media; + struct nk_rect total_space; + + if (nk_begin(ctx, &layout, "File Browser", nk_rect(50, 50, 800, 600), + NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_CLOSABLE|NK_WINDOW_MOVABLE)) + { + struct nk_panel sub; + 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; + nk_menubar_begin(ctx); + { + char *d = browser->directory; + char *begin = d + 1; + nk_layout_row_dynamic(ctx, 25, 6); + while (*d++) { + if (*d == '/') { + *d = '\0'; + if (nk_button_label(ctx, begin, NK_BUTTON_DEFAULT)) { + *d++ = '/'; *d = '\0'; + file_browser_reload_directory_content(browser, browser->directory); + break; + } + *d = '/'; + begin = d + 1; + } + } + } + nk_menubar_end(ctx); + ctx->style.window.spacing.x = spacing_x; + + /* window layout */ + total_space = nk_window_get_content_region(ctx); + nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 2, ratio); + nk_group_begin(ctx, &sub, "Special", NK_WINDOW_NO_SCROLLBAR); + { + struct nk_image home = media->icons.home; + struct nk_image desktop = media->icons.desktop; + struct nk_image computer = media->icons.computer; + + nk_layout_row_dynamic(ctx, 40, 1); + if (nk_button_image_label(ctx, home, "home", NK_TEXT_CENTERED, NK_BUTTON_DEFAULT)) + file_browser_reload_directory_content(browser, browser->home); + if (nk_button_image_label(ctx,desktop,"desktop",NK_TEXT_CENTERED, NK_BUTTON_DEFAULT)) + file_browser_reload_directory_content(browser, browser->desktop); + if (nk_button_image_label(ctx,computer,"computer",NK_TEXT_CENTERED,NK_BUTTON_DEFAULT)) + file_browser_reload_directory_content(browser, "/"); + nk_group_end(ctx); + } + + /* output directory content window */ + nk_group_begin(ctx, &sub, "Content", 0); + { + 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) { + {size_t n = j + cols; + nk_layout_row_dynamic(ctx, 135, (int)cols); + for (; j < count && j < n; ++j) { + /* draw one row of icons */ + if (j < browser->dir_count) { + /* draw and execute directory buttons */ + if (nk_button_image(ctx,media->icons.directory,NK_BUTTON_DEFAULT)) + index = (int)j; + } else { + /* draw and execute files buttons */ + struct nk_image *icon; + size_t fileIndex = ((size_t)j - browser->dir_count); + icon = media_icon_for_file(media,browser->files[fileIndex]); + if (nk_button_image(ctx, *icon, NK_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); + ret = 1; + } + } + }} + {size_t n = k + cols; + nk_layout_row_dynamic(ctx, 20, (int)cols); + for (; k < count && k < n; k++) { + /* draw one row of labels */ + if (k < browser->dir_count) { + nk_label(ctx, browser->directories[k], NK_TEXT_CENTERED); + } else { + size_t t = k-browser->dir_count; + nk_label(ctx,browser->files[t],NK_TEXT_CENTERED); + } + }} + } + + 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); + sub.offset->y = 0; + } + nk_group_end(ctx); + } + } + nk_end(ctx); + return ret; +} + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct 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; +}; +static struct nk_image +icon_load(const char *filename) +{ + int x,y,n; + GLuint tex; + unsigned char *data = stbi_load(filename, &x, &y, &n, 0); + if (!data) die("[SDL]: failed to load image: %s", filename); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(data); + return nk_image_id((int)tex); +} + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + "#version 300 es\n" + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "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 = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + 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_draw_vertex); + size_t vp = offsetof(struct nk_draw_vertex, position); + size_t vt = offsetof(struct nk_draw_vertex, uv); + size_t vc = offsetof(struct nk_draw_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); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + 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); +} + +static void +device_shutdown(struct device *dev) +{ + 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); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + enum nk_anti_aliasing AA) +{ + GLint last_prog, last_tex; + GLint last_ebo, last_vbo, last_vao; + 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)width; + ortho[1][1] /= (GLfloat)height; + + /* save previous opengl state */ + glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); + + /* 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]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + 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_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill converting configuration */ + struct nk_convert_config config; + memset(&config, 0, sizeof(config)); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.null = dev->null; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor((GLint)cmd->clip_rect.x, + height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), + (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* restore old state */ + glUseProgram((GLuint)last_prog); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); + glBindVertexArray((GLuint)last_vao); + glDisable(GL_SCISSOR_TEST); +} + + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + + /* GUI */ + struct device device; + struct nk_context ctx; + struct nk_font *font; + struct nk_font_atlas atlas; + struct file_browser browser; + struct media media; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + {/* GUI */ + device_init(&device); + {const void *image; int w, h; + const char *font_path = (argc > 1) ? argv[1]: 0; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); + else font = nk_font_atlas_add_default(&atlas, 14.0f, NULL); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);} + nk_init_default(&ctx, &font->handle);} + + /* icons */ + glEnable(GL_TEXTURE_2D); + media.icons.home = icon_load("../icon/home.png"); + media.icons.directory = icon_load("../icon/directory.png"); + media.icons.computer = icon_load("../icon/computer.png"); + media.icons.desktop = icon_load("../icon/desktop.png"); + media.icons.default_file = icon_load("../icon/default.png"); + media.icons.text_file = icon_load("../icon/text.png"); + media.icons.music_file = icon_load("../icon/music.png"); + media.icons.font_file = icon_load("../icon/font.png"); + media.icons.img_file = icon_load("../icon/img.png"); + media.icons.movie_file = icon_load("../icon/movie.png"); + media_init(&media); + + file_browser_init(&browser, &media); + while (!glfwWindowShouldClose(win)) + { + /* Input */ + {double x, y; + nk_input_begin(&ctx); + glfwPollEvents(); + 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_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_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL)) { + 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_P) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_SHIFT, 1); + } else { + 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); + } + glfwGetCursorPos(win, &x, &y); + nk_input_motion(&ctx, (int)x, (int)y); + 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_end(&ctx);} + + /* GUI */ + file_browser_run(&browser, &ctx); + + /* Draw */ + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + device_draw(&device, &ctx, width, height, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + } + + glDeleteTextures(1,(const GLuint*)&media.icons.home.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.directory.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.computer.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.desktop.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.default_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.text_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.music_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.font_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.img_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.movie_file.handle.id); + + file_browser_free(&browser); + nk_font_atlas_clear(&atlas); + nk_free(&ctx); + device_shutdown(&device); + glfwTerminate(); + return 0; +} + + diff --git a/demo/icon/checked.png b/example/icon/checked.png similarity index 100% rename from demo/icon/checked.png rename to example/icon/checked.png diff --git a/demo/icon/cloud.png b/example/icon/cloud.png similarity index 100% rename from demo/icon/cloud.png rename to example/icon/cloud.png diff --git a/demo/icon/computer.png b/example/icon/computer.png similarity index 100% rename from demo/icon/computer.png rename to example/icon/computer.png diff --git a/demo/icon/copy.png b/example/icon/copy.png similarity index 100% rename from demo/icon/copy.png rename to example/icon/copy.png diff --git a/demo/icon/default.png b/example/icon/default.png similarity index 100% rename from demo/icon/default.png rename to example/icon/default.png diff --git a/demo/icon/delete.png b/example/icon/delete.png similarity index 100% rename from demo/icon/delete.png rename to example/icon/delete.png diff --git a/demo/icon/desktop.png b/example/icon/desktop.png similarity index 100% rename from demo/icon/desktop.png rename to example/icon/desktop.png diff --git a/demo/icon/directory.png b/example/icon/directory.png similarity index 100% rename from demo/icon/directory.png rename to example/icon/directory.png diff --git a/demo/icon/edit.png b/example/icon/edit.png similarity index 100% rename from demo/icon/edit.png rename to example/icon/edit.png diff --git a/demo/icon/export.png b/example/icon/export.png similarity index 100% rename from demo/icon/export.png rename to example/icon/export.png diff --git a/demo/icon/font.png b/example/icon/font.png similarity index 100% rename from demo/icon/font.png rename to example/icon/font.png diff --git a/demo/icon/home.png b/example/icon/home.png similarity index 100% rename from demo/icon/home.png rename to example/icon/home.png diff --git a/demo/icon/img.png b/example/icon/img.png similarity index 100% rename from demo/icon/img.png rename to example/icon/img.png diff --git a/demo/icon/movie.png b/example/icon/movie.png similarity index 100% rename from demo/icon/movie.png rename to example/icon/movie.png diff --git a/demo/icon/music.png b/example/icon/music.png similarity index 100% rename from demo/icon/music.png rename to example/icon/music.png diff --git a/demo/icon/next.png b/example/icon/next.png similarity index 100% rename from demo/icon/next.png rename to example/icon/next.png diff --git a/demo/icon/pause.png b/example/icon/pause.png similarity index 100% rename from demo/icon/pause.png rename to example/icon/pause.png diff --git a/demo/icon/pen.png b/example/icon/pen.png similarity index 100% rename from demo/icon/pen.png rename to example/icon/pen.png diff --git a/demo/icon/phone.png b/example/icon/phone.png similarity index 100% rename from demo/icon/phone.png rename to example/icon/phone.png diff --git a/demo/icon/plane.png b/example/icon/plane.png similarity index 100% rename from demo/icon/plane.png rename to example/icon/plane.png diff --git a/demo/icon/play.png b/example/icon/play.png similarity index 100% rename from demo/icon/play.png rename to example/icon/play.png diff --git a/demo/icon/prev.png b/example/icon/prev.png similarity index 100% rename from demo/icon/prev.png rename to example/icon/prev.png diff --git a/demo/icon/rocket.png b/example/icon/rocket.png similarity index 100% rename from demo/icon/rocket.png rename to example/icon/rocket.png diff --git a/demo/icon/settings.png b/example/icon/settings.png similarity index 100% rename from demo/icon/settings.png rename to example/icon/settings.png diff --git a/demo/icon/stop.png b/example/icon/stop.png similarity index 100% rename from demo/icon/stop.png rename to example/icon/stop.png diff --git a/demo/icon/text.png b/example/icon/text.png similarity index 100% rename from demo/icon/text.png rename to example/icon/text.png diff --git a/demo/icon/tools.png b/example/icon/tools.png similarity index 100% rename from demo/icon/tools.png rename to example/icon/tools.png diff --git a/demo/icon/unchecked.png b/example/icon/unchecked.png similarity index 100% rename from demo/icon/unchecked.png rename to example/icon/unchecked.png diff --git a/demo/icon/volume.png b/example/icon/volume.png similarity index 100% rename from demo/icon/volume.png rename to example/icon/volume.png diff --git a/demo/icon/wifi.png b/example/icon/wifi.png similarity index 100% rename from demo/icon/wifi.png rename to example/icon/wifi.png diff --git a/demo/images/image1.png b/example/images/image1.png similarity index 100% rename from demo/images/image1.png rename to example/images/image1.png diff --git a/demo/images/image2.png b/example/images/image2.png similarity index 100% rename from demo/images/image2.png rename to example/images/image2.png diff --git a/demo/images/image3.png b/example/images/image3.png similarity index 100% rename from demo/images/image3.png rename to example/images/image3.png diff --git a/demo/images/image4.png b/example/images/image4.png similarity index 100% rename from demo/images/image4.png rename to example/images/image4.png diff --git a/demo/images/image5.png b/example/images/image5.png similarity index 100% rename from demo/images/image5.png rename to example/images/image5.png diff --git a/demo/images/image6.png b/example/images/image6.png similarity index 100% rename from demo/images/image6.png rename to example/images/image6.png diff --git a/demo/images/image7.png b/example/images/image7.png similarity index 100% rename from demo/images/image7.png rename to example/images/image7.png diff --git a/demo/images/image8.png b/example/images/image8.png similarity index 100% rename from demo/images/image8.png rename to example/images/image8.png diff --git a/demo/images/image9.png b/example/images/image9.png similarity index 100% rename from demo/images/image9.png rename to example/images/image9.png diff --git a/example/node_editor.c b/example/node_editor.c new file mode 100644 index 0000000..5b70e67 --- /dev/null +++ b/example/node_editor.c @@ -0,0 +1,715 @@ +/* nuklear - v1.00 - public domain */ +/* This is a simple node editor just to show a simple implementation and that + * it is possible to achieve it with this library. While all nodes inside this + * example use a simple color modifier as content you could change them + * to have your custom content depending on the node time. + * Biggest difference to most usual implementation is that this example does + * not has connectors on the right position of the property that it links. + * This is mainly done out of lazyness and could be implemented as well but + * requires calculating the position of all rows and add connectors. + * In addition adding and removing nodes is quite limited at the + * moment since it is based on a simple fixed array. If this is to be converted + * into something more serious it is probably best to extend it.*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +/* macros */ +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * NODE EDITOR + * + * ===============================================================*/ +struct node { + int ID; + char name[32]; + struct nk_rect bounds; + float value; + struct nk_color color; + int input_count; + int output_count; + struct node *next; + struct node *prev; +}; + +struct node_link { + int input_id; + int input_slot; + int output_id; + int output_slot; + struct nk_vec2 in; + struct nk_vec2 out; +}; + +struct node_linking { + int active; + struct node *node; + int input_id; + int input_slot; +}; + +struct node_editor { + struct node node_buf[32]; + struct node_link links[64]; + struct node *begin; + struct node *end; + int node_count; + int link_count; + struct nk_rect bounds; + struct node *selected; + int show_grid; + struct nk_vec2 scrolling; + struct node_linking linking; +}; + +static void +node_editor_push(struct node_editor *editor, struct node *node) +{ + if (!editor->begin) { + node->next = NULL; + node->prev = NULL; + editor->begin = node; + editor->end = node; + } else { + node->prev = editor->end; + if (editor->end) + editor->end->next = node; + node->next = NULL; + editor->end = node; + } +} + +static void +node_editor_pop(struct node_editor *editor, struct node *node) +{ + if (node->next) + node->next->prev = node->prev; + if (node->prev) + node->prev->next = node->next; + if (editor->end == node) + editor->end = node->prev; + if (editor->begin == node) + editor->begin = node->next; + node->next = NULL; + node->prev = NULL; +} + +static struct node* +node_editor_find(struct node_editor *editor, int ID) +{ + struct node *iter = editor->begin; + while (iter) { + if (iter->ID == ID) + return iter; + iter = iter->next; + } + return NULL; +} + +static void +node_editor_add(struct node_editor *editor, const char *name, struct nk_rect bounds, + struct nk_color col, int in_count, int out_count) +{ + static int IDs = 0; + struct node *node; + assert((nk_size)editor->node_count < LEN(editor->node_buf)); + node = &editor->node_buf[editor->node_count++]; + node->ID = IDs++; + node->value = 0; + node->color = nk_rgb(255, 0, 0); + node->input_count = in_count; + node->output_count = out_count; + node->color = col; + node->bounds = bounds; + strcpy(node->name, name); + node_editor_push(editor, node); +} + +static void +node_editor_link(struct node_editor *editor, int in_id, int in_slot, + int out_id, int out_slot) +{ + struct node_link *link; + assert((nk_size)editor->link_count < LEN(editor->links)); + link = &editor->links[editor->link_count++]; + link->input_id = in_id; + link->input_slot = in_slot; + link->output_id = out_id; + link->output_slot = out_slot; +} + +static void +node_editor_init(struct node_editor *editor) +{ + memset(editor, 0, sizeof(*editor)); + editor->begin = NULL; + editor->end = NULL; + node_editor_add(editor, "Source", nk_rect(-40, 10, 180, 220), nk_rgb(255, 0, 0), 0, 1); + node_editor_add(editor, "Source", nk_rect(-40, 260, 180, 220), nk_rgb(0, 255, 0), 0, 1); + node_editor_add(editor, "Combine", nk_rect(400, 100, 180, 220), nk_rgb(0,0,255), 2, 2); + node_editor_link(editor, 0, 0, 2, 0); + node_editor_link(editor, 1, 0, 2, 1); + editor->show_grid = nk_true; +} + +static int +node_editor_run(struct node_editor *nodedit, struct nk_context *ctx) +{ + int n = 0; + struct nk_rect total_space; + const struct nk_input *in = &ctx->input; + struct nk_command_buffer *canvas; + struct node *updated = 0; + struct nk_panel layout; + + if (nk_begin(ctx, &layout, "Node Editor", nk_rect(50, 50, WINDOW_WIDTH, WINDOW_HEIGHT), + NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE)) + { + /* allocate complete window space */ + canvas = nk_window_get_canvas(ctx); + total_space = nk_window_get_content_region(ctx); + nk_layout_space_begin(ctx, NK_STATIC, total_space.h, nodedit->node_count); + { + struct nk_panel node, menu; + struct node *it = nodedit->begin; + struct nk_rect size = nk_layout_space_bounds(ctx); + + if (nodedit->show_grid) { + /* display grid */ + float x, y; + const float grid_size = 32.0f; + const struct nk_color grid_color = nk_rgb(50, 50, 50); + for (x = (float)fmod(size.x - nodedit->scrolling.x, grid_size); x < size.w; x += grid_size) + nk_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, 1.0f, grid_color); + for (y = (float)fmod(size.y - nodedit->scrolling.y, grid_size); y < size.h; y += grid_size) + nk_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, 1.0f, grid_color); + } + + /* execute each node as a moveable group */ + while (it) { + /* calculate scrolled node window position and size */ + nk_layout_space_push(ctx, nk_rect(it->bounds.x - nodedit->scrolling.x, + it->bounds.y - nodedit->scrolling.y, it->bounds.w, it->bounds.h)); + + /* execute node window */ + if (nk_group_begin(ctx, &node, it->name, NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE)) + { + /* always have last selected node on top */ + if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, node.bounds) && + (!(it->prev && nk_input_mouse_clicked(in, NK_BUTTON_LEFT, + nk_layout_space_rect_to_screen(ctx, node.bounds)))) && + nodedit->end != it) + { + updated = it; + } + + /* ================= NODE CONTENT =====================*/ + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_color(ctx, it->color, NK_BUTTON_DEFAULT); + it->color.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, it->color.r, 255, 1,1); + it->color.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, it->color.g, 255, 1,1); + it->color.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, it->color.b, 255, 1,1); + it->color.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, it->color.a, 255, 1,1); + /* ====================================================*/ + nk_group_end(ctx); + } + { + /* node connector and linking */ + float space; + struct nk_rect bounds; + bounds = nk_layout_space_rect_to_local(ctx, node.bounds); + bounds.x += nodedit->scrolling.x - ctx->style.window.border; + bounds.y += nodedit->scrolling.y - ctx->style.window.border; + bounds.w += 2*ctx->style.window.border; + bounds.h += 2*ctx->style.window.border; + + it->bounds = bounds; + + /* output connector */ + space = node.bounds.h / (float)((it->output_count) + 1); + for (n = 0; n < it->output_count; ++n) { + struct nk_rect circle; + circle.x = node.bounds.x + node.bounds.w-4; + circle.y = node.bounds.y + space * (float)(n+1); + circle.w = 8; circle.h = 8; + nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100)); + + /* start linking process */ + if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true)) { + nodedit->linking.active = nk_true; + nodedit->linking.node = it; + nodedit->linking.input_id = it->ID; + nodedit->linking.input_slot = n; + } + + /* draw curve from linked node slot to mouse position */ + if (nodedit->linking.active && nodedit->linking.node == it && + nodedit->linking.input_slot == n) { + struct nk_vec2 l0 = nk_vec2(circle.x + 3, circle.y + 3); + struct nk_vec2 l1 = in->mouse.pos; + nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, + l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100)); + } + } + + /* input connector */ + space = node.bounds.h / (float)((it->input_count) + 1); + for (n = 0; n < it->input_count; ++n) { + struct nk_rect circle; + circle.x = node.bounds.x-4; + circle.y = node.bounds.y + space * (float)(n+1); + circle.w = 8; circle.h = 8; + nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100)); + if (nk_input_is_mouse_released(in, NK_BUTTON_LEFT) && + nk_input_is_mouse_hovering_rect(in, circle) && + nodedit->linking.active && nodedit->linking.node != it) { + nodedit->linking.active = nk_false; + node_editor_link(nodedit, nodedit->linking.input_id, + nodedit->linking.input_slot, it->ID, n); + } + } + } + it = it->next; + } + + /* reset linking connection */ + if (nodedit->linking.active && nk_input_is_mouse_released(in, NK_BUTTON_LEFT)) { + nodedit->linking.active = nk_false; + nodedit->linking.node = NULL; + fprintf(stdout, "linking failed\n"); + } + + /* draw each link */ + for (n = 0; n < nodedit->link_count; ++n) { + struct node_link *link = &nodedit->links[n]; + struct node *ni = node_editor_find(nodedit, link->input_id); + struct node *no = node_editor_find(nodedit, link->output_id); + float spacei = node.bounds.h / (float)((ni->output_count) + 1); + float spaceo = node.bounds.h / (float)((no->input_count) + 1); + struct nk_vec2 l0 = nk_layout_space_to_screen(ctx, + nk_vec2(ni->bounds.x + ni->bounds.w, 3.0f + ni->bounds.y + spacei * (float)(link->input_slot+1))); + struct nk_vec2 l1 = nk_layout_space_to_screen(ctx, + nk_vec2(no->bounds.x, 3.0f + no->bounds.y + spaceo * (float)(link->output_slot+1))); + + l0.x -= nodedit->scrolling.x; + l0.y -= nodedit->scrolling.y; + l1.x -= nodedit->scrolling.x; + l1.y -= nodedit->scrolling.y; + nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, + l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100)); + } + + if (updated) { + /* reshuffle nodes to have least recently selected node on top */ + node_editor_pop(nodedit, updated); + node_editor_push(nodedit, updated); + } + + /* node selection */ + if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nk_layout_space_bounds(ctx))) { + it = nodedit->begin; + nodedit->selected = NULL; + nodedit->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200); + while (it) { + struct nk_rect b = nk_layout_space_rect_to_screen(ctx, it->bounds); + b.x -= nodedit->scrolling.x; + b.y -= nodedit->scrolling.y; + if (nk_input_is_mouse_hovering_rect(in, b)) + nodedit->selected = it; + it = it->next; + } + } + + /* contextual menu */ + if (nk_contextual_begin(ctx, &menu, 0, nk_vec2(100, 220), nk_window_get_bounds(ctx))) { + const char *grid_option[] = {"Show Grid", "Hide Grid"}; + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_contextual_item_label(ctx, "New", NK_TEXT_CENTERED)) + node_editor_add(nodedit, "New", nk_rect(400, 260, 180, 220), + nk_rgb(255, 255, 255), 1, 2); + if (nk_contextual_item_label(ctx, grid_option[nodedit->show_grid],NK_TEXT_CENTERED)) + nodedit->show_grid = !nodedit->show_grid; + nk_contextual_end(ctx); + } + } + nk_layout_space_end(ctx); + + /* window content scrolling */ + if (nk_input_is_mouse_hovering_rect(in, nk_window_get_bounds(ctx)) && + nk_input_is_mouse_down(in, NK_BUTTON_MIDDLE)) { + nodedit->scrolling.x += in->mouse.delta.x; + nodedit->scrolling.y += in->mouse.delta.y; + } + } + nk_end(ctx); + return !nk_window_is_closed(ctx, "Node Editor"); +} + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct 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; +}; + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + "#version 300 es\n" + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "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 = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + 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_draw_vertex); + size_t vp = offsetof(struct nk_draw_vertex, position); + size_t vt = offsetof(struct nk_draw_vertex, uv); + size_t vc = offsetof(struct nk_draw_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); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + 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); +} + +static void +device_shutdown(struct device *dev) +{ + 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); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + enum nk_anti_aliasing AA) +{ + GLint last_prog, last_tex; + GLint last_ebo, last_vbo, last_vao; + 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)width; + ortho[1][1] /= (GLfloat)height; + + /* save previous opengl state */ + glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); + + /* 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]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + 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_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill converting configuration */ + struct nk_convert_config config; + memset(&config, 0, sizeof(config)); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.null = dev->null; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor((GLint)cmd->clip_rect.x, + height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), + (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* restore old state */ + glUseProgram((GLuint)last_prog); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); + glBindVertexArray((GLuint)last_vao); + glDisable(GL_SCISSOR_TEST); +} + + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + + /* GUI */ + struct device device; + struct nk_context ctx; + struct nk_font *font; + struct nk_font_atlas atlas; + struct node_editor node; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + {/* GUI */ + device_init(&device); + {const void *image; int w, h; + const char *font_path = (argc > 1) ? argv[1]: 0; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); + else font = nk_font_atlas_add_default(&atlas, 14.0f, NULL); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);} + nk_init_default(&ctx, &font->handle);} + + node_editor_init(&node); + while (!glfwWindowShouldClose(win)) + { + /* Input */ + {double x, y; + nk_input_begin(&ctx); + glfwPollEvents(); + 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_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_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL)) { + 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_P) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_SHIFT, 1); + } else { + 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); + } + glfwGetCursorPos(win, &x, &y); + nk_input_motion(&ctx, (int)x, (int)y); + 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_end(&ctx);} + + /* GUI */ + if (!node_editor_run(&node, &ctx)) break; + + /* Draw */ + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + device_draw(&device, &ctx, width, height, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + } + + nk_font_atlas_clear(&atlas); + nk_free(&ctx); + device_shutdown(&device); + glfwTerminate(); + return 0; +} + diff --git a/example/overview.c b/example/overview.c new file mode 100644 index 0000000..ded95e6 --- /dev/null +++ b/example/overview.c @@ -0,0 +1,1466 @@ +/* nuklear - v1.00 - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +/* macros */ +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +static int +overview_window(struct nk_context *ctx) +{ + struct nk_panel menu; + + /* window flags */ + static int show_menu = nk_true; + static int titlebar = nk_true; + static int border = nk_true; + static int resize = nk_true; + static int moveable = nk_true; + static int no_scrollbar = nk_false; + static nk_flags window_flags = 0; + static int minimizable = nk_true; + static int close = nk_true; + + /* popups */ + static enum nk_style_header_align header_align = NK_HEADER_RIGHT; + static int show_app_about = nk_false; + struct nk_panel layout; + + /* window flags */ + window_flags = 0; + ctx->style.window.header.align = header_align; + if (border) window_flags |= NK_WINDOW_BORDER; + if (resize) window_flags |= NK_WINDOW_SCALABLE; + if (moveable) window_flags |= NK_WINDOW_MOVABLE; + if (no_scrollbar) window_flags |= NK_WINDOW_NO_SCROLLBAR; + if (minimizable) window_flags |= NK_WINDOW_MINIMIZABLE; + if (close) window_flags |= NK_WINDOW_CLOSABLE; + + if (nk_begin(ctx, &layout, "Demo", nk_rect(10, 10, 400, 750), window_flags)) + { + if (show_menu) + { + /* menubar */ + enum menu_states {MENU_DEFAULT, MENU_WINDOWS}; + static enum menu_states menu_state = MENU_DEFAULT; + static nk_size mprog = 60; + static int mslider = 10; + static int mcheck = nk_true; + + nk_menubar_begin(ctx); + nk_layout_row_begin(ctx, NK_STATIC, 25, 2); + nk_layout_row_push(ctx, 45); + if (nk_menu_begin_label(ctx, &menu, "MENU", NK_TEXT_LEFT, 120)) + { + static size_t prog = 40; + static int slider = 10; + static int check = nk_true; + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_menu_item_label(ctx, "Hide", NK_TEXT_LEFT)) + show_menu = nk_false; + if (nk_menu_item_label(ctx, "About", NK_TEXT_LEFT)) + show_app_about = nk_true; + nk_progress(ctx, &prog, 100, NK_MODIFIABLE); + nk_slider_int(ctx, 0, &slider, 16, 1); + nk_checkbox_label(ctx, "check", &check); + nk_menu_end(ctx); + } + nk_layout_row_push(ctx, 70); + nk_progress(ctx, &mprog, 100, NK_MODIFIABLE); + nk_slider_int(ctx, 0, &mslider, 16, 1); + nk_checkbox_label(ctx, "check", &mcheck); + nk_menubar_end(ctx); + } + + if (show_app_about) + { + /* about popup */ + struct nk_panel popup; + static struct nk_rect s = {20, 100, 300, 190}; + if (nk_popup_begin(ctx, &popup, NK_POPUP_STATIC, "About", NK_WINDOW_CLOSABLE, s)) + { + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "nuklear", NK_TEXT_LEFT); + nk_label(ctx, "By Micha Mettke", NK_TEXT_LEFT); + nk_label(ctx, "nuklear is licensed under the zlib License.", NK_TEXT_LEFT); + nk_label(ctx, "See LICENSE for more information", NK_TEXT_LEFT); + nk_popup_end(ctx); + } else show_app_about = nk_false; + } + + /* window flags */ + if (nk_tree_push(ctx, NK_TREE_TAB, "Window", NK_MINIMIZED)) { + nk_layout_row_dynamic(ctx, 30, 2); + nk_checkbox_label(ctx, "Titlebar", &titlebar); + nk_checkbox_label(ctx, "Menu", &show_menu); + nk_checkbox_label(ctx, "Border", &border); + nk_checkbox_label(ctx, "Resizable", &resize); + nk_checkbox_label(ctx, "Moveable", &moveable); + nk_checkbox_label(ctx, "No Scrollbar", &no_scrollbar); + nk_checkbox_label(ctx, "Minimizable", &minimizable); + nk_checkbox_label(ctx, "Closeable", &close); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Widgets", NK_MINIMIZED)) + { + enum options {A,B,C}; + static int checkbox; + static int option; + if (nk_tree_push(ctx, NK_TREE_NODE, "Text", NK_MINIMIZED)) + { + /* Text Widgets */ + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "Label aligned left", NK_TEXT_LEFT); + nk_label(ctx, "Label aligned centered", NK_TEXT_CENTERED); + nk_label(ctx, "Label aligned right", NK_TEXT_RIGHT); + nk_label_colored(ctx, "Blue text", NK_TEXT_LEFT, nk_rgb(0,0,255)); + nk_label_colored(ctx, "Yellow text", NK_TEXT_LEFT, nk_rgb(255,255,0)); + nk_text(ctx, "Text without /0", 15, NK_TEXT_RIGHT); + + nk_layout_row_static(ctx, 100, 200, 1); + nk_label_wrap(ctx, "This is a very long line to hopefully get this text to be wrapped into multiple lines to show line wrapping"); + nk_layout_row_dynamic(ctx, 100, 1); + nk_label_wrap(ctx, "This is another long text to show dynamic window changes on multiline text"); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Button", NK_MINIMIZED)) + { + /* Buttons Widgets */ + nk_layout_row_static(ctx, 30, 100, 3); + if (nk_button_label(ctx, "Button", NK_BUTTON_DEFAULT)) + fprintf(stdout, "Button pressed!\n"); + if (nk_button_label(ctx, "Repeater", NK_BUTTON_REPEATER)) + fprintf(stdout, "Repeater is being pressed!\n"); + nk_button_color(ctx, nk_rgb(0,0,255), NK_BUTTON_DEFAULT); + + nk_layout_row_static(ctx, 20, 20, 8); + nk_button_symbol(ctx, NK_SYMBOL_CIRCLE, NK_BUTTON_DEFAULT); + nk_button_symbol(ctx, NK_SYMBOL_CIRCLE_FILLED, NK_BUTTON_DEFAULT); + nk_button_symbol(ctx, NK_SYMBOL_RECT, NK_BUTTON_DEFAULT); + nk_button_symbol(ctx, NK_SYMBOL_RECT_FILLED, NK_BUTTON_DEFAULT); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_UP, NK_BUTTON_DEFAULT); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_DOWN, NK_BUTTON_DEFAULT); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_LEFT, NK_BUTTON_DEFAULT); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_RIGHT, NK_BUTTON_DEFAULT); + + nk_layout_row_static(ctx, 30, 100, 2); + nk_button_symbol_label(ctx, NK_SYMBOL_TRIANGLE_LEFT, "prev", NK_TEXT_RIGHT, NK_BUTTON_DEFAULT); + nk_button_symbol_label(ctx, NK_SYMBOL_TRIANGLE_RIGHT, "next", NK_TEXT_LEFT, NK_BUTTON_DEFAULT); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Basic", NK_MINIMIZED)) + { + /* Basic widgets */ + static int int_slider = 5; + static float float_slider = 2.5f; + static size_t prog_value = 40; + static float property_float = 2; + static int property_int = 10; + static int property_neg = 10; + + static float range_float_min = 0; + static float range_float_max = 100; + static float range_float_value = 50; + static int range_int_min = 0; + static int range_int_value = 2048; + static int range_int_max = 4096; + static const float ratio[] = {120, 150}; + + nk_layout_row_static(ctx, 30, 100, 1); + nk_checkbox_label(ctx, "Checkbox", &checkbox); + + nk_layout_row_static(ctx, 30, 80, 3); + option = nk_option_label(ctx, "optionA", option == A) ? A : option; + option = nk_option_label(ctx, "optionB", option == B) ? B : option; + option = nk_option_label(ctx, "optionC", option == C) ? C : option; + + + nk_layout_row(ctx, NK_STATIC, 30, 2, ratio); + nk_labelf(ctx, NK_TEXT_LEFT, "Slider int"); + nk_slider_int(ctx, 0, &int_slider, 10, 1); + + nk_label(ctx, "Slider float", NK_TEXT_LEFT); + nk_slider_float(ctx, 0, &float_slider, 5.0, 0.5f); + nk_labelf(ctx, NK_TEXT_LEFT, "Progressbar" , prog_value); + nk_progress(ctx, &prog_value, 100, NK_MODIFIABLE); + + nk_layout_row(ctx, NK_STATIC, 25, 2, ratio); + nk_label(ctx, "Property float:", NK_TEXT_LEFT); + nk_property_float(ctx, "Float:", 0, &property_float, 64.0f, 0.1f, 0.2f); + nk_label(ctx, "Property int:", NK_TEXT_LEFT); + nk_property_int(ctx, "Int:", 0, &property_int, 100.0f, 1, 1); + nk_label(ctx, "Property neg:", NK_TEXT_LEFT); + nk_property_int(ctx, "Neg:", -10, &property_neg, 10, 1, 1); + + nk_layout_row_dynamic(ctx, 25, 1); + nk_label(ctx, "Range:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 3); + nk_property_float(ctx, "#min:", 0, &range_float_min, range_float_max, 1.0f, 0.2f); + nk_property_float(ctx, "#float:", range_float_min, &range_float_value, range_float_max, 1.0f, 0.2f); + nk_property_float(ctx, "#max:", range_float_min, &range_float_max, 100, 1.0f, 0.2f); + + nk_property_int(ctx, "#min:", INT_MIN, &range_int_min, range_int_max, 1, 10); + nk_property_int(ctx, "#neg:", range_int_min, &range_int_value, range_int_max, 1, 10); + nk_property_int(ctx, "#max:", range_int_min, &range_int_max, INT_MAX, 1, 10); + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Selectable", NK_MINIMIZED)) + { + if (nk_tree_push(ctx, NK_TREE_NODE, "List", NK_MINIMIZED)) + { + static int selected[4] = {nk_false, nk_false, nk_true, nk_false}; + nk_layout_row_static(ctx, 18, 100, 1); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[0]); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[1]); + nk_label(ctx, "Not Selectable", NK_TEXT_LEFT); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[2]); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[3]); + nk_tree_pop(ctx); + } + if (nk_tree_push(ctx, NK_TREE_NODE, "Grid", NK_MINIMIZED)) + { + int i; + static int selected[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; + nk_layout_row_static(ctx, 50, 50, 4); + for (i = 0; i < 16; ++i) { + if (nk_selectable_label(ctx, "Z", NK_TEXT_CENTERED, &selected[i])) { + int x = (i % 4), y = i / 4; + if (x > 0) selected[i - 1] ^= 1; + if (x < 3) selected[i + 1] ^= 1; + if (y > 0) selected[i - 4] ^= 1; + if (y < 3) selected[i + 4] ^= 1; + } + } + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Combo", NK_MINIMIZED)) + { + /* Combobox Widgets + * In this library comboboxes are not limited to being a popup + * list of selectable text. Instead it is a abstract concept of + * having something that is *selected* or displayed, a popup window + * which opens if something needs to be modified and the content + * of the popup which causes the *selected* or displayed value to + * change or if wanted close the combobox. + * + * While strange at first handling comboboxes in a abstract way + * solves the problem of overloaded window content. For example + * changing a color value requires 4 value modifier (slider, property,...) + * for RGBA then you need a label and ways to display the current color. + * If you want to go fancy you even add rgb and hsv ratio boxes. + * While fine for one color if you have a lot of them it because + * tedious to look at and quite wasteful in space. You could add + * a popup which modifies the color but this does not solve the + * fact that it still requires a lot of cluttered space to do. + * + * In these kind of instance abstract comboboxes are quite handy. All + * value modifiers are hidden inside the combobox popup and only + * the color is shown if not open. This combines the clarity of the + * popup with the ease of use of just using the space for modifiers. + * + * Other instances are for example time and especially date picker, + * which only show the currently activated time/data and hide the + * selection logic inside the combobox popup. + */ + static float chart_selection = 8.0f; + static int current_weapon = 0; + static int check_values[5]; + static float position[3]; + static struct nk_color combo_color = {130, 50, 50, 255}; + static struct nk_color combo_color2 = {130, 180, 50, 255}; + static size_t prog_a = 20, prog_b = 40, prog_c = 10, prog_d = 90; + static const char *weapons[] = {"Fist","Pistol","Shotgun","Plasma","BFG"}; + + char buffer[64]; + size_t sum = 0; + struct nk_panel combo; + + /* default combobox */ + nk_layout_row_static(ctx, 25, 200, 1); + current_weapon = nk_combo(ctx, weapons, LEN(weapons), current_weapon, 25); + + /* slider color combobox */ + if (nk_combo_begin_color(ctx, &combo, combo_color, 200)) { + float ratios[] = {0.15f, 0.85f}; + nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratios); + nk_label(ctx, "R:", NK_TEXT_LEFT); + combo_color.r = (nk_byte)nk_slide_int(ctx, 0, combo_color.r, 255, 5); + nk_label(ctx, "G:", NK_TEXT_LEFT); + combo_color.g = (nk_byte)nk_slide_int(ctx, 0, combo_color.g, 255, 5); + nk_label(ctx, "B:", NK_TEXT_LEFT); + combo_color.b = (nk_byte)nk_slide_int(ctx, 0, combo_color.b, 255, 5); + nk_label(ctx, "A:", NK_TEXT_LEFT); + combo_color.a = (nk_byte)nk_slide_int(ctx, 0, combo_color.a , 255, 5); + nk_combo_end(ctx); + } + + /* complex color combobox */ + if (nk_combo_begin_color(ctx, &combo, combo_color2, 400)) { + enum color_mode {COL_RGB, COL_HSV}; + static int col_mode = COL_RGB; + #ifndef DEMO_DO_NOT_USE_COLOR_PICKER + nk_layout_row_dynamic(ctx, 120, 1); + combo_color2 = nk_color_picker(ctx, combo_color2, NK_RGBA); + #endif + + nk_layout_row_dynamic(ctx, 25, 2); + col_mode = nk_option_label(ctx, "RGB", col_mode == COL_RGB) ? COL_RGB : col_mode; + col_mode = nk_option_label(ctx, "HSV", col_mode == COL_HSV) ? COL_HSV : col_mode; + + nk_layout_row_dynamic(ctx, 25, 1); + if (col_mode == COL_RGB) { + combo_color2.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, combo_color2.r, 255, 1,1); + combo_color2.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, combo_color2.g, 255, 1,1); + combo_color2.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, combo_color2.b, 255, 1,1); + combo_color2.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, combo_color2.a, 255, 1,1); + } else { + nk_byte tmp[4]; + nk_color_hsva_bv(tmp, combo_color2); + tmp[0] = (nk_byte)nk_propertyi(ctx, "#H:", 0, tmp[0], 255, 1,1); + tmp[1] = (nk_byte)nk_propertyi(ctx, "#S:", 0, tmp[1], 255, 1,1); + tmp[2] = (nk_byte)nk_propertyi(ctx, "#V:", 0, tmp[2], 255, 1,1); + tmp[3] = (nk_byte)nk_propertyi(ctx, "#A:", 0, tmp[3], 255, 1,1); + combo_color2 = nk_hsva_bv(tmp); + } + nk_combo_end(ctx); + } + + /* progressbar combobox */ + sum = prog_a + prog_b + prog_c + prog_d; + sprintf(buffer, "%lu", sum); + if (nk_combo_begin_label(ctx, &combo, buffer, 200)) { + nk_layout_row_dynamic(ctx, 30, 1); + nk_progress(ctx, &prog_a, 100, NK_MODIFIABLE); + nk_progress(ctx, &prog_b, 100, NK_MODIFIABLE); + nk_progress(ctx, &prog_c, 100, NK_MODIFIABLE); + nk_progress(ctx, &prog_d, 100, NK_MODIFIABLE); + nk_combo_end(ctx); + } + + /* checkbox combobox */ + sum = (size_t)(check_values[0] + check_values[1] + check_values[2] + check_values[3] + check_values[4]); + sprintf(buffer, "%lu", sum); + if (nk_combo_begin_label(ctx, &combo, buffer, 200)) { + nk_layout_row_dynamic(ctx, 30, 1); + nk_checkbox_label(ctx, weapons[0], &check_values[0]); + nk_checkbox_label(ctx, weapons[1], &check_values[1]); + nk_checkbox_label(ctx, weapons[2], &check_values[2]); + nk_checkbox_label(ctx, weapons[3], &check_values[3]); + nk_combo_end(ctx); + } + + /* complex text combobox */ + sprintf(buffer, "%.2f, %.2f, %.2f", position[0], position[1],position[2]); + if (nk_combo_begin_label(ctx, &combo, buffer, 200)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_float(ctx, "#X:", -1024.0f, &position[0], 1024.0f, 1,0.5f); + nk_property_float(ctx, "#Y:", -1024.0f, &position[1], 1024.0f, 1,0.5f); + nk_property_float(ctx, "#Z:", -1024.0f, &position[2], 1024.0f, 1,0.5f); + nk_combo_end(ctx); + } + + /* chart combobox */ + sprintf(buffer, "%.1f", chart_selection); + if (nk_combo_begin_label(ctx, &combo, buffer, 250)) { + size_t i = 0; + static const float values[]={26.0f,13.0f,30.0f,15.0f,25.0f,10.0f,20.0f,40.0f, 12.0f, 8.0f, 22.0f, 28.0f, 5.0f}; + nk_layout_row_dynamic(ctx, 150, 1); + nk_chart_begin(ctx, NK_CHART_COLUMN, LEN(values), 0, 50); + for (i = 0; i < LEN(values); ++i) { + nk_flags res = nk_chart_push(ctx, values[i]); + if (res & NK_CHART_CLICKED) { + chart_selection = values[i]; + nk_combo_close(ctx); + } + } + nk_chart_end(ctx); + nk_combo_end(ctx); + } + + { + static int time_selected = 0; + static int date_selected = 0; + static struct tm sel_time; + static struct tm sel_date; + if (!time_selected || !date_selected) { + /* keep time and date updated if nothing is selected */ + time_t cur_time = time(0); + struct tm *n = localtime(&cur_time); + if (!time_selected) + memcpy(&sel_time, n, sizeof(struct tm)); + if (!date_selected) + memcpy(&sel_date, n, sizeof(struct tm)); + } + + /* time combobox */ + sprintf(buffer, "%02d:%02d:%02d", sel_time.tm_hour, sel_time.tm_min, sel_time.tm_sec); + if (nk_combo_begin_label(ctx, &combo, buffer, 250)) { + time_selected = 1; + nk_layout_row_dynamic(ctx, 25, 1); + sel_time.tm_sec = nk_propertyi(ctx, "#S:", 0, sel_time.tm_sec, 60, 1, 1); + sel_time.tm_min = nk_propertyi(ctx, "#M:", 0, sel_time.tm_min, 60, 1, 1); + sel_time.tm_hour = nk_propertyi(ctx, "#H:", 0, sel_time.tm_hour, 23, 1, 1); + nk_combo_end(ctx); + } + + /* date combobox */ + nk_layout_row_static(ctx, 25, 350, 1); + sprintf(buffer, "%02d-%02d-%02d", sel_date.tm_mday, sel_date.tm_mon+1, sel_date.tm_year+1900); + if (nk_combo_begin_label(ctx, &combo, buffer, 400)) + { + int i = 0; + const char *month[] = {"January", "February", "March", "Apil", "May", "June", "July", "August", "September", "Ocotober", "November", "December"}; + const char *week_days[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; + const int month_days[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + int year = sel_date.tm_year+1900; + int leap_year = (!(year % 4) && ((year % 100))) || !(year % 400); + int days = (sel_date.tm_mon == 1) ? + month_days[sel_date.tm_mon] + leap_year: + month_days[sel_date.tm_mon]; + + /* header with month and year */ + date_selected = 1; + nk_layout_row_begin(ctx, NK_DYNAMIC, 20, 3); + nk_layout_row_push(ctx, 0.05f); + if (nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_LEFT, NK_BUTTON_DEFAULT)) { + if (sel_date.tm_mon == 0) { + sel_date.tm_mon = 11; + sel_date.tm_year = MAX(0, sel_date.tm_year-1); + } else sel_date.tm_mon--; + } + nk_layout_row_push(ctx, 0.9f); + sprintf(buffer, "%s %d", month[sel_date.tm_mon], year); + nk_label(ctx, buffer, NK_TEXT_CENTERED); + nk_layout_row_push(ctx, 0.05f); + if (nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_RIGHT, NK_BUTTON_DEFAULT)) { + if (sel_date.tm_mon == 11) { + sel_date.tm_mon = 0; + sel_date.tm_year++; + } else sel_date.tm_mon++; + } + nk_layout_row_end(ctx); + + /* good old week day formula (double because precision) */ + {int year_n = (sel_date.tm_mon < 2) ? year-1: year; + int y = year_n % 100; + int c = year_n / 100; + int y4 = (int)((float)y / 4); + int c4 = (int)((float)c / 4); + int m = (int)(2.6 * (double)(((sel_date.tm_mon + 10) % 12) + 1) - 0.2); + int week_day = (((1 + m + y + y4 + c4 - 2 * c) % 7) + 7) % 7; + + /* weekdays */ + nk_layout_row_dynamic(ctx, 35, 7); + for (i = 0; i < (int)LEN(week_days); ++i) + nk_label(ctx, week_days[i], NK_TEXT_CENTERED); + + /* days */ + if (week_day > 0) nk_spacing(ctx, week_day); + for (i = 1; i <= days; ++i) { + sprintf(buffer, "%d", i); + if (nk_button_label(ctx, buffer, NK_BUTTON_DEFAULT)) { + sel_date.tm_mday = i; + nk_combo_close(ctx); + } + }} + nk_combo_end(ctx); + } + } + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Input", NK_MINIMIZED)) + { + static const float ratio[] = {120, 150}; + static char field_buffer[64]; + static char text[9][64]; + static int text_len[9]; + static char box_buffer[512]; + static int field_len; + static int box_len; + nk_flags active; + + nk_layout_row(ctx, NK_STATIC, 25, 2, ratio); + nk_label(ctx, "Default:", NK_TEXT_LEFT); + + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[0], &text_len[0], 64, nk_filter_default); + nk_label(ctx, "Int:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[1], &text_len[1], 64, nk_filter_decimal); + nk_label(ctx, "Float:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[2], &text_len[2], 64, nk_filter_float); + nk_label(ctx, "Hex:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[4], &text_len[4], 64, nk_filter_hex); + nk_label(ctx, "Octal:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[5], &text_len[5], 64, nk_filter_oct); + nk_label(ctx, "Binary:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[6], &text_len[6], 64, nk_filter_binary); + + nk_label(ctx, "Password:", NK_TEXT_LEFT); + { + int i = 0; + int old_len = text_len[8]; + char buffer[64]; + for (i = 0; i < text_len[8]; ++i) buffer[i] = '*'; + nk_edit_string(ctx, NK_EDIT_FIELD, buffer, &text_len[8], 64, nk_filter_default); + if (old_len < text_len[8]) + memcpy(&text[8][old_len], &buffer[old_len], (nk_size)(text_len[8] - old_len)); + } + + nk_label(ctx, "Field:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_FIELD, field_buffer, &field_len, 64, nk_filter_default); + + nk_label(ctx, "Box:", NK_TEXT_LEFT); + nk_layout_row_static(ctx, 180, 278, 1); + nk_edit_string(ctx, NK_EDIT_BOX, box_buffer, &box_len, 512, nk_filter_default); + + nk_layout_row(ctx, NK_STATIC, 25, 2, ratio); + active = nk_edit_string(ctx, NK_EDIT_FIELD|NK_EDIT_SIG_ENTER, text[7], &text_len[7], 64, nk_filter_ascii); + if (nk_button_label(ctx, "Submit", NK_BUTTON_DEFAULT) || + (active & NK_EDIT_COMMITED)) + { + text[7][text_len[7]] = '\n'; + text_len[7]++; + memcpy(&box_buffer[box_len], &text[7], (nk_size)text_len[7]); + box_len += text_len[7]; + text_len[7] = 0; + } + nk_layout_row_end(ctx); + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Chart", NK_MINIMIZED)) + { + /* Chart Widgets + * This library has two different rather simple charts. The line and the + * column chart. Both provide a simple way of visualizing values and + * have a retain mode and immedidate mode API version. For the retain + * mode version `nk_plot` and `nk_plot_function` you either provide + * an array or a callback to call to handle drawing the graph. + * For the immediate mode version you start by calling `nk_chart_begin` + * and need to provide min and max values for scaling on the Y-axis. + * and then call `nk_chart_push` to push values into the chart. + * Finally `nk_chart_end` needs to be called to end the process. */ + float id = 0; + static int col_index = -1; + static int line_index = -1; + float step = (2*3.141592654f) / 32; + + int i; + int index = -1; + struct nk_rect bounds; + + /* line chart */ + id = 0; + index = -1; + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin(ctx, NK_CHART_LINES, 32, -1.0f, 1.0f)) { + for (i = 0; i < 32; ++i) { + nk_flags res = nk_chart_push(ctx, (float)cos(id)); + if (res & NK_CHART_HOVERING) + index = (int)i; + if (res & NK_CHART_CLICKED) + line_index = (int)i; + id += step; + } + nk_chart_end(ctx); + } + + if (index != -1) { + char buffer[NK_MAX_NUMBER_BUFFER]; + float val = (float)cos((float)index*step); + sprintf(buffer, "Value: %.2f", val); + nk_tooltip(ctx, buffer); + } + if (line_index != -1) { + nk_layout_row_dynamic(ctx, 20, 1); + nk_labelf(ctx, NK_TEXT_LEFT, "Selected value: %.2f", (float)cos((float)index*step)); + } + + /* column chart */ + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin(ctx, NK_CHART_COLUMN, 32, 0.0f, 1.0f)) { + for (i = 0; i < 32; ++i) { + nk_flags res = nk_chart_push(ctx, (float)fabs(sin(id))); + if (res & NK_CHART_HOVERING) + index = (int)i; + if (res & NK_CHART_CLICKED) + col_index = (int)i; + id += step; + } + nk_chart_end(ctx); + } + if (index != -1) { + char buffer[NK_MAX_NUMBER_BUFFER]; + sprintf(buffer, "Value: %.2f", (float)fabs(sin(step * (float)index))); + nk_tooltip(ctx, buffer); + } + if (col_index != -1) { + nk_layout_row_dynamic(ctx, 20, 1); + nk_labelf(ctx, NK_TEXT_LEFT, "Selected value: %.2f", (float)fabs(sin(step * (float)col_index))); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Popup", NK_MINIMIZED)) + { + static struct nk_color color = {255,0,0, 255}; + static int select[4]; + static int popup_active; + const struct nk_input *in = &ctx->input; + struct nk_rect bounds; + + /* menu contextual */ + nk_layout_row_static(ctx, 30, 150, 1); + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Right click me for menu", NK_TEXT_LEFT); + + if (nk_contextual_begin(ctx, &menu, 0, nk_vec2(100, 300), bounds)) { + static size_t prog = 40; + static int slider = 10; + + nk_layout_row_dynamic(ctx, 25, 1); + nk_checkbox_label(ctx, "Menu", &show_menu); + nk_progress(ctx, &prog, 100, NK_MODIFIABLE); + nk_slider_int(ctx, 0, &slider, 16, 1); + if (nk_contextual_item_label(ctx, "About", NK_TEXT_CENTERED)) + show_app_about = nk_true; + nk_selectable_label(ctx, select[0]?"Unselect":"Select", NK_TEXT_LEFT, &select[0]); + nk_selectable_label(ctx, select[1]?"Unselect":"Select", NK_TEXT_LEFT, &select[1]); + nk_selectable_label(ctx, select[2]?"Unselect":"Select", NK_TEXT_LEFT, &select[2]); + nk_selectable_label(ctx, select[3]?"Unselect":"Select", NK_TEXT_LEFT, &select[3]); + nk_contextual_end(ctx); + } + + /* color contextual */ + nk_layout_row_begin(ctx, NK_STATIC, 30, 2); + nk_layout_row_push(ctx, 100); + nk_label(ctx, "Right Click here:", NK_TEXT_LEFT); + nk_layout_row_push(ctx, 50); + bounds = nk_widget_bounds(ctx); + nk_button_color(ctx, color, NK_BUTTON_DEFAULT); + nk_layout_row_end(ctx); + + if (nk_contextual_begin(ctx, &menu, 0, nk_vec2(350, 60), bounds)) { + nk_layout_row_dynamic(ctx, 30, 4); + color.r = (nk_byte)nk_propertyi(ctx, "#r", 0, color.r, 255, 1, 1); + color.g = (nk_byte)nk_propertyi(ctx, "#g", 0, color.g, 255, 1, 1); + color.b = (nk_byte)nk_propertyi(ctx, "#b", 0, color.b, 255, 1, 1); + color.a = (nk_byte)nk_propertyi(ctx, "#a", 0, color.a, 255, 1, 1); + nk_contextual_end(ctx); + } + + /* popup */ + nk_layout_row_begin(ctx, NK_STATIC, 30, 2); + nk_layout_row_push(ctx, 100); + nk_label(ctx, "Popup:", NK_TEXT_LEFT); + nk_layout_row_push(ctx, 50); + if (nk_button_label(ctx, "Popup", NK_BUTTON_DEFAULT)) + popup_active = 1; + nk_layout_row_end(ctx); + + if (popup_active) + { + static struct nk_rect s = {20, 100, 220, 150}; + if (nk_popup_begin(ctx, &menu, NK_POPUP_STATIC, "Error", NK_WINDOW_DYNAMIC, s)) + { + nk_layout_row_dynamic(ctx, 25, 1); + nk_label(ctx, "A terrible error as occured", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 2); + if (nk_button_label(ctx, "OK", NK_BUTTON_DEFAULT)) { + popup_active = 0; + nk_popup_close(ctx); + } + if (nk_button_label(ctx, "Cancel", NK_BUTTON_DEFAULT)) { + popup_active = 0; + nk_popup_close(ctx); + } + nk_popup_end(ctx); + } else popup_active = nk_false; + } + + /* tooltip */ + nk_layout_row_static(ctx, 30, 150, 1); + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Hover me for tooltip", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) + nk_tooltip(ctx, "This is a tooltip"); + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Layout", NK_MINIMIZED)) + { + if (nk_tree_push(ctx, NK_TREE_NODE, "Widget", NK_MINIMIZED)) + { + float ratio_two[] = {0.2f, 0.6f, 0.2f}; + float width_two[] = {100, 200, 50}; + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Dynamic fixed column layout with generated position and size:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 30, 3); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "static fixed column layout with generated position and size:", NK_TEXT_LEFT); + nk_layout_row_static(ctx, 30, 100, 3); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Dynamic array-based custom column layout with generated position and custom size:",NK_TEXT_LEFT); + nk_layout_row(ctx, NK_DYNAMIC, 30, 3, ratio_two); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Static array-based custom column layout with generated position and custom size:",NK_TEXT_LEFT ); + nk_layout_row(ctx, NK_STATIC, 30, 3, width_two); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Dynamic immediate mode custom column layout with generated position and custom size:",NK_TEXT_LEFT); + nk_layout_row_begin(ctx, NK_DYNAMIC, 30, 3); + nk_layout_row_push(ctx, 0.2f); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_row_push(ctx, 0.6f); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_row_push(ctx, 0.2f); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_row_end(ctx); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Static immmediate mode custom column layout with generated position and custom size:", NK_TEXT_LEFT); + nk_layout_row_begin(ctx, NK_STATIC, 30, 3); + nk_layout_row_push(ctx, 100); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_row_push(ctx, 200); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_row_push(ctx, 50); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_row_end(ctx); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Static free space with custom position and custom size:", NK_TEXT_LEFT); + nk_layout_space_begin(ctx, NK_STATIC, 120, 4); + nk_layout_space_push(ctx, nk_rect(100, 0, 100, 30)); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_space_push(ctx, nk_rect(0, 15, 100, 30)); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_space_push(ctx, nk_rect(200, 15, 100, 30)); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_space_push(ctx, nk_rect(100, 30, 100, 30)); + nk_button_label(ctx, "button", NK_BUTTON_DEFAULT); + nk_layout_space_end(ctx); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Group", NK_MINIMIZED)) + { + static int group_titlebar = nk_false; + static int group_border = nk_true; + static int group_no_scrollbar = nk_false; + static int group_width = 320; + static int group_height = 200; + struct nk_panel tab; + + nk_flags group_flags = 0; + if (group_border) group_flags |= NK_WINDOW_BORDER; + if (group_no_scrollbar) group_flags |= NK_WINDOW_NO_SCROLLBAR; + if (group_titlebar) group_flags |= NK_WINDOW_TITLE; + + nk_layout_row_dynamic(ctx, 30, 3); + nk_checkbox_label(ctx, "Titlebar", &group_titlebar); + nk_checkbox_label(ctx, "Border", &group_border); + nk_checkbox_label(ctx, "No Scrollbar", &group_no_scrollbar); + + nk_layout_row_begin(ctx, NK_STATIC, 22, 2); + nk_layout_row_push(ctx, 50); + nk_label(ctx, "size:", NK_TEXT_LEFT); + nk_layout_row_push(ctx, 130); + nk_property_int(ctx, "#Width:", 100, &group_width, 500, 10, 1); + nk_layout_row_push(ctx, 130); + nk_property_int(ctx, "#Height:", 100, &group_height, 500, 10, 1); + nk_layout_row_end(ctx); + + nk_layout_row_static(ctx, (float)group_height, group_width, 2); + if (nk_group_begin(ctx, &tab, "Group", group_flags)) { + int i = 0; + static int selected[16]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 16; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Simple", NK_MINIMIZED)) + { + struct nk_panel tab; + nk_layout_row_dynamic(ctx, 300, 2); + if (nk_group_begin(ctx, &tab, "Group_Without_Border", 0)) { + int i = 0; + char buffer[64]; + nk_layout_row_static(ctx, 18, 150, 1); + for (i = 0; i < 64; ++i) { + sprintf(buffer, "0x%02x", i); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: scrollable region", buffer); + } + nk_group_end(ctx); + } + if (nk_group_begin(ctx, &tab, "Group_With_Border", NK_WINDOW_BORDER)) { + int i = 0; + char buffer[64]; + nk_layout_row_dynamic(ctx, 25, 2); + for (i = 0; i < 64; ++i) { + sprintf(buffer, "%08d", ((((i%7)*10)^32))+(64+(i%2)*2)); + nk_button_label(ctx, buffer, NK_BUTTON_DEFAULT); + } + nk_group_end(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Complex", NK_MINIMIZED)) + { + int i; + struct nk_panel tab; + nk_layout_space_begin(ctx, NK_STATIC, 500, 64); + nk_layout_space_push(ctx, nk_rect(0,0,150,500)); + if (nk_group_begin(ctx, &tab, "Group_left", NK_WINDOW_BORDER)) { + static int selected[32]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 32; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(160,0,150,240)); + if (nk_group_begin(ctx, &tab, "Group_top", NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(160,250,150,250)); + if (nk_group_begin(ctx, &tab, "Group_buttom", NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(320,0,150,150)); + if (nk_group_begin(ctx, &tab, "Group_right_top", NK_WINDOW_BORDER)) { + static int selected[4]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 4; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(320,160,150,150)); + if (nk_group_begin(ctx, &tab, "Group_right_center", NK_WINDOW_BORDER)) { + static int selected[4]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 4; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(320,320,150,150)); + if (nk_group_begin(ctx, &tab, "Group_right_bottom", NK_WINDOW_BORDER)) { + static int selected[4]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 4; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + nk_layout_space_end(ctx); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Splitter", NK_MINIMIZED)) + { + const struct nk_input *in = &ctx->input; + nk_layout_row_static(ctx, 20, 320, 1); + nk_label(ctx, "Use slider and spinner to change tile size", NK_TEXT_LEFT); + nk_label(ctx, "Drag the space between tiles to change tile ratio", NK_TEXT_LEFT); + + if (nk_tree_push(ctx, NK_TREE_NODE, "Vertical", NK_MINIMIZED)) + { + static float a = 100, b = 100, c = 100; + struct nk_rect bounds; + struct nk_panel sub; + + float row_layout[5]; + row_layout[0] = a; + row_layout[1] = 8; + row_layout[2] = b; + row_layout[3] = 8; + row_layout[4] = c; + + /* header */ + nk_layout_row_static(ctx, 30, 100, 2); + nk_label(ctx, "left:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &a, 200.0f, 10.0f); + + nk_label(ctx, "middle:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &b, 200.0f, 10.0f); + + nk_label(ctx, "right:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &c, 200.0f, 10.0f); + + /* tiles */ + nk_layout_row(ctx, NK_STATIC, 200, 5, row_layout); + + /* left space */ + if (nk_group_begin(ctx, &sub, "left", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + + /* scaler */ + bounds = nk_widget_bounds(ctx); + nk_spacing(ctx, 1); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + a = row_layout[0] + in->mouse.delta.x; + b = row_layout[2] - in->mouse.delta.x; + } + + /* middle space */ + if (nk_group_begin(ctx, &sub, "center", NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + + /* scaler */ + bounds = nk_widget_bounds(ctx); + nk_spacing(ctx, 1); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + b = (row_layout[2] + in->mouse.delta.x); + c = (row_layout[4] - in->mouse.delta.x); + } + + /* right space */ + if (nk_group_begin(ctx, &sub, "right", NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Horizontal", NK_MINIMIZED)) + { + static float a = 100, b = 100, c = 100; + struct nk_panel sub; + struct nk_rect bounds; + + /* header */ + nk_layout_row_static(ctx, 30, 100, 2); + nk_label(ctx, "top:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &a, 200.0f, 10.0f); + + nk_label(ctx, "middle:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &b, 200.0f, 10.0f); + + nk_label(ctx, "bottom:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &c, 200.0f, 10.0f); + + /* top space */ + nk_layout_row_dynamic(ctx, a, 1); + if (nk_group_begin(ctx, &sub, "top", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 3); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + + /* scaler */ + nk_layout_row_dynamic(ctx, 8, 1); + bounds = nk_widget_bounds(ctx); + nk_spacing(ctx, 1); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + a = a + in->mouse.delta.y; + b = b - in->mouse.delta.y; + } + + /* middle space */ + nk_layout_row_dynamic(ctx, b, 1); + if (nk_group_begin(ctx, &sub, "middle", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 3); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + + { + /* scaler */ + nk_layout_row_dynamic(ctx, 8, 1); + bounds = nk_widget_bounds(ctx); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + b = b + in->mouse.delta.y; + c = c - in->mouse.delta.y; + } + } + + /* bottom space */ + nk_layout_row_dynamic(ctx, c, 1); + if (nk_group_begin(ctx, &sub, "bottom", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 3); + nk_button_label(ctx, "#FFAA", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFBB", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFCC", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFDD", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFEE", NK_BUTTON_DEFAULT); + nk_button_label(ctx, "#FFFF", NK_BUTTON_DEFAULT); + nk_group_end(ctx); + } + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + } + nk_end(ctx); + return !nk_window_is_closed(ctx, "Demo"); +} + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct 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; +}; + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + "#version 300 es\n" + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "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 = + "#version 300 es\n" + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + 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_draw_vertex); + size_t vp = offsetof(struct nk_draw_vertex, position); + size_t vt = offsetof(struct nk_draw_vertex, uv); + size_t vc = offsetof(struct nk_draw_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); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + 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); +} + +static void +device_shutdown(struct device *dev) +{ + 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); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + enum nk_anti_aliasing AA) +{ + GLint last_prog, last_tex; + GLint last_ebo, last_vbo, last_vao; + 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)width; + ortho[1][1] /= (GLfloat)height; + + /* save previous opengl state */ + glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo); + + /* 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]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + 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_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill converting configuration */ + struct nk_convert_config config; + memset(&config, 0, sizeof(config)); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.null = dev->null; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor((GLint)cmd->clip_rect.x, + height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h), + (GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* restore old state */ + glUseProgram((GLuint)last_prog); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo); + glBindVertexArray((GLuint)last_vao); + glDisable(GL_SCISSOR_TEST); +} + + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + + /* GUI */ + struct device device; + struct nk_context ctx; + struct nk_font *font; + struct nk_font_atlas atlas; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + {/* GUI */ + device_init(&device); + {const void *image; int w, h; + const char *font_path = (argc > 1) ? argv[1]: 0; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL); + else font = nk_font_atlas_add_default(&atlas, 14.0f, NULL); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);} + nk_init_default(&ctx, &font->handle);} + + while (!glfwWindowShouldClose(win)) + { + /* Input */ + {double x, y; + nk_input_begin(&ctx); + glfwPollEvents(); + 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_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_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL)) { + 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_P) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_SHIFT, 1); + } else { + 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); + } + glfwGetCursorPos(win, &x, &y); + nk_input_motion(&ctx, (int)x, (int)y); + 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_end(&ctx);} + + /* GUI */ + if (!overview_window(&ctx)) break; + + /* Draw */ + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + device_draw(&device, &ctx, width, height, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + } + + nk_font_atlas_clear(&atlas); + nk_free(&ctx); + device_shutdown(&device); + glfwTerminate(); + return 0; +} + diff --git a/demo/stb_image.h b/example/stb_image.h similarity index 100% rename from demo/stb_image.h rename to example/stb_image.h diff --git a/example/style.c b/example/style.c new file mode 100644 index 0000000..8cea152 --- /dev/null +++ b/example/style.c @@ -0,0 +1,132 @@ +enum theme {THEME_BLACK, THEME_WHITE, THEME_RED, THEME_BLUE, THEME_DARK}; + +void +set_style(struct nk_context *ctx, enum theme theme) +{ + struct nk_color table[NK_COLOR_COUNT]; + if (theme == THEME_WHITE) { + table[NK_COLOR_TEXT] = nk_rgba(70, 70, 70, 255); + table[NK_COLOR_WINDOW] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_HEADER] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_BORDER] = nk_rgba(0, 0, 0, 255); + table[NK_COLOR_BUTTON] = nk_rgba(185, 185, 185, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(170, 170, 170, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(120, 120, 120, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_SELECT] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_SLIDER] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(80, 80, 80, 255); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(70, 70, 70, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(60, 60, 60, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_EDIT] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(0, 0, 0, 255); + table[NK_COLOR_COMBO] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_CHART] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(45, 45, 45, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(180, 180, 180, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(140, 140, 140, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(180, 180, 180, 255); + nk_style_from_table(ctx, table); + } else if (theme == THEME_RED) { + table[NK_COLOR_TEXT] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_WINDOW] = nk_rgba(30, 33, 40, 215); + table[NK_COLOR_HEADER] = nk_rgba(181, 45, 69, 220); + table[NK_COLOR_BORDER] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_BUTTON] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(190, 50, 70, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(195, 55, 75, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 60, 60, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SELECT] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SLIDER] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(186, 50, 74, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(191, 55, 79, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_EDIT] = nk_rgba(51, 55, 67, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_COMBO] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_CHART] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(170, 40, 60, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(30, 33, 40, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(181, 45, 69, 220); + nk_style_from_table(ctx, table); + } else if (theme == THEME_BLUE) { + table[NK_COLOR_TEXT] = nk_rgba(20, 20, 20, 255); + table[NK_COLOR_WINDOW] = nk_rgba(202, 212, 214, 215); + table[NK_COLOR_HEADER] = nk_rgba(137, 182, 224, 220); + table[NK_COLOR_BORDER] = nk_rgba(140, 159, 173, 255); + table[NK_COLOR_BUTTON] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(142, 187, 229, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(147, 192, 234, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(182, 215, 215, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_SELECT] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_SLIDER] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(137, 182, 224, 245); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(142, 188, 229, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(147, 193, 234, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_EDIT] = nk_rgba(210, 210, 210, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(20, 20, 20, 255); + table[NK_COLOR_COMBO] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_CHART] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(190, 200, 200, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(156, 193, 220, 255); + nk_style_from_table(ctx, table); + } else if (theme == THEME_DARK) { + table[NK_COLOR_TEXT] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_WINDOW] = nk_rgba(57, 67, 71, 215); + table[NK_COLOR_HEADER] = nk_rgba(51, 51, 56, 220); + table[NK_COLOR_BORDER] = nk_rgba(46, 46, 46, 255); + table[NK_COLOR_BUTTON] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(63, 98, 126, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 53, 56, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SELECT] = nk_rgba(57, 67, 61, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SLIDER] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(48, 83, 111, 245); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_EDIT] = nk_rgba(50, 58, 61, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_COMBO] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_CHART] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba(255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(48, 83, 111, 255); + nk_style_from_table(ctx, table); + } else { + nk_style_default(ctx); + } +} + + diff --git a/extra_font/Cousine-Regular.ttf b/extra_font/Cousine-Regular.ttf new file mode 100644 index 0000000..70a0bf9 Binary files /dev/null and b/extra_font/Cousine-Regular.ttf differ diff --git a/font/DroidSans.ttf b/extra_font/DroidSans.ttf similarity index 100% rename from font/DroidSans.ttf rename to extra_font/DroidSans.ttf diff --git a/extra_font/Karla-Regular.ttf b/extra_font/Karla-Regular.ttf new file mode 100644 index 0000000..81b3de6 Binary files /dev/null and b/extra_font/Karla-Regular.ttf differ diff --git a/extra_font/ProggyClean.ttf b/extra_font/ProggyClean.ttf new file mode 100644 index 0000000..0270cdf Binary files /dev/null and b/extra_font/ProggyClean.ttf differ diff --git a/extra_font/ProggyTiny.ttf b/extra_font/ProggyTiny.ttf new file mode 100644 index 0000000..1c4312c Binary files /dev/null and b/extra_font/ProggyTiny.ttf differ diff --git a/extra_font/Raleway-Bold.ttf b/extra_font/Raleway-Bold.ttf new file mode 100644 index 0000000..7aa37f0 Binary files /dev/null and b/extra_font/Raleway-Bold.ttf differ diff --git a/font/Roboto-Bold.ttf b/extra_font/Roboto-Bold.ttf similarity index 100% rename from font/Roboto-Bold.ttf rename to extra_font/Roboto-Bold.ttf diff --git a/font/Roboto-Light.ttf b/extra_font/Roboto-Light.ttf similarity index 100% rename from font/Roboto-Light.ttf rename to extra_font/Roboto-Light.ttf diff --git a/font/Roboto-Regular.ttf b/extra_font/Roboto-Regular.ttf similarity index 100% rename from font/Roboto-Regular.ttf rename to extra_font/Roboto-Regular.ttf diff --git a/extra_font/kenvector_future.ttf b/extra_font/kenvector_future.ttf new file mode 100644 index 0000000..39ebdfa Binary files /dev/null and b/extra_font/kenvector_future.ttf differ diff --git a/extra_font/kenvector_future_thin.ttf b/extra_font/kenvector_future_thin.ttf new file mode 100644 index 0000000..9f4b4fa Binary files /dev/null and b/extra_font/kenvector_future_thin.ttf differ diff --git a/nuklear.h b/nuklear.h new file mode 100644 index 0000000..6ed335c --- /dev/null +++ b/nuklear.h @@ -0,0 +1,19513 @@ +/* + Nuklear - v1.00 - public domain + no warrenty implied; use at your own risk. + authored from 2015-2016 by Micha Mettke + +ABOUT: + This is a minimal state immediate mode graphical user interface single header + toolkit written in ANSI C and licensed under public domain. + It was designed as a simple embeddable user interface for application and does + not have any dependencies, a default renderbackend or OS window and input handling + but instead provides a very modular library approach by using simple input state + for input and draw commands describing primitive shapes as output. + So instead of providing a layered library that tries to abstract over a number + of platform and render backends it only focuses on the actual UI. + +VALUES: + - Immediate mode graphical user interface toolkit + - Single header library + - Written in C89 (ANSI C) + - Small codebase (~15kLOC) + - Focus on portability, efficiency and simplicity + - No dependencies (not even the standard library if not wanted) + - Fully skinnable and customizable + - Low memory footprint with total memory control if needed or wanted + - UTF-8 support + - No global or hidden state + - Customizeable library modules (you can compile and use only what you need) + - Optional font baker and vertex buffer output + +USAGE: + This library is self contained in one single header file and can be used either + in header only mode or in implementation mode. The header only mode is used + by default when included and allows including this header in other headers + and does not contain the actual implementation. + + The implementation mode requires to define the preprocessor macro + NK_IMPLEMENTATION in *one* .c/.cpp file before #includeing this file, e.g.: + + #define NK_IMPLEMENTATION + #include "nuklear.h" + + Also optionally define the symbols listed in the section "OPTIONAL DEFINES" + below in implemenation mode if you want to use additional functionality + or need more control over the library. + +FEATURES: + - Absolutly no platform dependend code + - Memory management control ranging from/to + - Ease of use by allocating everything from the standard library + - Control every byte of memory inside the library + - Font handling control ranging from/to + - Use your own font implementation to draw shapes/vertexes + - Use this libraries internal font baking and handling API + - Drawing output control ranging from/to + - Simple shapes for more high level APIs which already having drawing capabilities + - Hardware accessable anti-aliased vertex buffer output + - Customizeable colors and properties ranging from/to + - Simple changes to color by filling a simple color table + - Complete control with ability to use skinning to decorate widgets + - Bendable UI library with widget ranging from/to + - Basic widgets like buttons, checkboxes, slider, ... + - Advanced widget like abstract comboboxes, contextual menus,... + - Compile time configuration to only compile what you need + - Subset which can be used if you do not want to link or use the standard library + - Can be easily modified only update on user input instead of frame updates + +OPTIONAL DEFINES: + NK_PRIVATE + If defined declares all functions as static, so they can only be accessed + for the file that creates the implementation + + NK_INCLUDE_FIXED_TYPES + If defined it will include header for fixed sized types + otherwise you have to select the correct types. + + NK_INCLUDE_DEFAULT_ALLOCATOR + if defined it will include header and provide additional functions + to use this library without caring for memory allocation control and therefore + ease memory management. + IMPORTANT: this adds the standard libary with malloc and free so don't define + if you don't want to link to the standard library! + + NK_INCLUDE_STANDARD_IO + if defined it will include header and and provide + additional functions depending on file loading and variable arguments + IMPORTANT: this adds the standard libary with fopen,fclose,... + as well as va_list,... so don't define this + if you don't want to link to the standard library! + + NK_INCLUDE_VERTEX_BUFFER_OUTPUT + Defining this adds a vertex draw command list backend to this + library, which allows you to convert queue commands into vertex draw commands. + This is mainly if you need a harware accessable format for OpenGL, DirectX, + Vulkan, Metal,... + + NK_INCLUDE_FONT_BAKING + Defining this adds the `stb_truetype` and `stb_rect_pack` implementation + to this library and provides a default font for font loading and rendering. + If you already have font handling or do not want to use this font handler + you don't have to define it. + + NK_INCLUDE_DEFAULT_FONT + Defining this adds the default font: ProggyClean.ttf font into this library + which can be loaded into a font atlas and allows using this library without + having a truetype font + IMPORTANT: enableing this adds ~12kb to global stack memory + + NK_INCLUDE_COMMAND_USERDATA + Defining this adds a userdata pointer into each command. Can be usefull for + example if you want to provide custom shader depending on the used widget. + Can be combined with the style structures. + + NK_ASSERT + If you don't define this, nuklear will use with assert(). + IMPORTANT: it also adds the standard library so define to nothing of not wanted! + + NK_BUFFER_DEFAULT_INITIAL_SIZE + Initial buffer size allocated by all buffers while using the default allocator + functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't + want to allocate the default 4k memory then redefine it. + + NK_MAX_NUMBER_BUFFER + Maximum buffer size for the conversion buffer between float and string + Under normal circumstances this should be more than sufficient. + + NK_INPUT_MAX + Defines the max number of bytes which can be added as text input in one frame. + Under normal circumstances this should be more than sufficient. + + NK_MEMSET + You can define this to 'memset' or your own memset implementation + replacement. If not nuklear will use its own version. + + NK_MEMCOPY + You can define this to 'memcpy' or your own memset implementation + replacement. If not nuklear will use its own version. + + NK_SQRT + You can define this to 'sqrt' or your own sqrt implementation + replacement. If not nuklear will use its own slow and not highly + accurate version. + + NK_SIN + You can define this to 'sinf' or your own sine implementation + replacement. If not nuklear will use its own approximation implementation. + + NK_COS + You can define this to 'cosf' or your own cosine implementation + replacement. If not nuklear will use its own approximation implementation. + + NK_BYTE + NK_INT16 + NK_UINT16 + NK_INT32 + NK_UINT32 + NK_SIZE_TYPE + NK_POINTER_TYPE + If you compile without NK_USE_FIXED_TYPE then a number of standard types + will be selected and compile time validated. If they are incorrect you can + define the correct types. + +CREDITS: + Developed by Micha Mettke and every direct or indirect contributor to the GitHub. + + Embeds stb_texedit, stb_truetype and stb_rectpack by Sean Barret (public domain) + Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license). + + Big thank you to Omar Cornut (ocornut@github) for his imgui library and + giving me the inspiration for this library, Casey Muratori for handmade hero + and his original immediate mode graphical user interface idea and Sean + Barret for his amazing single header libraries which restored by faith + in libraries and brought me to create some of my own. + +LICENSE: + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish and distribute this file as you see fit. +*/ +#ifndef NK_H_ +#define NK_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/* + * ============================================================== + * + * CONSTANTS + * + * =============================================================== + */ +#define NK_UTF_INVALID 0xFFFD /* internal invalid utf8 rune */ +#define NK_UTF_SIZE 4 /* describes the number of bytes a glyph consists of*/ +#ifndef NK_INPUT_MAX +#define NK_INPUT_MAX 16 +#endif +#ifndef NK_MAX_NUMBER_BUFFER +#define NK_MAX_NUMBER_BUFFER 64 +#endif +/* + * =============================================================== + * + * BASIC + * + * =============================================================== + */ +#ifdef NK_INCLUDE_FIXED_TYPES +#include +typedef int16_t nk_short; +typedef uint16_t nk_ushort; +typedef int32_t nk_int; +typedef uint32_t nk_uint; +typedef uint32_t nk_hash; +typedef uintptr_t nk_size; +typedef uintptr_t nk_ptr; +typedef uint32_t nk_flags; +typedef uint32_t nk_rune; +typedef uint8_t nk_byte; +#else +#ifndef NK_BYTE +typedef unsigned char nk_byte; +#else +typedef NK_BYTE nk_byte; +#endif +#ifndef NK_INT16 +typedef short nk_short; +#else +typedef NK_INT16 nk_short; +#endif +#ifndef NK_UINT16 +typedef unsigned short nk_ushort; +#else +typedef NK_UINT16 nk_ushort; +#endif +#ifndef NK_INT32 +typedef short nk_int; +#else +typedef NK_INT32 nk_int; +#endif +#ifndef NK_UINT32 +typedef unsigned short nk_uint; +#else +typedef NK_UINT32 nk_uint; +#endif +#ifndef NK_SIZE_TYPE +typedef unsigned long nk_size; +#else +typedef NK_SIZE_TYPE nk_byte; +#endif +#ifndef NK_POINTER_TYPE +typedef unsigned long nk_size; +#else +typedef NK_POINTER_TYPE nk_byte; +#endif +typedef unsigned int nk_hash; +typedef unsigned int nk_flags; +typedef nk_uint nk_rune; +typedef unsigned char nk_byte; +#endif + +#ifdef NK_PRIVATE +#define NK_API static +#else +#define NK_API extern +#endif + +#define NK_INTERN static +#define NK_STORAGE static +#define NK_GLOBAL static + +/* ============================================================================ + * + * API + * + * =========================================================================== */ +#define NK_UNDEFINED (-1.0f) +#define NK_FLAG(x) (1 << (x)) + +struct nk_buffer; +struct nk_allocator; +struct nk_command_buffer; +struct nk_draw_command; +struct nk_convert_config; +struct nk_text_edit; +struct nk_draw_list; +struct nk_user_font; +struct nk_panel; +struct nk_context; + +enum {nk_false, nk_true}; +struct nk_color {nk_byte r,g,b,a;}; +struct nk_vec2 {float x,y;}; +struct nk_vec2i {short x, y;}; +struct nk_rect {float x,y,w,h;}; +struct nk_recti {short x,y,w,h;}; +typedef char nk_glyph[NK_UTF_SIZE]; +typedef union {void *ptr; int id;} nk_handle; +struct nk_image {nk_handle handle;unsigned short w,h;unsigned short region[4];}; +struct nk_scroll {unsigned short x, y;}; +enum nk_heading {NK_UP, NK_RIGHT, NK_DOWN, NK_LEFT}; + +typedef int(*nk_filter)(const struct nk_text_edit*, nk_rune unicode); +typedef void(*nk_paste_f)(nk_handle, struct nk_text_edit*); +typedef void(*nk_copy_f)(nk_handle, const char*, int len); + +enum nk_button_behavior {NK_BUTTON_DEFAULT,NK_BUTTON_REPEATER}; +enum nk_modify {NK_FIXED=nk_false,NK_MODIFIABLE=nk_true}; +enum nk_orientation {NK_VERTICAL,NK_HORIZONTAL}; +enum nk_collapse_states {NK_MINIMIZED=nk_false,NK_MAXIMIZED = nk_true}; +enum nk_show_states {NK_HIDDEN=nk_false,NK_SHOWN=nk_true}; +enum nk_chart_type {NK_CHART_LINES,NK_CHART_COLUMN,NK_CHART_MAX}; +enum nk_chart_event {NK_CHART_HOVERING=0x01, NK_CHART_CLICKED=0x02}; +enum nk_color_format {NK_RGB, NK_RGBA}; +enum nk_popup_type {NK_POPUP_STATIC,NK_POPUP_DYNAMIC}; +enum nk_layout_format {NK_DYNAMIC,NK_STATIC}; +enum nk_tree_type {NK_TREE_NODE,NK_TREE_TAB}; +enum nk_anti_aliasing {NK_ANTI_ALIASING_OFF,NK_ANTI_ALIASING_ON}; + +struct nk_allocator { + nk_handle userdata; + void*(*alloc)(nk_handle, void *old, nk_size); + void(*free)(nk_handle, void*); +}; + +struct nk_draw_null_texture { + nk_handle texture;/* texture handle to a texture with a white pixel */ + struct nk_vec2 uv; /* coordinates to a white pixel in the texture */ +}; +struct nk_convert_config { + float global_alpha; /* global alpha value */ + enum nk_anti_aliasing line_AA; /* line anti-aliasing flag can be turned off if you are thight on memory */ + enum nk_anti_aliasing shape_AA; /* shape anti-aliasing flag can be turned off if you are thight on memory */ + unsigned int circle_segment_count; /* number of segments used for circles: default to 22 */ + unsigned int arc_segment_count; /* number of segments used for arcs: default to 22 */ + unsigned int curve_segment_count; /* number of segments used for curves: default to 22 */ + struct nk_draw_null_texture null; /* handle to texture with a white pixel for shape drawing */ +}; + +enum nk_symbol_type { + NK_SYMBOL_NONE, + NK_SYMBOL_X, + NK_SYMBOL_UNDERSCORE, + NK_SYMBOL_CIRCLE, + NK_SYMBOL_CIRCLE_FILLED, + NK_SYMBOL_RECT, + NK_SYMBOL_RECT_FILLED, + NK_SYMBOL_TRIANGLE_UP, + NK_SYMBOL_TRIANGLE_DOWN, + NK_SYMBOL_TRIANGLE_LEFT, + NK_SYMBOL_TRIANGLE_RIGHT, + NK_SYMBOL_PLUS, + NK_SYMBOL_MINUS, + NK_SYMBOL_MAX +}; + +enum nk_keys { + NK_KEY_NONE, + NK_KEY_SHIFT, + NK_KEY_CTRL, + NK_KEY_DEL, + NK_KEY_ENTER, + NK_KEY_TAB, + NK_KEY_BACKSPACE, + NK_KEY_COPY, + NK_KEY_CUT, + NK_KEY_PASTE, + NK_KEY_UP, + NK_KEY_DOWN, + NK_KEY_LEFT, + NK_KEY_RIGHT, + NK_KEY_TEXT_INSERT_MODE, + NK_KEY_TEXT_LINE_START, + NK_KEY_TEXT_LINE_END, + NK_KEY_TEXT_START, + NK_KEY_TEXT_END, + NK_KEY_TEXT_UNDO, + NK_KEY_TEXT_REDO, + NK_KEY_TEXT_WORD_LEFT, + NK_KEY_TEXT_WORD_RIGHT, + NK_KEY_MAX +}; + +enum nk_buttons { + NK_BUTTON_LEFT, + NK_BUTTON_MIDDLE, + NK_BUTTON_RIGHT, + NK_BUTTON_MAX +}; + +enum nk_style_colors { + NK_COLOR_TEXT, + NK_COLOR_WINDOW, + NK_COLOR_HEADER, + NK_COLOR_BORDER, + NK_COLOR_BUTTON, + NK_COLOR_BUTTON_HOVER, + NK_COLOR_BUTTON_ACTIVE, + NK_COLOR_TOGGLE, + NK_COLOR_TOGGLE_HOVER, + NK_COLOR_TOGGLE_CURSOR, + NK_COLOR_SELECT, + NK_COLOR_SELECT_ACTIVE, + NK_COLOR_SLIDER, + NK_COLOR_SLIDER_CURSOR, + NK_COLOR_SLIDER_CURSOR_HOVER, + NK_COLOR_SLIDER_CURSOR_ACTIVE, + NK_COLOR_PROPERTY, + NK_COLOR_EDIT, + NK_COLOR_EDIT_CURSOR, + NK_COLOR_COMBO, + NK_COLOR_CHART, + NK_COLOR_CHART_COLOR, + NK_COLOR_CHART_COLOR_HIGHLIGHT, + NK_COLOR_SCROLLBAR, + NK_COLOR_SCROLLBAR_CURSOR, + NK_COLOR_SCROLLBAR_CURSOR_HOVER, + NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, + NK_COLOR_TAB_HEADER, + NK_COLOR_COUNT +}; + +enum nk_widget_layout_states { + NK_WIDGET_INVALID, /* The widget cannot be seen and is completly out of view */ + NK_WIDGET_VALID, /* The widget is completly inside the window and can be updated and drawn */ + NK_WIDGET_ROM /* The widget is partially visible and cannot be updated */ +}; + +/* widget states */ +enum nk_widget_states { + NK_WIDGET_STATE_INACTIVE = NK_FLAG(0), /* widget is neither active nor hovered */ + NK_WIDGET_STATE_ENTERED = NK_FLAG(1), /* widget has been hovered on the current frame */ + NK_WIDGET_STATE_HOVERED = NK_FLAG(2), /* widget is being hovered */ + NK_WIDGET_STATE_LEFT = NK_FLAG(3), /* widget is from this frame on not hovered anymore */ + NK_WIDGET_STATE_ACTIVE = NK_FLAG(4) /* widget is currently activated */ +}; + +/* text alignment */ +enum nk_text_align { + NK_TEXT_ALIGN_LEFT = 0x01, + NK_TEXT_ALIGN_CENTERED = 0x02, + NK_TEXT_ALIGN_RIGHT = 0x04, + NK_TEXT_ALIGN_TOP = 0x08, + NK_TEXT_ALIGN_MIDDLE = 0x10, + NK_TEXT_ALIGN_BOTTOM = 0x20 +}; +enum nk_text_alignment { + NK_TEXT_LEFT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_LEFT, + NK_TEXT_CENTERED = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_CENTERED, + NK_TEXT_RIGHT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_RIGHT +}; + +enum nk_edit_flags { + NK_EDIT_DEFAULT = 0, + NK_EDIT_READ_ONLY = NK_FLAG(0), + NK_EDIT_AUTO_SELECT = NK_FLAG(1), + NK_EDIT_SIG_ENTER = NK_FLAG(2), + NK_EDIT_ALLOW_TAB = NK_FLAG(3), + NK_EDIT_NO_CURSOR = NK_FLAG(4), + NK_EDIT_SELECTABLE = NK_FLAG(5), + NK_EDIT_CLIPBOARD = NK_FLAG(6), + NK_EDIT_CTRL_ENTER_NEWLINE = NK_FLAG(7), + NK_EDIT_NO_HORIZONTAL_SCROLL = NK_FLAG(8), + NK_EDIT_ALWAYS_INSERT_MODE = NK_FLAG(9), + NK_EDIT_MULTILINE = NK_FLAG(11) +}; +enum nk_edit_types { + NK_EDIT_SIMPLE = NK_EDIT_ALWAYS_INSERT_MODE, + NK_EDIT_FIELD = NK_EDIT_SIMPLE|NK_EDIT_SELECTABLE, + NK_EDIT_BOX = NK_EDIT_ALWAYS_INSERT_MODE| NK_EDIT_SELECTABLE| + NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB +}; +enum nk_edit_events { + NK_EDIT_ACTIVE = NK_FLAG(0), /* edit widget is currently being modified */ + NK_EDIT_INACTIVE = NK_FLAG(1), /* edit widget is not active and is not being modified */ + NK_EDIT_ACTIVATED = NK_FLAG(2), /* edit widget went from state inactive to state active */ + NK_EDIT_DEACTIVATED = NK_FLAG(3), /* edit widget went from state active to state inactive */ + NK_EDIT_COMMITED = NK_FLAG(4) /* edit widget has received an enter and lost focus */ +}; + +enum nk_panel_flags { + NK_WINDOW_BORDER = NK_FLAG(0), /* Draws a border around the window to visually seperate the window * from the background */ + NK_WINDOW_BORDER_HEADER = NK_FLAG(1), /* Draws a border between window header and body */ + NK_WINDOW_MOVABLE = NK_FLAG(2), /* The moveable flag inidicates that a window can be moved by user input or * by dragging the window header */ + NK_WINDOW_SCALABLE = NK_FLAG(3), /* The scaleable flag indicates that a window can be scaled by user input * by dragging a scaler icon at the button of the window */ + NK_WINDOW_CLOSABLE = NK_FLAG(4), /* adds a closeable icon into the header */ + NK_WINDOW_MINIMIZABLE = NK_FLAG(5), /* adds a minimize icon into the header */ + NK_WINDOW_DYNAMIC = NK_FLAG(6), /* special window type growing up in height while being filled to a * certain maximum height */ + NK_WINDOW_NO_SCROLLBAR = NK_FLAG(7), /* Removes the scrollbar from the window */ + NK_WINDOW_TITLE = NK_FLAG(8) /* Forces a header at the top at the window showing the title */ +}; + +/* context */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API int nk_init_default(struct nk_context*, const struct nk_user_font*); +#endif +NK_API int nk_init_fixed(struct nk_context*, void *memory, nk_size size, const struct nk_user_font*); +NK_API int nk_init_custom(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font*); +NK_API int nk_init(struct nk_context*, struct nk_allocator*, const struct nk_user_font*); +NK_API void nk_clear(struct nk_context*); +NK_API void nk_free(struct nk_context*); +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void nk_set_user_data(struct nk_context*, nk_handle handle); +#endif + +/* window */ +NK_API int nk_begin(struct nk_context*, struct nk_panel*, const char *title, struct nk_rect bounds, nk_flags flags); +NK_API void nk_end(struct nk_context*); + +NK_API struct nk_window* nk_window_find(struct nk_context *ctx, const char *name); +NK_API struct nk_rect nk_window_get_bounds(const struct nk_context*); +NK_API struct nk_vec2 nk_window_get_position(const struct nk_context*); +NK_API struct nk_vec2 nk_window_get_size(const struct nk_context*); +NK_API float nk_window_get_width(const struct nk_context*); +NK_API float nk_window_get_height(const struct nk_context*); +NK_API struct nk_panel* nk_window_get_panel(struct nk_context*); +NK_API struct nk_rect nk_window_get_content_region(struct nk_context*); +NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context*); +NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context*); +NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context*); +NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context*); + +NK_API int nk_window_has_focus(const struct nk_context*); +NK_API int nk_window_is_collapsed(struct nk_context*, const char*); +NK_API int nk_window_is_closed(struct nk_context*, const char*); +NK_API int nk_window_is_active(struct nk_context*, const char*); +NK_API int nk_window_is_hovered(struct nk_context*); +NK_API int nk_window_is_any_hovered(struct nk_context*); + +NK_API void nk_window_set_bounds(struct nk_context*, struct nk_rect); +NK_API void nk_window_set_position(struct nk_context*, struct nk_vec2); +NK_API void nk_window_set_size(struct nk_context*, struct nk_vec2); +NK_API void nk_window_set_focus(struct nk_context*, const char *name); + +NK_API void nk_window_close(struct nk_context *ctx, const char *name); +NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states); +NK_API void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); +NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); +NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); + +/* Layout */ +NK_API void nk_layout_row_dynamic(struct nk_context*, float height, int cols); +NK_API void nk_layout_row_static(struct nk_context*, float height, int item_width, int cols); + +NK_API void nk_layout_row_begin(struct nk_context*, enum nk_layout_format, float row_height, int cols); +NK_API void nk_layout_row_push(struct nk_context*, float value); +NK_API void nk_layout_row_end(struct nk_context*); +NK_API void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); + +NK_API void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); +NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect); +NK_API void nk_layout_space_end(struct nk_context*); + +NK_API struct nk_rect nk_layout_space_bounds(struct nk_context*); +NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); +NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); +NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); +NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); + +/* Layout: Group */ +NK_API int nk_group_begin(struct nk_context*, struct nk_panel*, const char *title, nk_flags); +NK_API void nk_group_end(struct nk_context*); + +/* Layout: Tree */ +NK_API int nk__tree_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash2, int seed); +#define nk_tree_push(ctx, type, title, state) nk__tree_push(ctx, type, title, state, __FILE__,__LINE__) +NK_API void nk_tree_pop(struct nk_context*); + +/* Widgets */ +NK_API void nk_text(struct nk_context*, const char*, int, nk_flags); +NK_API void nk_text_colored(struct nk_context*, const char*, int, nk_flags, struct nk_color); +NK_API void nk_text_wrap(struct nk_context*, const char*, int); +NK_API void nk_text_wrap_colored(struct nk_context*, const char*, int, struct nk_color); + +NK_API void nk_label(struct nk_context*, const char*, nk_flags); +NK_API void nk_label_colored(struct nk_context*, const char*, nk_flags align, struct nk_color); +NK_API void nk_label_wrap(struct nk_context*, const char*); +NK_API void nk_label_colored_wrap(struct nk_context*, const char*, struct nk_color); +NK_API void nk_image(struct nk_context*, struct nk_image); +#ifdef NK_INCLUDE_STANDARD_IO +NK_API void nk_labelf(struct nk_context*, nk_flags, const char*, ...); +NK_API void nk_labelf_colored(struct nk_context*, nk_flags align, struct nk_color, const char*,...); +NK_API void nk_labelf_wrap(struct nk_context*, const char*,...); +NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, const char*,...); + +NK_API void nk_value_bool(struct nk_context*, const char *prefix, int); +NK_API void nk_value_int(struct nk_context*, const char *prefix, int); +NK_API void nk_value_uint(struct nk_context*, const char *prefix, unsigned int); +NK_API void nk_value_float(struct nk_context*, const char *prefix, float); +NK_API void nk_value_color_byte(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_float(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_hex(struct nk_context*, const char *prefix, struct nk_color); +#endif + +/* Widgets: Buttons */ +NK_API int nk_button_text(struct nk_context *ctx, const char *title, int len, enum nk_button_behavior); +NK_API int nk_button_label(struct nk_context *ctx, const char *title, enum nk_button_behavior); +NK_API int nk_button_color(struct nk_context*, struct nk_color, enum nk_button_behavior); +NK_API int nk_button_symbol(struct nk_context*, enum nk_symbol_type, enum nk_button_behavior); +NK_API int nk_button_image(struct nk_context*, struct nk_image img, enum nk_button_behavior); +NK_API int nk_button_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags text_alignment, enum nk_button_behavior); +NK_API int nk_button_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment, enum nk_button_behavior); +NK_API int nk_button_image_label(struct nk_context*, struct nk_image img, const char*, nk_flags text_alignment, enum nk_button_behavior); +NK_API int nk_button_image_text(struct nk_context*, struct nk_image img, const char*, int, nk_flags alignment, enum nk_button_behavior); + +/* Widgets: Checkbox */ +NK_API int nk_check_label(struct nk_context*, const char*, int active); +NK_API int nk_check_text(struct nk_context*, const char*, int,int active); +NK_API unsigned nk_check_flags_label(struct nk_context*, const char*, unsigned int flags, unsigned int value); +NK_API unsigned nk_check_flags_text(struct nk_context*, const char*, int, unsigned int flags, unsigned int value); +NK_API int nk_checkbox_label(struct nk_context*, const char*, int *active); +NK_API int nk_checkbox_text(struct nk_context*, const char*, int, int *active); +NK_API int nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value); +NK_API int nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value); + +/* Widgets: Radio */ +NK_API int nk_radio_label(struct nk_context*, const char*, int *active); +NK_API int nk_radio_text(struct nk_context*, const char*, int, int *active); +NK_API int nk_option_label(struct nk_context*, const char*, int active); +NK_API int nk_option_text(struct nk_context*, const char*, int, int active); + +/* Widgets: Selectable */ +NK_API int nk_selectable_label(struct nk_context*, const char*, nk_flags align, int *value); +NK_API int nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, int *value); +NK_API int nk_select_label(struct nk_context*, const char*, nk_flags align, int value); +NK_API int nk_select_text(struct nk_context*, const char*, int, nk_flags align, int value); + +/* Widgets: Slider */ +NK_API float nk_slide_float(struct nk_context*, float min, float val, float max, float step); +NK_API int nk_slide_int(struct nk_context*, int min, int val, int max, int step); +NK_API int nk_slider_float(struct nk_context*, float min, float *val, float max, float step); +NK_API int nk_slider_int(struct nk_context*, int min, int *val, int max, int step); + +/* Widgets: Progressbar */ +NK_API int nk_progress(struct nk_context*, nk_size *cur, nk_size max, int modifyable); +NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, int modifyable); + +/* Widgets: Color picker */ +NK_API struct nk_color nk_color_picker(struct nk_context*, struct nk_color, enum nk_color_format); +NK_API int nk_color_pick(struct nk_context*, struct nk_color*, enum nk_color_format); + +NK_API void nk_property_float(struct nk_context *layout, const char *name, float min, float *val, float max, float step, float inc_per_pixel); +NK_API void nk_property_int(struct nk_context *layout, const char *name, int min, int *val, int max, int step, int inc_per_pixel); +NK_API float nk_propertyf(struct nk_context *layout, const char *name, float min, float val, float max, float step, float inc_per_pixel); +NK_API int nk_propertyi(struct nk_context *layout, const char *name, int min, int val, int max, int step, int inc_per_pixel); + +/* Widgets: TextEdit */ +NK_API nk_flags nk_edit_string(struct nk_context*, nk_flags, char *buffer, int *len, int max, nk_filter); +NK_API nk_flags nk_edit_buffer(struct nk_context*, nk_flags, struct nk_text_edit*, nk_filter); + +/* Chart */ +NK_API int nk_chart_begin(struct nk_context*, enum nk_chart_type, int num, float min, float max); +NK_API nk_flags nk_chart_push(struct nk_context*, float); +NK_API void nk_chart_end(struct nk_context*); +NK_API void nk_plot(struct nk_context*, enum nk_chart_type, const float *values, int count, int offset); +NK_API void nk_plot_function(struct nk_context*, enum nk_chart_type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset); + +/* Popups */ +NK_API int nk_popup_begin(struct nk_context*, struct nk_panel*, enum nk_popup_type, const char*, nk_flags, struct nk_rect bounds); +NK_API void nk_popup_close(struct nk_context*); +NK_API void nk_popup_end(struct nk_context*); + +/* Combobox */ +NK_API int nk_combo(struct nk_context*, const char **items, int count, int selected, int item_height); +NK_API int nk_combo_seperator(struct nk_context*, const char *items_seperated_by_seperator, int seperator, int selected, int count, int item_height); +NK_API int nk_combo_string(struct nk_context*, const char *items_seperated_by_zeros, int selected, int count, int item_height); +NK_API int nk_combo_callback(struct nk_context*, void(item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height); +NK_API void nk_combobox(struct nk_context*, const char **items, int count, int *selected, int item_height); +NK_API void nk_combobox_string(struct nk_context*, const char *items_seperated_by_zeros, int *selected, int count, int item_height); +NK_API void nk_combobox_seperator(struct nk_context*, const char *items_seperated_by_seperator, int seperator,int *selected, int count, int item_height); +NK_API void nk_combobox_callback(struct nk_context*, void(item_getter)(void*, int, const char**), void*, int *selected, int count, int item_height); + +/* Combobox: abstract */ +NK_API int nk_combo_begin_text(struct nk_context*, struct nk_panel*, const char *selected, int, int max_height); +NK_API int nk_combo_begin_label(struct nk_context*, struct nk_panel*, const char *selected, int max_height); +NK_API int nk_combo_begin_color(struct nk_context*, struct nk_panel*, struct nk_color color, int max_height); +NK_API int nk_combo_begin_symbol(struct nk_context*, struct nk_panel*, enum nk_symbol_type, int max_height); +NK_API int nk_combo_begin_symbol_label(struct nk_context*, struct nk_panel*, const char *selected, enum nk_symbol_type, int height); +NK_API int nk_combo_begin_symbol_text(struct nk_context*, struct nk_panel*, const char *selected, int, enum nk_symbol_type, int height); +NK_API int nk_combo_begin_image(struct nk_context*, struct nk_panel*, struct nk_image img, int max_height); +NK_API int nk_combo_begin_image_label(struct nk_context*, struct nk_panel*, const char *selected, struct nk_image, int height); +NK_API int nk_combo_begin_image_text(struct nk_context*, struct nk_panel*, const char *selected, int, struct nk_image, int height); +NK_API int nk_combo_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API int nk_combo_item_text(struct nk_context*, const char*,int, nk_flags alignment); +NK_API int nk_combo_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_combo_item_image_text(struct nk_context*, struct nk_image, const char*, int,nk_flags alignment); +NK_API int nk_combo_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API int nk_combo_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_combo_close(struct nk_context*); +NK_API void nk_combo_end(struct nk_context*); + +/* Contextual */ +NK_API int nk_contextual_begin(struct nk_context*, struct nk_panel*, nk_flags, struct nk_vec2, struct nk_rect trigger_bounds); +NK_API int nk_contextual_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API int nk_contextual_item_label(struct nk_context*, const char*, nk_flags align); +NK_API int nk_contextual_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_contextual_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API int nk_contextual_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API int nk_contextual_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_contextual_close(struct nk_context*); +NK_API void nk_contextual_end(struct nk_context*); + +/* Tooltip */ +NK_API void nk_tooltip(struct nk_context*, const char*); +NK_API int nk_tooltip_begin(struct nk_context*, struct nk_panel*, float width); +NK_API void nk_tooltip_end(struct nk_context*); + +/* Menu */ +NK_API void nk_menubar_begin(struct nk_context*); +NK_API void nk_menubar_end(struct nk_context*); +NK_API int nk_menu_begin_text(struct nk_context*, struct nk_panel*, const char*, int, nk_flags align, float width); +NK_API int nk_menu_begin_label(struct nk_context*, struct nk_panel*, const char*, nk_flags align, float width); +NK_API int nk_menu_begin_image(struct nk_context*, struct nk_panel*, const char*, struct nk_image, float width); +NK_API int nk_menu_begin_image_text(struct nk_context*, struct nk_panel*, const char*, int,nk_flags align,struct nk_image, float width); +NK_API int nk_menu_begin_image_label(struct nk_context*, struct nk_panel*, const char*, nk_flags align,struct nk_image, float width); +NK_API int nk_menu_begin_symbol(struct nk_context*, struct nk_panel*, const char*, enum nk_symbol_type, float width); +NK_API int nk_menu_begin_symbol_text(struct nk_context*, struct nk_panel*, const char*, int,nk_flags align,enum nk_symbol_type, float width); +NK_API int nk_menu_begin_symbol_label(struct nk_context*, struct nk_panel*, const char*, nk_flags align,enum nk_symbol_type, float width); +NK_API int nk_menu_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API int nk_menu_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API int nk_menu_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_menu_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API int nk_menu_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API int nk_menu_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API void nk_menu_close(struct nk_context*); +NK_API void nk_menu_end(struct nk_context*); + +/* Drawing*/ +#define nk_foreach(c, ctx)for((c)=nk__begin(ctx); (c)!=0; (c)=nk__next(ctx, c)) +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_API void nk_convert(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); +#define nk_draw_foreach(cmd,ctx, b) for((cmd)=nk__draw_begin(ctx, b); (cmd)!=0; (cmd)=nk__draw_next(cmd, b, ctx)) +#endif + +/* User Input */ +NK_API void nk_input_begin(struct nk_context*); +NK_API void nk_input_motion(struct nk_context*, int x, int y); +NK_API void nk_input_key(struct nk_context*, enum nk_keys, int down); +NK_API void nk_input_button(struct nk_context*, enum nk_buttons, int x, int y, int down); +NK_API void nk_input_scroll(struct nk_context*, float y); +NK_API void nk_input_char(struct nk_context*, char); +NK_API void nk_input_glyph(struct nk_context*, const nk_glyph); +NK_API void nk_input_unicode(struct nk_context*, nk_rune); +NK_API void nk_input_end(struct nk_context*); + +/* Style */ +NK_API void nk_style_default(struct nk_context*); +NK_API void nk_style_from_table(struct nk_context*, const struct nk_color*); +NK_API const char* nk_style_color_name(enum nk_style_colors); +NK_API void nk_style_set_font(struct nk_context*, const struct nk_user_font*); + +/* Utilities */ +NK_API struct nk_rect nk_widget_bounds(struct nk_context*); +NK_API struct nk_vec2 nk_widget_position(struct nk_context*); +NK_API struct nk_vec2 nk_widget_size(struct nk_context*); +NK_API int nk_widget_is_hovered(struct nk_context*); +NK_API int nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons); +NK_API int nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, int down); +NK_API void nk_spacing(struct nk_context*, int cols); + +/* base widget function */ +NK_API enum nk_widget_layout_states nk_widget(struct nk_rect*, const struct nk_context*); +NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect*, struct nk_context*, struct nk_vec2); + +/* color (conversion user --> nuklear) */ +NK_API struct nk_color nk_rgb(int r, int g, int b); +NK_API struct nk_color nk_rgb_iv(const int *rgb); +NK_API struct nk_color nk_rgb_bv(const nk_byte* rgb); +NK_API struct nk_color nk_rgb_f(float r, float g, float b); +NK_API struct nk_color nk_rgb_fv(const float *rgb); +NK_API struct nk_color nk_rgb_hex(const char *rgb); + +NK_API struct nk_color nk_rgba(int r, int g, int b, int a); +NK_API struct nk_color nk_rgba_u32(nk_uint); +NK_API struct nk_color nk_rgba_iv(const int *rgba); +NK_API struct nk_color nk_rgba_bv(const nk_byte *rgba); +NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a); +NK_API struct nk_color nk_rgba_fv(const float *rgba); +NK_API struct nk_color nk_rgba_hex(const char *rgb); + +NK_API struct nk_color nk_hsv(int h, int s, int v); +NK_API struct nk_color nk_hsv_iv(const int *hsv); +NK_API struct nk_color nk_hsv_bv(const nk_byte *hsv); +NK_API struct nk_color nk_hsv_f(float h, float s, float v); +NK_API struct nk_color nk_hsv_fv(const float *hsv); + +NK_API struct nk_color nk_hsva(int h, int s, int v, int a); +NK_API struct nk_color nk_hsva_iv(const int *hsva); +NK_API struct nk_color nk_hsva_bv(const nk_byte *hsva); +NK_API struct nk_color nk_hsva_f(float h, float s, float v, float a); +NK_API struct nk_color nk_hsva_fv(const float *hsva); + +/* color (conversion nuklear --> user) */ +NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color); +NK_API void nk_color_fv(float *rgba_out, struct nk_color); +NK_API nk_uint nk_color_u32(struct nk_color); +NK_API void nk_color_hex_rgba(char *output, struct nk_color); +NK_API void nk_color_hex_rgb(char *output, struct nk_color); + +NK_API void nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color); +NK_API void nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color); +NK_API void nk_color_hsv_iv(int *hsv_out, struct nk_color); +NK_API void nk_color_hsv_bv(nk_byte *hsv_out, struct nk_color); +NK_API void nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color); +NK_API void nk_color_hsv_fv(float *hsv_out, struct nk_color); + +NK_API void nk_color_hsva_i(int *h, int *s, int *v, int *a, struct nk_color); +NK_API void nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color); +NK_API void nk_color_hsva_iv(int *hsva_out, struct nk_color); +NK_API void nk_color_hsva_bv(nk_byte *hsva_out, struct nk_color); +NK_API void nk_color_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_color); +NK_API void nk_color_hsva_fv(float *hsva_out, struct nk_color); + +/* image */ +NK_API nk_handle nk_handle_ptr(void*); +NK_API nk_handle nk_handle_id(int); +NK_API struct nk_image nk_image_ptr(void*); +NK_API struct nk_image nk_image_id(int); +NK_API int nk_image_is_subimage(const struct nk_image* img); +NK_API struct nk_image nk_subimage_ptr(void*, unsigned short w, unsigned short h, struct nk_rect sub_region); +NK_API struct nk_image nk_subimage_id(int, unsigned short w, unsigned short h, struct nk_rect sub_region); + +/* math */ +NK_API nk_hash nk_murmur_hash(const void *key, int len, nk_hash seed); +NK_API void nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, float pad_x, float pad_y, enum nk_heading); + +NK_API struct nk_vec2 nk_vec2(float x, float y); +NK_API struct nk_vec2 nk_vec2i(int x, int y); +NK_API struct nk_vec2 nk_vec2v(const float *xy); +NK_API struct nk_vec2 nk_vec2iv(const int *xy); + +NK_API struct nk_rect nk_get_null_rect(void); +NK_API struct nk_rect nk_rect(float x, float y, float w, float h); +NK_API struct nk_rect nk_recti(int x, int y, int w, int h); +NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size); +NK_API struct nk_rect nk_rectv(const float *xywh); +NK_API struct nk_rect nk_rectiv(const int *xywh); + +/* string*/ +NK_API int nk_strlen(const char *str); +NK_API int nk_stricmp(const char *s1, const char *s2); +NK_API int nk_stricmpn(const char *s1, const char *s2, int n); +NK_API int nk_strtof(float *number, const char *buffer); +NK_API int nk_strfilter(const char *text, const char *regexp); +NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score); +NK_API int nk_strmatch_fuzzy_text(const char *txt, int txt_len, const char *pattern, int *out_score); +#ifdef NK_INCLUDE_STANDARD_IO +NK_API int nk_strfmt(char *buf, int len, const char *fmt,...); +#endif + +/* UTF-8 */ +NK_API int nk_utf_decode(const char*, nk_rune*, int); +NK_API int nk_utf_encode(nk_rune, char*, int); +NK_API int nk_utf_len(const char*, int byte_len); +NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len); + +/* ============================================================== + * + * MEMORY BUFFER + * + * ===============================================================*/ +/* A basic (double)-buffer with linear allocation and resetting as only + freeing policy. The buffers main purpose is to control all memory management + inside the GUI toolkit and still leave memory control as much as possible in + the hand of the user while also making sure the library is easy to use if + not as much control is needed. + In general all memory inside this library can be provided from the user in + three different ways. + + The first way and the one providing most control is by just passing a fixed + size memory block. In this case all control lies in the hand of the user + since he can exactly control where the memory comes from and how much memory + the library should consume. Of course using the fixed size API removes the + ability to automatically resize a buffer if not enough memory is provided so + you have to take over the resizing. While being a fixed sized buffer sounds + quite limiting, it is very effective in this library since the actual memory + consumption is quite stable and has a fixed upper bound for a lot of cases. + + If you don't want to think about how much memory the library should allocate + at all time or have a very dynamic UI with unpredictable memory consumoption + habits but still want control over memory allocation you can use the dynamic + allocator based API. The allocator consists of two callbacks for allocating + and freeing memory and optional userdata so you can plugin your own allocator. + + The final and easiest way can be used by defining + NK_INCLUDE_DEFAULT_ALLOCATOR which uses the standard library memory + allocation functions malloc and free and takes over complete control over + memory in this library. +*/ +struct nk_memory_status { + void *memory; + unsigned int type; + nk_size size; + nk_size allocated; + nk_size needed; + nk_size calls; +}; + +enum nk_allocation_type { + NK_BUFFER_FIXED, + NK_BUFFER_DYNAMIC +}; + +enum nk_buffer_allocation_type { + NK_BUFFER_FRONT, + NK_BUFFER_BACK, + NK_BUFFER_MAX +}; + +struct nk_buffer_marker { + int active; + nk_size offset; +}; + +struct nk_memory {void *ptr;nk_size size;}; +struct nk_buffer { + struct nk_buffer_marker marker[NK_BUFFER_MAX]; + /* buffer marker to free a buffer to a certain offset */ + struct nk_allocator pool; + /* allocator callback for dynamic buffers */ + enum nk_allocation_type type; + /* memory management type */ + struct nk_memory memory; + /* memory and size of the current memory block */ + float grow_factor; + /* growing factor for dynamic memory management */ + nk_size allocated; + /* total amount of memory allocated */ + nk_size needed; + /* totally consumed memory given that enough memory is present */ + nk_size calls; + /* number of allocation calls */ + nk_size size; + /* current size of the buffer */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_buffer_init_default(struct nk_buffer*); +#endif +NK_API void nk_buffer_init(struct nk_buffer*, const struct nk_allocator*, nk_size size); +NK_API void nk_buffer_init_fixed(struct nk_buffer*, void *memory, nk_size size); +NK_API void nk_buffer_info(struct nk_memory_status*, struct nk_buffer*); +NK_API void nk_buffer_push(struct nk_buffer*, enum nk_buffer_allocation_type type, + void *memory, nk_size size, nk_size align); +NK_API void nk_buffer_mark(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_reset(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_clear(struct nk_buffer*); +NK_API void nk_buffer_free(struct nk_buffer*); +NK_API void *nk_buffer_memory(struct nk_buffer*); +NK_API const void *nk_buffer_memory_const(const struct nk_buffer*); +NK_API nk_size nk_buffer_total(struct nk_buffer*); + +/* ============================================================== + * + * STRING + * + * ===============================================================*/ +/* Basic string buffer which is only used in context of the text editor + * to manage and manipulate dynamic or fixed size string content. This is _NOT_ + * the default string handling method.*/ +struct nk_str { + struct nk_buffer buffer; + int len; /* in glyphes */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_str_init_default(struct nk_str*); +#endif +NK_API void nk_str_init(struct nk_str*, const struct nk_allocator*, nk_size size); +NK_API void nk_str_init_fixed(struct nk_str*, void *memory, nk_size size); +NK_API void nk_str_clear(struct nk_str*); +NK_API void nk_str_free(struct nk_str*); + +NK_API int nk_str_append_text_char(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_char(struct nk_str*, const char*); +NK_API int nk_str_append_text_utf8(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_utf8(struct nk_str*, const char*); +NK_API int nk_str_append_text_runes(struct nk_str*, const nk_rune*, int); +NK_API int nk_str_append_str_runes(struct nk_str*, const nk_rune*); + +NK_API int nk_str_insert_at_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_at_rune(struct nk_str*, int pos, const char*, int); + +NK_API int nk_str_insert_text_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_char(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_utf8(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_utf8(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_runes(struct nk_str*, int pos, const nk_rune*, int); +NK_API int nk_str_insert_str_runes(struct nk_str*, int pos, const nk_rune*); + +NK_API void nk_str_remove_chars(struct nk_str*, int len); +NK_API void nk_str_remove_runes(struct nk_str *str, int len); +NK_API void nk_str_delete_chars(struct nk_str*, int pos, int len); +NK_API void nk_str_delete_runes(struct nk_str*, int pos, int len); + +NK_API char *nk_str_at_char(struct nk_str*, int pos); +NK_API char *nk_str_at_rune(struct nk_str*, int pos, nk_rune *unicode, int *len); +NK_API nk_rune nk_str_rune_at(const struct nk_str*, int pos); +NK_API const char *nk_str_at_char_const(const struct nk_str*, int pos); +NK_API const char *nk_str_at_const(const struct nk_str*, int pos, nk_rune *unicode, int *len); + +NK_API char *nk_str_get(struct nk_str*); +NK_API const char *nk_str_get_const(const struct nk_str*); +NK_API int nk_str_len(struct nk_str*); +NK_API int nk_str_len_char(struct nk_str*); + +/*=============================================================== + * + * TEXT EDITOR + * + * ===============================================================*/ +#define NK_TEXTEDIT_UNDOSTATECOUNT 99 +#define NK_TEXTEDIT_UNDOCHARCOUNT 999 + +struct nk_text_edit; + +struct nk_clipboard { + nk_handle userdata; + nk_paste_f paste; + nk_copy_f copy; +}; + +struct nk_text_undo_record { + int where; + short insert_length; + short delete_length; + short char_storage; +}; + +struct nk_text_undo_state { + struct nk_text_undo_record undo_rec[NK_TEXTEDIT_UNDOSTATECOUNT]; + nk_rune undo_char[NK_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point; + short redo_point; + short undo_char_point; + short redo_char_point; +}; + +enum nk_text_edit_type { + NK_TEXT_EDIT_SINGLE_LINE, + NK_TEXT_EDIT_MULTI_LINE +}; + +struct nk_text_edit { + struct nk_clipboard clip; + struct nk_str string; + nk_filter filter; + struct nk_vec2 scrollbar; + + int cursor; + int select_start; + int select_end; + unsigned char insert_mode; + unsigned char cursor_at_end_of_line; + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char active; + unsigned char padding1; + float preferred_x; + struct nk_text_undo_state undo; +}; + +/* filter function */ +NK_API int nk_filter_default(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_ascii(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_float(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_decimal(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_hex(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_oct(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_binary(const struct nk_text_edit*, nk_rune unicode); + +/* text editor */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_textedit_init_default(struct nk_text_edit*); +#endif +NK_API void nk_textedit_init(struct nk_text_edit*, struct nk_allocator*, nk_size size); +NK_API void nk_textedit_init_fixed(struct nk_text_edit*, void *memory, nk_size size); +NK_API void nk_textedit_free(struct nk_text_edit*); +NK_API void nk_textedit_text(struct nk_text_edit*, const char*, int total_len); +NK_API void nk_textedit_delete(struct nk_text_edit*, int where, int len); +NK_API void nk_textedit_delete_selection(struct nk_text_edit*); +NK_API void nk_textedit_select_all(struct nk_text_edit*); +NK_API int nk_textedit_cut(struct nk_text_edit*); +NK_API int nk_textedit_paste(struct nk_text_edit*, char const*, int len); +NK_API void nk_textedit_undo(struct nk_text_edit*); +NK_API void nk_textedit_redo(struct nk_text_edit*); + +/* =============================================================== + * + * FONT + * + * ===============================================================*/ +/* Font handling in this library was designed to be quite customizeable and lets + you decide what you want to use and what you want to provide. In this sense + there are four different degrees between control and ease of use and two + different drawing APIs to provide for. + + So first of the easiest way to do font handling is by just providing a + `nk_user_font` struct which only requires the height in pixel of the used + font and a callback to calculate the width of a string. This way of handling + fonts is best fitted for using the normal draw shape command API were you + do all the text drawing yourself and the library does not require any kind + of deeper knowledge about which font handling mechanism you use. + + While the first approach works fine if you don't want to use the optional + vertex buffer output it is not enough if you do. To get font handling working + for these cases you have to provide to additional parameter inside the + `nk_user_font`. First a texture atlas handle used to draw text as subimages + of a bigger font atlas texture and a callback to query a characters glyph + information (offset, size, ...). So it is still possible to provide your own + font and use the vertex buffer output. + + The final approach if you do not have a font handling functionality or don't + want to use it in this library is by using the optional font baker. This API + is divided into a high- and low-level API with different priorites between + ease of use and control. Both API's can be used to create a font and + font atlas texture and can even be used with or without the vertex buffer + output. So it still uses the `nk_user_font` struct and the two different + approaches previously stated still work. + Now to the difference between the low level API and the high level API. The low + level API provides a lot of control over the baking process of the font and + provides total control over memory. It consists of a number of functions that + need to be called from begin to end and each step requires some additional + configuration, so it is a lot more complex than the high-level API. + If you don't want to do all the work required for using the low-level API + you can use the font atlas API. It provides the same functionality as the + low-level API but takes away some configuration and all of memory control and + in term provides a easier to use API. +*/ +struct nk_user_font_glyph; +typedef float(*nk_text_width_f)(nk_handle, float h, const char*, int len); +typedef void(*nk_query_font_glyph_f)(nk_handle handle, float font_height, + struct nk_user_font_glyph *glyph, + nk_rune codepoint, nk_rune next_codepoint); + +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +struct nk_user_font_glyph { + struct nk_vec2 uv[2]; + /* texture coordinates */ + struct nk_vec2 offset; + /* offset between top left and glyph */ + float width, height; + /* size of the glyph */ + float xadvance; + /* offset to the next glyph */ +}; +#endif + +struct nk_user_font { + nk_handle userdata; + /* user provided font handle */ + float height; + /* max height of the font */ + nk_text_width_f width; + /* font string width in pixel callback */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_query_font_glyph_f query; + /* font glyph callback to query drawing info */ + nk_handle texture; + /* texture handle to the used font atlas or texture */ +#endif +}; + +#ifdef NK_INCLUDE_FONT_BAKING +enum nk_font_coord_type { + NK_COORD_UV, + /* texture coordinates inside font glyphs are clamped between 0-1 */ + NK_COORD_PIXEL + /* texture coordinates inside font glyphs are in absolute pixel */ +}; + +struct nk_baked_font { + float height; + /* height of the font */ + float ascent, descent; + /* font glyphs ascent and descent */ + nk_rune glyph_offset; + /* glyph array offset inside the font glyph baking output array */ + nk_rune glyph_count; + /* number of glyphs of this font inside the glyph baking array output */ + const nk_rune *ranges; + /* font codepoint ranges as pairs of (from/to) and 0 as last element */ +}; + +struct nk_font_config { + void *ttf_blob; + /* pointer to loaded TTF file memory block. + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + nk_size ttf_size; + /* size of the loaded TTF file memory block + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + + unsigned char ttf_data_owned_by_atlas; + /* used inside font atlas: default to: 0*/ + unsigned char merge_mode; + /* merges this font into the last font */ + unsigned char pixel_snap; + /* align very character to pixel boundry (if true set oversample (1,1)) */ + unsigned char oversample_v, oversample_h; + /* rasterize at hight quality for sub-pixel position */ + unsigned char padding[3]; + + float size; + /* baked pixel height of the font */ + enum nk_font_coord_type coord_type; + /* texture coordinate format with either pixel or UV coordinates */ + struct nk_vec2 spacing; + /* extra pixel spacing between glyphs */ + const nk_rune *range; + /* list of unicode ranges (2 values per range, zero terminated) */ + struct nk_baked_font *font; + /* font to setup in the baking process: NOTE: not needed for font atlas */ + nk_rune fallback_glyph; + /* fallback glyph to use if a given rune is not found */ +}; + +struct nk_font_glyph { + nk_rune codepoint; + float xadvance; + float x0, y0, x1, y1, w, h; + float u0, v0, u1, v1; +}; + +struct nk_font { + struct nk_user_font handle; + struct nk_baked_font info; + float scale; + struct nk_font_glyph *glyphs; + const struct nk_font_glyph *fallback; + nk_rune fallback_codepoint; + nk_handle texture; + int config; +}; + +enum nk_font_atlas_format { + NK_FONT_ATLAS_ALPHA8, + NK_FONT_ATLAS_RGBA32 +}; + +struct nk_font_atlas { + void *pixel; + int tex_width; + int tex_height; + struct nk_allocator alloc; + struct nk_recti custom; + + int glyph_count; + struct nk_font *default_font; + struct nk_font_glyph *glyphes; + struct nk_font **fonts; + struct nk_font_config *config; + int font_num, font_cap; +}; + +/* some language glyph codepoint ranges */ +NK_API const nk_rune *nk_font_default_glyph_ranges(void); +NK_API const nk_rune *nk_font_chinese_glyph_ranges(void); +NK_API const nk_rune *nk_font_cyrillic_glyph_ranges(void); +NK_API const nk_rune *nk_font_korean_glyph_ranges(void); + +/* Font Atlas + * --------------------------------------------------------------- + * This is the high level font baking and handling API to generate an image + * out of font glyphes used to draw text onto the screen. This API takes away + * some control over the baking process like fine grained memory control and + * custom baking data but provides additional functionality and easier to + * use and manage datastructures and functions. */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_font_atlas_init_default(struct nk_font_atlas*); +#endif +NK_API void nk_font_atlas_init(struct nk_font_atlas*, struct nk_allocator*); + +NK_API void nk_font_atlas_begin(struct nk_font_atlas*); +NK_API struct nk_font_config nk_font_config(float pixel_height); +NK_API struct nk_font *nk_font_atlas_add(struct nk_font_atlas*, const struct nk_font_config*); +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* nk_font_atlas_add_default(struct nk_font_atlas*, float height, + const struct nk_font_config*); +#endif +NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, + void *memory, nk_size size, float height, + const struct nk_font_config *config); +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, + const char *file_path, float height, + const struct nk_font_config*); +#endif +NK_API struct nk_font *nk_font_atlas_add_compressed(struct nk_font_atlas*, + void *memory, nk_size size, float height, + const struct nk_font_config*); +NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas*, + const char *data, float height, + const struct nk_font_config *config); +NK_API const void* nk_font_atlas_bake(struct nk_font_atlas*, int *width, int *height, + enum nk_font_atlas_format); +NK_API void nk_font_atlas_end(struct nk_font_atlas*, nk_handle tex, + struct nk_draw_null_texture*); +NK_API void nk_font_atlas_clear(struct nk_font_atlas*); + +/* Font + * ----------------------------------------------------------------- + * The font structure is just a simple container to hold the output of a baking + * process in the low level API. */ +NK_API void nk_font_init(struct nk_font*, float pixel_height, nk_rune fallback_codepoint, + struct nk_font_glyph*, const struct nk_baked_font*, + nk_handle atlas); +NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font*, nk_rune unicode); + +/* Font baking (needs to be called sequentially top to bottom) + * -------------------------------------------------------------------- + * This is a low level API to bake font glyphs into an image and is more + * complex than the atlas API but provides more control over the baking + * process with custom bake data and memory management. */ +NK_API void nk_font_bake_memory(nk_size *temporary_memory, int *glyph_count, + struct nk_font_config*, int count); +NK_API int nk_font_bake_pack(nk_size *img_memory, int *img_width, int *img_height, + struct nk_recti *custom_space, + void *temporary_memory, nk_size temporary_size, + const struct nk_font_config*, int font_count, + struct nk_allocator *alloc); +NK_API void nk_font_bake(void *image_memory, int image_width, int image_height, + void *temporary_memory, nk_size temporary_memory_size, + struct nk_font_glyph*, int glyphs_count, + const struct nk_font_config*, int font_count); +NK_API void nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, + struct nk_recti img_dst, const char *image_data_mask, + int tex_width, int tex_height,char white,char black); +NK_API void nk_font_bake_convert(void *out_memory, int image_width, int image_height, + const void *in_memory); + +#endif + +/* =============================================================== + * + * DRAWING + * + * ===============================================================*/ +/* This library was designed to be render backend agnostic so it does + not draw anything to screen. Instead all drawn shapes, widgets + are made of, are buffered into memory and make up a command queue. + Each frame therefore fills the command buffer with draw commands + that then need to be executed by the user and his own render backend. + After that the command buffer needs to be cleared and a new frame can be + started. It is probably important to note that the command buffer is the main + drawing API and the optional vertex buffer API only takes this format and + converts it into a hardware accessable format. + + Draw commands are divided into filled shapes and shape outlines but only + the filled shapes as well as line, curves and scissor are required to be provided. + All other shape drawing commands can be used but are not required. This was + done to allow the maximum number of render backends to be able to use this + library without you having to do additional work. +*/ +enum nk_command_type { + NK_COMMAND_NOP, + NK_COMMAND_SCISSOR, + NK_COMMAND_LINE, + NK_COMMAND_CURVE, + NK_COMMAND_RECT, + NK_COMMAND_RECT_FILLED, + NK_COMMAND_RECT_MULTI_COLOR, + NK_COMMAND_CIRCLE, + NK_COMMAND_CIRCLE_FILLED, + NK_COMMAND_ARC, + NK_COMMAND_ARC_FILLED, + NK_COMMAND_TRIANGLE, + NK_COMMAND_TRIANGLE_FILLED, + NK_COMMAND_POLYGON, + NK_COMMAND_POLYGON_FILLED, + NK_COMMAND_POLYLINE, + NK_COMMAND_TEXT, + NK_COMMAND_IMAGE +}; + +/* command base and header of every comand inside the buffer */ +struct nk_command { + enum nk_command_type type; + nk_size next; +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_command_scissor { + struct nk_command header; + short x, y; + unsigned short w, h; +}; + +struct nk_command_line { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_color color; +}; + +struct nk_command_curve { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_vec2i ctrl[2]; + struct nk_color color; +}; + +struct nk_command_rect { + struct nk_command header; + unsigned short rounding; + unsigned short line_thickness; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_filled { + struct nk_command header; + unsigned short rounding; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_multi_color { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color left; + struct nk_color top; + struct nk_color bottom; + struct nk_color right; +}; + +struct nk_command_triangle { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_triangle_filled { + struct nk_command header; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_circle { + struct nk_command header; + short x, y; + unsigned short line_thickness; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_circle_filled { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_arc { + struct nk_command header; + short cx, cy; + unsigned short r; + unsigned short line_thickness; + float a[2]; + struct nk_color color; +}; + +struct nk_command_arc_filled { + struct nk_command header; + short cx, cy; + unsigned short r; + float a[2]; + struct nk_color color; +}; + +struct nk_command_polygon { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polygon_filled { + struct nk_command header; + struct nk_color color; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polyline { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_image { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_image img; +}; + +struct nk_command_text { + struct nk_command header; + const struct nk_user_font *font; + struct nk_color background; + struct nk_color foreground; + short x, y; + unsigned short w, h; + float height; + int length; + char string[1]; +}; + +enum nk_command_clipping { + NK_CLIPPING_OFF = nk_false, + NK_CLIPPING_ON = nk_true +}; + +struct nk_command_buffer { + struct nk_buffer *base; + struct nk_rect clip; + int use_clipping; + nk_handle userdata; + nk_size begin, end, last; +}; + +/* shape outlines */ +NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, + float x1, float y1, float line_thickness, struct nk_color); +NK_API void nk_stroke_curve(struct nk_command_buffer*, float, float, float, float, + float, float, float, float, float line_thickness, struct nk_color); +NK_API void nk_stroke_rect(struct nk_command_buffer*, struct nk_rect, float rounding, + float line_thickness, struct nk_color); +NK_API void nk_stroke_circle(struct nk_command_buffer*, struct nk_rect, + float line_thickness, struct nk_color); +NK_API void nk_stroke_arc(struct nk_command_buffer*, float cx, float cy, float radius, + float a_min, float a_max, float line_thickness, struct nk_color); +NK_API void nk_stroke_triangle(struct nk_command_buffer*, float, float, + float, float, float, float, float line_thichness, + struct nk_color); +NK_API void nk_stroke_polyline(struct nk_command_buffer*, float *points, int point_count, + float line_thickness, struct nk_color col); +NK_API void nk_stroke_polygon(struct nk_command_buffer*, float*, int point_count, + float line_thickness, struct nk_color); + +/* filled shades */ +NK_API void nk_fill_rect(struct nk_command_buffer*, struct nk_rect, float rounding, + struct nk_color); +NK_API void nk_fill_rect_multi_color(struct nk_command_buffer*, struct nk_rect, + struct nk_color left, struct nk_color top, + struct nk_color right, struct nk_color bottom); +NK_API void nk_fill_circle(struct nk_command_buffer*, struct nk_rect, struct nk_color); +NK_API void nk_fill_arc(struct nk_command_buffer*, float cx, float cy, float radius, + float a_min, float a_max, struct nk_color); +NK_API void nk_fill_triangle(struct nk_command_buffer*, float x0, float y0, + float x1, float y1, float x2, float y2, struct nk_color); +NK_API void nk_fill_polygon(struct nk_command_buffer*, float*, int point_count, + struct nk_color); +/* misc */ +NK_API void nk_push_scissor(struct nk_command_buffer*, struct nk_rect); +NK_API void nk_draw_image(struct nk_command_buffer*, struct nk_rect, const struct nk_image*); +NK_API void nk_draw_text(struct nk_command_buffer*, struct nk_rect, + const char *text, int len, const struct nk_user_font*, + struct nk_color, struct nk_color); + +NK_API const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); +NK_API const struct nk_command* nk__begin(struct nk_context*); + +/* =============================================================== + * + * INPUT + * + * ===============================================================*/ +struct nk_mouse_button { + int down; + unsigned int clicked; + struct nk_vec2 clicked_pos; +}; + +struct nk_mouse { + struct nk_mouse_button buttons[NK_BUTTON_MAX]; + struct nk_vec2 pos; + struct nk_vec2 prev; + struct nk_vec2 delta; + float scroll_delta; +}; + +struct nk_key { + int down; + unsigned int clicked; +}; + +struct nk_keyboard { + struct nk_key keys[NK_KEY_MAX]; + char text[NK_INPUT_MAX]; + int text_len; +}; + +struct nk_input { + struct nk_keyboard keyboard; + struct nk_mouse mouse; +}; + +NK_API int nk_input_has_mouse_click_in_rect(const struct nk_input*, + enum nk_buttons, struct nk_rect); +NK_API int nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, + struct nk_rect, int down); +NK_API int nk_input_is_mouse_click_in_rect(const struct nk_input*, + enum nk_buttons, struct nk_rect); +NK_API int nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, int down); +NK_API int nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API int nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_mouse_pressed(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_mouse_released(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_key_pressed(const struct nk_input*, enum nk_keys); +NK_API int nk_input_is_key_released(const struct nk_input*, enum nk_keys); +NK_API int nk_input_is_key_down(const struct nk_input*, enum nk_keys); + + +/* =============================================================== + * + * DRAW LIST + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/* The optional vertex buffer draw list provides a 2D drawing context + with antialiasing functionality which takes basic filled or outlined shapes + or a path and outputs vertexes, elements and draw commands. + The actual draw list API is not required to be used dirctly while using this + library since converting the default library draw command output is done by + just calling `nk_convert` but I decided to still make this library accessable + since it can be useful. + + The draw list is based on a path buffering and polygon and polyline + rendering API which allows a lot of ways to draw 2D content to screen. + In fact it is probably more powerful than needed but allows even more crazy + things than this library provides by default. +*/ +typedef unsigned short nk_draw_index; +typedef nk_uint nk_draw_vertex_color; + +enum nk_draw_list_stroke { + NK_STROKE_OPEN = nk_false, + /* build up path has no connection back to the beginning */ + NK_STROKE_CLOSED = nk_true + /* build up path has a connection back to the beginning */ +}; + +struct nk_draw_vertex { + struct nk_vec2 position; + struct nk_vec2 uv; + nk_draw_vertex_color col; +}; + +struct nk_draw_command { + unsigned int elem_count; + /* number of elements in the current draw batch */ + struct nk_rect clip_rect; + /* current screen clipping rectangle */ + nk_handle texture; + /* current texture to set */ +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_draw_list { + float global_alpha; + enum nk_anti_aliasing shape_AA; + enum nk_anti_aliasing line_AA; + struct nk_draw_null_texture null; + struct nk_rect clip_rect; + struct nk_buffer *buffer; + struct nk_buffer *vertices; + struct nk_buffer *elements; + unsigned int element_count; + unsigned int vertex_count; + nk_size cmd_offset; + unsigned int cmd_count; + unsigned int path_count; + unsigned int path_offset; + struct nk_vec2 circle_vtx[12]; +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +/* draw list */ +NK_API void nk_draw_list_init(struct nk_draw_list*); +NK_API void nk_draw_list_setup(struct nk_draw_list*, float global_alpha, + enum nk_anti_aliasing line_AA, enum nk_anti_aliasing shape_AA, + struct nk_draw_null_texture, struct nk_buffer *cmds, + struct nk_buffer *vertices, struct nk_buffer *elements); +NK_API void nk_draw_list_clear(struct nk_draw_list*); + +/* drawing */ +#define nk_draw_list_foreach(cmd, can, b)\ + for((cmd)=nk__draw_list_begin(can, b); (cmd)!=0; (cmd)=nk__draw_list_next(cmd, b, can)) +NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list*, + const struct nk_buffer*); +NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command*, + const struct nk_buffer*, + const struct nk_draw_list*); +NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context*, + const struct nk_buffer*); +NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, + const struct nk_buffer*, + const struct nk_context*); + +/* path */ +NK_API void nk_draw_list_path_clear(struct nk_draw_list*); +NK_API void nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos); +NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list*, struct nk_vec2 center, + float radius, int a_min, int a_max); +NK_API void nk_draw_list_path_arc_to(struct nk_draw_list*, struct nk_vec2 center, + float radius, float a_min, float a_max, + unsigned int segments); +NK_API void nk_draw_list_path_rect_to(struct nk_draw_list*, struct nk_vec2 a, + struct nk_vec2 b, float rounding); +NK_API void nk_draw_list_path_curve_to(struct nk_draw_list*, struct nk_vec2 p2, + struct nk_vec2 p3, struct nk_vec2 p4, + unsigned int num_segments); +NK_API void nk_draw_list_path_fill(struct nk_draw_list*, struct nk_color); +NK_API void nk_draw_list_path_stroke(struct nk_draw_list*, struct nk_color, + enum nk_draw_list_stroke closed, float thickness); +/* stroke */ +NK_API void nk_draw_list_stroke_line(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, + struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, + float rounding, float thickness); +NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, + struct nk_vec2 c, struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, + struct nk_color, unsigned int segs, float thickness); +NK_API void nk_draw_list_stroke_curve(struct nk_draw_list*, struct nk_vec2 p0, struct nk_vec2 cp0, + struct nk_vec2 cp1, struct nk_vec2 p1, struct nk_color, + unsigned int segments, float thickness); +NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list*, const struct nk_vec2 *points, + const unsigned int count, struct nk_color, + enum nk_draw_list_stroke closed, float thickness, + enum nk_anti_aliasing aliasing); +/* fill */ +NK_API void nk_draw_list_fill_rect(struct nk_draw_list*, struct nk_rect rect, + struct nk_color, float rounding); +NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, + struct nk_rect rect, struct nk_color left, + struct nk_color top, struct nk_color right, + struct nk_color bottom); +NK_API void nk_draw_list_fill_triangle(struct nk_draw_list*, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color); +NK_API void nk_draw_list_fill_circle(struct nk_draw_list*, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs); +NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list*, const struct nk_vec2 *points, + const unsigned int count, struct nk_color, + enum nk_anti_aliasing aliasing); +/* misc */ +NK_API void nk_draw_list_add_image(struct nk_draw_list*, struct nk_image texture, + struct nk_rect rect, struct nk_color); +NK_API void nk_draw_list_add_text(struct nk_draw_list*, const struct nk_user_font*, + struct nk_rect, const char *text, int len, + float font_height, struct nk_color); +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void nk_draw_list_push_userdata(struct nk_draw_list*, nk_handle userdata); +#endif + +#endif + +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +enum nk_style_item_type { + NK_STYLE_ITEM_COLOR, + NK_STYLE_ITEM_IMAGE +}; + +union nk_style_item_data { + struct nk_image image; + struct nk_color color; +}; + +struct nk_style_item { + enum nk_style_item_type type; + union nk_style_item_data data; +}; + +struct nk_style_text { + struct nk_color color; + struct nk_vec2 padding; +}; + +struct nk_style_button; +struct nk_style_custom_button_drawing { + void(*button_text)(struct nk_command_buffer*, + const struct nk_rect *background, const struct nk_rect*, + nk_flags state, const struct nk_style_button*, + const char*, int len, nk_flags text_alignment, + const struct nk_user_font*); + void(*button_symbol)(struct nk_command_buffer*, + const struct nk_rect *background, const struct nk_rect*, + nk_flags state, const struct nk_style_button*, + enum nk_symbol_type, const struct nk_user_font*); + void(*button_image)(struct nk_command_buffer*, + const struct nk_rect *background, const struct nk_rect*, + nk_flags state, const struct nk_style_button*, + const struct nk_image *img); + void(*button_text_symbol)(struct nk_command_buffer*, + const struct nk_rect *background, const struct nk_rect*, + const struct nk_rect *symbol, nk_flags state, + const struct nk_style_button*, + const char *text, int len, enum nk_symbol_type, + const struct nk_user_font*); + void(*button_text_image)(struct nk_command_buffer*, + const struct nk_rect *background, const struct nk_rect*, + const struct nk_rect *image, nk_flags state, + const struct nk_style_button*, + const char *text, int len, const struct nk_user_font*, + const struct nk_image *img); +}; + +struct nk_style_button { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color text_background; + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + nk_flags text_alignment; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + struct nk_vec2 image_padding; + struct nk_vec2 touch_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle userdata); + struct nk_style_custom_button_drawing draw; + void(*draw_end)(struct nk_command_buffer*, nk_handle userdata); +}; + +struct nk_style_toggle; +union nk_style_custom_toggle_drawing { + void(*radio)(struct nk_command_buffer*, nk_flags state, + const struct nk_style_toggle *toggle, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursor, const char *string, int len, + const struct nk_user_font *font); + void(*checkbox)(struct nk_command_buffer*, nk_flags state, + const struct nk_style_toggle *toggle, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursor, const char *string, int len, + const struct nk_user_font *font); +}; + +struct nk_style_toggle { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + + /* text */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + union nk_style_custom_toggle_drawing draw; + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_selectable { + /* background (inactive) */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item pressed; + + /* background (active) */ + struct nk_style_item normal_active; + struct nk_style_item hover_active; + struct nk_style_item pressed_active; + + /* text color (inactive) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_pressed; + + /* text color (active) */ + struct nk_color text_normal_active; + struct nk_color text_hover_active; + struct nk_color text_pressed_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + float rounding; + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw)(struct nk_command_buffer*, + nk_flags state, const struct nk_style_selectable*, int active, + const struct nk_rect*, const char *string, int len, + nk_flags align, const struct nk_user_font*); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_slider { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* background bar */ + struct nk_color bar_normal; + struct nk_color bar_hover; + struct nk_color bar_active; + struct nk_color bar_filled; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + + /* properties */ + float border; + float rounding; + float bar_height; + struct nk_vec2 padding; + struct nk_vec2 spacing; + struct nk_vec2 cursor_size; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw)(struct nk_command_buffer*, nk_flags state, + const struct nk_style_slider*, const struct nk_rect *bar, + const struct nk_rect *cursor, float min, float value, float max); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_progress { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + + /* properties */ + float rounding; + struct nk_vec2 padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw)(struct nk_command_buffer*, nk_flags state, + const struct nk_style_progress*, const struct nk_rect *bounds, + const struct nk_rect *cursor, nk_size value, nk_size max); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_scrollbar { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw)(struct nk_command_buffer*, nk_flags state, + const struct nk_style_scrollbar*, const struct nk_rect *scroll, + const struct nk_rect *cursor); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_edit { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + struct nk_style_scrollbar scrollbar; + + /* cursor */ + struct nk_color cursor_normal; + struct nk_color cursor_hover; + struct nk_color cursor_text_normal; + struct nk_color cursor_text_hover; + + /* text (unselected) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + + /* text (selected) */ + struct nk_color selected_normal; + struct nk_color selected_hover; + struct nk_color selected_text_normal; + struct nk_color selected_text_hover; + + /* properties */ + float border; + float rounding; + float cursor_size; + struct nk_vec2 scrollbar_size; + struct nk_vec2 padding; + float row_padding; +}; + +struct nk_style_property { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbols */ + enum nk_symbol_type sym_left; + enum nk_symbol_type sym_right; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + + struct nk_style_edit edit; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw)(struct nk_command_buffer*, const struct nk_style_property*, + const struct nk_rect*, const struct nk_rect *label, nk_flags state, + const char *name, int len, const struct nk_user_font*); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_chart { + /* colors */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color selected_color; + struct nk_color color; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; +}; + +struct nk_style_combo { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* label */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbol */ + struct nk_color symbol_normal; + struct nk_color symbol_hover; + struct nk_color symbol_active; + + /* button */ + struct nk_style_button button; + enum nk_symbol_type sym_normal; + enum nk_symbol_type sym_hover; + enum nk_symbol_type sym_active; + + /* properties */ + float border; + float rounding; + struct nk_vec2 content_padding; + struct nk_vec2 button_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_tab { + /* background */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color text; + + /* button */ + struct nk_style_button tab_button; + struct nk_style_button node_button; + enum nk_symbol_type sym_minimize; + enum nk_symbol_type sym_maximize; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + struct nk_vec2 spacing; +}; + +enum nk_style_header_align { + NK_HEADER_LEFT, + NK_HEADER_RIGHT +}; + +struct nk_style_window_header { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + + /* button */ + struct nk_style_button close_button; + struct nk_style_button minimize_button; + enum nk_symbol_type close_symbol; + enum nk_symbol_type minimize_symbol; + enum nk_symbol_type maximize_symbol; + + /* title */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* properties */ + enum nk_style_header_align align; + struct nk_vec2 padding; + struct nk_vec2 label_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_window { + struct nk_style_window_header header; + struct nk_style_item fixed_background; + struct nk_color background; + + struct nk_color border_color; + struct nk_color combo_border_color; + struct nk_color contextual_border_color; + struct nk_color menu_border_color; + struct nk_color group_border_color; + struct nk_color tooltip_border_color; + + struct nk_style_item scaler; + struct nk_vec2 footer_padding; + + float border; + float combo_border; + float contextual_border; + float menu_border; + float group_border; + float tooltip_border; + + float rounding; + struct nk_vec2 scaler_size; + struct nk_vec2 padding; + struct nk_vec2 spacing; + struct nk_vec2 scrollbar_size; + struct nk_vec2 min_size; +}; + +struct nk_style { + struct nk_user_font font; + struct nk_style_text text; + struct nk_style_button button; + struct nk_style_button contextual_button; + struct nk_style_button menu_button; + struct nk_style_toggle option; + struct nk_style_toggle checkbox; + struct nk_style_selectable selectable; + struct nk_style_slider slider; + struct nk_style_progress progress; + struct nk_style_property property; + struct nk_style_edit edit; + struct nk_style_chart line_chart; + struct nk_style_chart column_chart; + struct nk_style_scrollbar scrollh; + struct nk_style_scrollbar scrollv; + struct nk_style_tab tab; + struct nk_style_combo combo; + struct nk_style_window window; +}; + +NK_API struct nk_style_item nk_style_item_image(struct nk_image img); +NK_API struct nk_style_item nk_style_item_color(struct nk_color); +NK_API struct nk_style_item nk_style_item_hide(void); + +/*============================================================== + * PANEL + * =============================================================*/ +struct nk_chart { + const struct nk_style_chart *style; + enum nk_chart_type type; + float x, y, w, h; + float min, max, range; + struct nk_vec2 last; + int index; + int count; +}; + +struct nk_row_layout { + int type; + int index; + float height; + int columns; + const float *ratio; + float item_width, item_height; + float item_offset; + float filled; + struct nk_rect item; + int tree_depth; +}; + +struct nk_popup_buffer { + nk_size begin; + nk_size parent; + nk_size last; + nk_size end; + int active; +}; + +struct nk_menu_state { + float x, y, w, h; + struct nk_scroll offset; +}; + +struct nk_panel { + nk_flags flags; + struct nk_rect bounds; + struct nk_scroll *offset; + float at_x, at_y, max_x; + float width, height; + float footer_h; + float header_h; + float border; + struct nk_rect clip; + struct nk_menu_state menu; + struct nk_row_layout row; + struct nk_chart chart; + struct nk_popup_buffer popup_buffer; + struct nk_command_buffer *buffer; + struct nk_panel *parent; +}; + +/*============================================================== + * WINDOW + * =============================================================*/ +struct nk_table; + +enum nk_window_flags { + NK_WINDOW_PRIVATE = NK_FLAG(9), + /* dummy flag which mark the beginning of the private window flag part */ + NK_WINDOW_ROM = NK_FLAG(10), + /* sets the window into a read only mode and does not allow input changes */ + NK_WINDOW_HIDDEN = NK_FLAG(11), + /* Hiddes the window and stops any window interaction and drawing can be set + * by user input or by closing the window */ + NK_WINDOW_MINIMIZED = NK_FLAG(12), + /* marks the window as minimized */ + NK_WINDOW_SUB = NK_FLAG(13), + /* Marks the window as subwindow of another window*/ + NK_WINDOW_GROUP = NK_FLAG(14), + /* Marks the window as window widget group */ + NK_WINDOW_POPUP = NK_FLAG(15), + /* Marks the window as a popup window */ + NK_WINDOW_NONBLOCK = NK_FLAG(16), + /* Marks the window as a nonblock popup window */ + NK_WINDOW_CONTEXTUAL = NK_FLAG(17), + /* Marks the window as a combo box or menu */ + NK_WINDOW_COMBO = NK_FLAG(18), + /* Marks the window as a combo box */ + NK_WINDOW_MENU = NK_FLAG(19), + /* Marks the window as a menu */ + NK_WINDOW_TOOLTIP = NK_FLAG(20), + /* Marks the window as a menu */ + NK_WINDOW_REMOVE_ROM = NK_FLAG(21) + /* Removes the read only mode at the end of the window */ +}; + +struct nk_popup_state { + struct nk_window *win; + enum nk_window_flags type; + nk_hash name; + int active; + unsigned combo_count; + unsigned con_count, con_old; + unsigned active_con; +}; + +struct nk_edit_state { + nk_hash name; + unsigned int seq; + unsigned int old; + int active, prev; + int cursor; + int sel_start; + int sel_end; + struct nk_scroll scrollbar; + unsigned char insert_mode; + unsigned char single_line; +}; + +struct nk_property_state { + int active, prev; + char buffer[NK_MAX_NUMBER_BUFFER]; + int length; + int cursor; + nk_hash name; + unsigned int seq; + unsigned int old; + int state; +}; + +struct nk_window { + nk_hash name; + nk_flags flags; + unsigned int seq; + struct nk_rect bounds; + struct nk_scroll scrollbar; + struct nk_command_buffer buffer; + struct nk_panel *layout; + + /* persistent widget state */ + struct nk_property_state property; + struct nk_popup_state popup; + struct nk_edit_state edit; + + struct nk_table *tables; + unsigned short table_count; + unsigned short table_size; + + /* window list hooks */ + struct nk_window *next; + struct nk_window *prev; + struct nk_window *parent; +}; + +/*============================================================== + * CONTEXT + * =============================================================*/ +struct nk_context { +/* public: can be accessed freely */ + struct nk_input input; + struct nk_style style; + struct nk_buffer memory; + struct nk_clipboard clip; + nk_flags last_widget_state; + +/* private: + should only be accessed if you + know what you are doing */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + struct nk_draw_list draw_list; +#endif +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif + + /* text editor objects are quite big because they have a internal + * undo/redo stack. It therefore does not make sense to have one for + * each window for temporary use cases, so I only provide *one* instance + * for all windows. This works because the content is cleared anyway */ + struct nk_text_edit text_edit; + + /* windows */ + int build; + void *pool; + struct nk_window *begin; + struct nk_window *end; + struct nk_window *active; + struct nk_window *current; + struct nk_window *freelist; + unsigned int count; + unsigned int seq; +}; + +#ifdef __cplusplus +} +#endif +#endif /* NK_H_ */ + +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_IMPLEMENTATION + +#ifndef NK_POOL_DEFAULT_CAPACITY +#define NK_POOL_DEFAULT_CAPACITY 16 +#endif + +#ifndef NK_VALUE_PAGE_CAPACITY +#define NK_VALUE_PAGE_CAPACITY 32 +#endif + +#ifndef NK_DEFAULT_COMMAND_BUFFER_SIZE +#define NK_DEFAULT_COMMAND_BUFFER_SIZE (4*1024) +#endif + +#ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE +#define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024) +#endif + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +#include /* malloc, free */ +#endif +#ifdef NK_INCLUDE_STANDARD_IO +#include /* fopen, fclose,... */ +#include +#endif + +#ifndef NK_ASSERT +#include +#define NK_ASSERT(expr) assert(expr) +#endif + +#ifndef NK_MEMSET +#define NK_MEMSET nk_memset +#endif +#ifndef NK_MEMCPY +#define NK_MEMCPY nk_memcopy +#endif +#ifndef NK_SQRT +#define NK_SQRT nk_sqrt +#endif +#ifndef NK_SIN +#define NK_SIN nk_sin +#endif +#ifndef NK_COS +#define NK_COS nk_cos +#endif + +/* ============================================================== + * MATH + * =============================================================== */ +#define NK_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define NK_MAX(a,b) ((a) < (b) ? (b) : (a)) +#define NK_CLAMP(i,v,x) (NK_MAX(NK_MIN(v,x), i)) + +#define NK_PI 3.141592654f +#define NK_UTF_INVALID 0xFFFD +#define NK_MAX_FLOAT_PRECISION 2 + +#define NK_UNUSED(x) ((void)(x)) +#define NK_SATURATE(x) (NK_MAX(0, NK_MIN(1.0f, x))) +#define NK_LEN(a) (sizeof(a)/sizeof(a)[0]) +#define NK_ABS(a) (((a) < 0) ? -(a) : (a)) +#define NK_BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define NK_INBOX(px, py, x, y, w, h)\ + (NK_BETWEEN(px,x,x+w) && NK_BETWEEN(py,y,y+h)) +#define NK_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \ + (!(((x1 > (x0 + w0)) || ((x1 + w1) < x0) || (y1 > (y0 + h0)) || (y1 + h1) < y0))) +#define NK_CONTAINS(x, y, w, h, bx, by, bw, bh)\ + (NK_INBOX(x,y, bx, by, bw, bh) && NK_INBOX(x+w,y+h, bx, by, bw, bh)) + +#define nk_vec2_sub(a, b) nk_vec2((a).x - (b).x, (a).y - (b).y) +#define nk_vec2_add(a, b) nk_vec2((a).x + (b).x, (a).y + (b).y) +#define nk_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y) +#define nk_vec2_muls(a, t) nk_vec2((a).x * (t), (a).y * (t)) + +#define nk_ptr_add(t, p, i) ((t*)((void*)((nk_byte*)(p) + (i)))) +#define nk_ptr_add_const(t, p, i) ((const t*)((const void*)((const nk_byte*)(p) + (i)))) +#define nk_zero_struct(s) nk_zero(&s, sizeof(s)) + +/* ============================================================== + * ALIGNMENT + * =============================================================== */ +/* Pointer to Integer type conversion for pointer alignment */ +#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC*/ +# define NK_UINT_TO_PTR(x) ((void*)(__PTRDIFF_TYPE__)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(__PTRDIFF_TYPE__)(x)) +#elif !defined(__GNUC__) /* works for compilers other than LLVM */ +# define NK_UINT_TO_PTR(x) ((void*)&((char*)0)[x]) +# define NK_PTR_TO_UINT(x) ((nk_size)(((char*)x)-(char*)0)) +#elif defined(NK_USE_FIXED_TYPES) /* used if we have */ +# define NK_UINT_TO_PTR(x) ((void*)(uintptr_t)(x)) +# define NK_PTR_TO_UINT(x) ((uintptr_t)(x)) +#else /* generates warning but works */ +# define NK_UINT_TO_PTR(x) ((void*)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(x)) +#endif + +#define NK_ALIGN_PTR(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x) + (mask-1)) & ~(mask-1)))) +#define NK_ALIGN_PTR_BACK(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x)) & ~(mask-1)))) + +#ifdef __cplusplus +template struct nk_alignof; +template struct nk_helper{enum {value = size_diff};}; +template struct nk_helper{enum {value = nk_alignof::value};}; +template struct nk_alignof{struct Big {T x; char c;}; enum { + diff = sizeof(Big) - sizeof(T), value = nk_helper::value};}; +#define NK_ALIGNOF(t) (nk_alignof::value); +#else +#define NK_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0) +#endif + +/* make sure correct type size */ +typedef int nk__check_size[(sizeof(nk_size) >= sizeof(void*)) ? 1 : -1]; +typedef int nk__check_ptr[(sizeof(nk_ptr) == sizeof(void*)) ? 1 : -1]; +typedef int nk__check_flags[(sizeof(nk_flags) >= 4) ? 1 : -1]; +typedef int nk__check_rune[(sizeof(nk_rune) >= 4) ? 1 : -1]; +typedef int nk__check_ushort[(sizeof(nk_ushort) == 2) ? 1 : -1]; +typedef int nk__check_short[(sizeof(nk_short) == 2) ? 1 : -1]; +typedef int nk__check_uint[(sizeof(nk_uint) == 4) ? 1 : -1]; +typedef int nk__check_int[(sizeof(nk_int) == 4) ? 1 : -1]; +typedef int nk__check_byte[(sizeof(nk_byte) == 1) ? 1 : -1]; + +NK_GLOBAL const struct nk_rect nk_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; +NK_GLOBAL const float NK_FLOAT_PRECISION = 0.00000000000001f; +/* + * ============================================================== + * + * MATH + * + * =============================================================== + */ +/* Since nuklear is supposed to work on all systems providing floating point + math without any dependencies I also had to implement my own math functions + for sqrt, sin and cos. Since the actual highly accurate implementations for + the standard library functions are quite complex and I do not need high + precision for my use cases I use approximations. + + Sqrt + ---- + For square root nuklear uses the famous fast inverse square root: + https://en.wikipedia.org/wiki/Fast_inverse_square_root with + slightly tweaked magic constant. While on todays hardware it is + probably not faster it is still fast and accurate enough for + nuklears use cases. IMPORTANT: this requires float format IEEE 754 + + Sine/Cosine + ----------- + All constants inside both function are generated Remez's minimax + approximations for value range 0...2*PI. The reason why I decided to + approximate exactly that range is that nuklear only needs sine and + cosine to generate circles which only requires that exact range. + In addition I used Remez instead of Taylor for additional precision: + www.lolengine.net/blog/2011/12/21/better-function-approximatations. + + The tool I used to generate constants for both sine and cosine + (it can actually approximate a lot more functions) can be + found here: www.lolengine.net/wiki/oss/lolremez +*/ +NK_INTERN float +nk_inv_sqrt(float number) +{ + float x2; + const float threehalfs = 1.5f; + union {nk_uint i; float f;} conv = {0}; + conv.f = number; + x2 = number * 0.5f; + conv.i = 0x5f375A84 - (conv.i >> 1); + conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); + return conv.f; +} + +NK_INTERN float +nk_sqrt(float x) +{ + return x * nk_inv_sqrt(x); +} + +NK_INTERN float +nk_sin(float x) +{ + NK_STORAGE const float a0 = +1.91059300966915117e-31f; + NK_STORAGE const float a1 = +1.00086760103908896f; + NK_STORAGE const float a2 = -1.21276126894734565e-2f; + NK_STORAGE const float a3 = -1.38078780785773762e-1f; + NK_STORAGE const float a4 = -2.67353392911981221e-2f; + NK_STORAGE const float a5 = +2.08026600266304389e-2f; + NK_STORAGE const float a6 = -3.03996055049204407e-3f; + NK_STORAGE const float a7 = +1.38235642404333740e-4f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); +} + +NK_INTERN float +nk_cos(float x) +{ + NK_STORAGE const float a0 = +1.00238601909309722f; + NK_STORAGE const float a1 = -3.81919947353040024e-2f; + NK_STORAGE const float a2 = -3.94382342128062756e-1f; + NK_STORAGE const float a3 = -1.18134036025221444e-1f; + NK_STORAGE const float a4 = +1.07123798512170878e-1f; + NK_STORAGE const float a5 = -1.86637164165180873e-2f; + NK_STORAGE const float a6 = +9.90140908664079833e-4f; + NK_STORAGE const float a7 = -5.23022132118824778e-14f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); +} + +NK_INTERN nk_uint +nk_round_up_pow2(nk_uint v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +NK_API struct nk_rect +nk_get_null_rect(void) +{ + return nk_null_rect; +} + +NK_API struct nk_rect +nk_rect(float x, float y, float w, float h) +{ + struct nk_rect r; + r.x = x, r.y = y; + r.w = w, r.h = h; + return r; +} + + +NK_API struct nk_rect +nk_recti(int x, int y, int w, int h) +{ + struct nk_rect r; + r.x = (float)x; + r.y = (float)y; + r.w = (float)w; + r.h = (float)h; + return r; +} + +NK_API struct nk_rect +nk_recta(struct nk_vec2 pos, struct nk_vec2 size) +{ + return nk_rect(pos.x, pos.y, size.x, size.y); +} + +NK_API struct nk_rect +nk_rectv(const float *r) +{ + return nk_rect(r[0], r[1], r[2], r[3]); +} + +NK_API struct nk_rect +nk_rectiv(const int *r) +{ + return nk_recti(r[0], r[1], r[2], r[3]); +} + +NK_INTERN struct nk_rect +nk_shrink_rect(struct nk_rect r, float amount) +{ + struct nk_rect res; + r.w = NK_MAX(r.w, 2 * amount); + r.h = NK_MAX(r.h, 2 * amount); + res.x = r.x + amount; + res.y = r.y + amount; + res.w = r.w - 2 * amount; + res.h = r.h - 2 * amount; + return res; +} + +NK_INTERN struct nk_rect +nk_pad_rect(struct nk_rect r, struct nk_vec2 pad) +{ + r.w = NK_MAX(r.w, 2 * pad.x); + r.h = NK_MAX(r.h, 2 * pad.y); + r.x += pad.x; r.y += pad.y; + r.w -= 2 * pad.x; + r.h -= 2 * pad.y; + return r; +} + +NK_API struct nk_vec2 +nk_vec2(float x, float y) +{ + struct nk_vec2 ret; + ret.x = x; ret.y = y; + return ret; +} + +NK_API struct nk_vec2 +nk_vec2i(int x, int y) +{ + struct nk_vec2 ret; + ret.x = (float)x; + ret.y = (float)y; + return ret; +} + +NK_API struct nk_vec2 +nk_vec2v(const float *v) +{ + return nk_vec2(v[0], v[1]); +} + +NK_API struct nk_vec2 +nk_vec2iv(const int *v) +{ + return nk_vec2i(v[0], v[1]); +} +/* + * ============================================================== + * + * UTIL + * + * =============================================================== + */ +NK_INTERN int nk_str_match_here(const char *regexp, const char *text); +NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text); +NK_INTERN int nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} +NK_INTERN int nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} +NK_INTERN int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} +NK_INTERN int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} + +NK_INTERN void* +nk_memcopy(void *dst0, const void *src0, nk_size length) +{ + nk_ptr t; + char *dst = (char*)dst0; + const char *src = (const char*)src0; + if (length == 0 || dst == src) + goto done; + + #define nk_word int + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize-1) + #define NK_TLOOP(s) if (t) NK_TLOOP1(s) + #define NK_TLOOP1(s) do { s; } while (--t) + + if (dst < src) { + t = (nk_ptr)src; /* only need low bits */ + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length < nk_wsize) + t = length; + else + t = nk_wsize - (t & nk_wmask); + length -= t; + NK_TLOOP1(*dst++ = *src++); + } + t = length / nk_wsize; + NK_TLOOP(*(nk_word*)(void*)dst = *(const nk_word*)(const void*)src; + src += nk_wsize; dst += nk_wsize); + t = length & nk_wmask; + NK_TLOOP(*dst++ = *src++); + } else { + src += length; + dst += length; + t = (nk_ptr)src; + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length <= nk_wsize) + t = length; + else + t &= nk_wmask; + length -= t; + NK_TLOOP1(*--dst = *--src); + } + t = length / nk_wsize; + NK_TLOOP(src -= nk_wsize; dst -= nk_wsize; + *(nk_word*)(void*)dst = *(const nk_word*)(const void*)src); + t = length & nk_wmask; + NK_TLOOP(*--dst = *--src); + } + #undef nk_word + #undef nk_wsize + #undef nk_wmask + #undef NK_TLOOP + #undef NK_TLOOP1 +done: + return (dst0); +} + +NK_INTERN void +nk_memset(void *ptr, int c0, nk_size size) +{ + #define nk_word unsigned + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize - 1) + nk_byte *dst = (nk_byte*)ptr; + unsigned c = 0; + nk_size t = 0; + + if ((c = (nk_byte)c0) != 0) { + c = (c << 8) | c; /* at least 16-bits */ + if (sizeof(unsigned int) > 2) + c = (c << 16) | c; /* at least 32-bits*/ + } + + /* to small of a word count */ + dst = (nk_byte*)ptr; + if (size < 3 * nk_wsize) { + while (size--) *dst++ = (nk_byte)c0; + return; + } + + /* align destination */ + if ((t = NK_PTR_TO_UINT(dst) & nk_wmask) != 0) { + t = nk_wsize -t; + size -= t; + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + /* fill word */ + t = size / nk_wsize; + do { + *(nk_word*)((void*)dst) = c; + dst += nk_wsize; + } while (--t != 0); + + /* fill trailing bytes */ + t = (size & nk_wmask); + if (t != 0) { + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + #undef nk_word + #undef nk_wsize + #undef nk_wmask +} + +NK_INTERN void +nk_zero(void *ptr, nk_size size) +{ + NK_ASSERT(ptr); + NK_MEMSET(ptr, 0, size); +} + +NK_API int +nk_strlen(const char *str) +{ + int siz = 0; + NK_ASSERT(str); + while (str && *str++ != '\0') siz++; + return siz; +} + +NK_API int +nk_strtof(float *number, const char *buffer) +{ + float m; + float neg = 1.0f; + const char *p = buffer; + float floatvalue = 0; + + NK_ASSERT(number); + NK_ASSERT(buffer); + if (!number || !buffer) return 0; + *number = 0; + + /* skip whitespace */ + while (*p && *p == ' ') p++; + if (*p == '-') { + neg = -1.0f; + p++; + } + + while( *p && *p != '.' && *p != 'e' ) { + floatvalue = floatvalue * 10.0f + (float) (*p - '0'); + p++; + } + + if ( *p == '.' ) { + p++; + for(m = 0.1f; *p && *p != 'e'; p++ ) { + floatvalue = floatvalue + (float) (*p - '0') * m; + m *= 0.1f; + } + } + if ( *p == 'e' ) { + int i, pow, div; + p++; + if ( *p == '-' ) { + div = nk_true; + p++; + } else if ( *p == '+' ) { + div = nk_false; + p++; + } else div = nk_false; + + for ( pow = 0; *p; p++ ) + pow = pow * 10 + (int) (*p - '0'); + + for ( m = 1.0, i = 0; i < pow; i++ ) + m *= 10.0f; + + if ( div ) + floatvalue /= m; + else floatvalue *= m; + } + *number = floatvalue * neg; + return 1; +} + +NK_API int +nk_stricmp(const char *s1, const char *s2) +{ + nk_int c1,c2,d; + do { + c1 = *s1++; + c2 = *s2++; + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} + +NK_API int +nk_stricmpn(const char *s1, const char *s2, int n) +{ + int c1,c2,d; + assert(n >= 0); + do { + c1 = *s1++; + c2 = *s2++; + if (!n--) return 0; + + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} + +NK_INTERN int +nk_str_match_here(const char *regexp, const char *text) +{ + if (regexp[0] == '\0') + return 1; + if (regexp[1] == '*') + return nk_str_match_star(regexp[0], regexp+2, text); + if (regexp[0] == '$' && regexp[1] == '\0') + return *text == '\0'; + if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) + return nk_str_match_here(regexp+1, text+1); + return 0; +} + +NK_INTERN int +nk_str_match_star(int c, const char *regexp, const char *text) +{ + do {/* a '* matches zero or more instances */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text != '\0' && (*text++ == c || c == '.')); + return 0; +} + +NK_API int +nk_strfilter(const char *text, const char *regexp) +{ + /* + c matches any literal character c + . matches any single character + ^ matches the beginning of the input string + $ matches the end of the input string + * matches zero or more occurrences of the previous character*/ + if (regexp[0] == '^') + return nk_str_match_here(regexp+1, text); + do { /* must look even if string is empty */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text++ != '\0'); + return 0; +} + +NK_API int +nk_strmatch_fuzzy_text(const char *str, int str_len, + const char *pattern, int *out_score) +{ + /* Returns true if each character in pattern is found sequentially within str + * if found then outScore is also set. Score value has no intrinsic meaning. + * Range varies with pattern. Can only compare scores with same search pattern. */ + + /* ------- scores --------- */ + /* bonus for adjacent matches */ + #define NK_ADJACENCY_BONUS 5 + /* bonus if match occurs after a separator */ + #define NK_SEPARATOR_BONUS 10 + /* bonus if match is uppercase and prev is lower */ + #define NK_CAMEL_BONUS 10 + /* penalty applied for every letter in str before the first match */ + #define NK_LEADING_LETTER_PENALTY (-3) + /* maximum penalty for leading letters */ + #define NK_MAX_LEADING_LETTER_PENALTY (-9) + /* penalty for every letter that doesn't matter */ + #define NK_UNMATCHED_LETTER_PENALTY (-1) + + + /* loop variables */ + int score = 0; + char const * pattern_iter = pattern; + int str_iter = 0; + int prev_matched = nk_false; + int prev_lower = nk_false; + /* true so if first letter match gets separator bonus*/ + int prev_separator = nk_true; + + /* use "best" matched letter if multiple string letters match the pattern */ + char const * best_letter = 0; + int best_letter_score = 0; + + /* loop over strings */ + NK_ASSERT(str); + NK_ASSERT(pattern); + if (!str || !str_len || !pattern) return 0; + while (str_iter < str_len) + { + const char pattern_letter = *pattern_iter; + const char str_letter = str[str_iter]; + + int next_match = *pattern_iter != '\0' && + nk_to_lower(pattern_letter) == nk_to_lower(str_letter); + int rematch = best_letter && nk_to_lower(*best_letter) == nk_to_lower(str_letter); + + int advanced = next_match && best_letter; + int pattern_repeat = best_letter && pattern_iter != '\0'; + pattern_repeat = pattern_repeat && + nk_to_lower(*best_letter) == nk_to_lower(pattern_letter); + + if (advanced || pattern_repeat) { + score += best_letter_score; + best_letter = 0; + best_letter_score = 0; + } + + if (next_match || rematch) + { + int new_score = 0; + /* Apply penalty for each letter before the first pattern match */ + if (pattern_iter == pattern) + { + int count = (int)(&str[str_iter] - str); + int penalty = NK_LEADING_LETTER_PENALTY * count; + if (penalty < NK_MAX_LEADING_LETTER_PENALTY) + penalty = NK_MAX_LEADING_LETTER_PENALTY; + + score += penalty; + } + + /* apply bonus for consecutive bonuses */ + if (prev_matched) + new_score += NK_ADJACENCY_BONUS; + + /* apply bonus for matches after a separator */ + if (prev_separator) + new_score += NK_SEPARATOR_BONUS; + + /* apply bonus across camel case boundaries */ + if (prev_lower && nk_is_upper(str_letter)) + new_score += NK_CAMEL_BONUS; + + /* update pattern iter IFF the next pattern letter was matched */ + if (next_match) + ++pattern_iter; + + /* update best letter in str which may be for a "next" letter or a rematch */ + if (new_score >= best_letter_score) + { + /* apply penalty for now skipped letter */ + if (best_letter != 0) + score += NK_UNMATCHED_LETTER_PENALTY; + + best_letter = &str[str_iter]; + best_letter_score = new_score; + } + + prev_matched = nk_true; + } + else + { + score += NK_UNMATCHED_LETTER_PENALTY; + prev_matched = nk_false; + } + + /* separators should be more easily defined */ + prev_lower = nk_is_lower(str_letter) != 0; + prev_separator = str_letter == '_' || str_letter == ' '; + + ++str_iter; + } + + /* apply score for last match */ + if (best_letter) + score += best_letter_score; + + /* did not match full pattern */ + if (*pattern_iter != '\0') + return nk_false; + + if (out_score) + *out_score = score; + return nk_true; +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_API int +nk_strfmt(char *buf, int buf_size, const char *fmt,...) +{ + int w; + va_list args; + va_start(args, fmt); + w = vsnprintf(buf, (nk_size)buf_size, fmt, args); + va_end(args); + buf[buf_size-1] = 0; + return (w == -1) ?(int)buf_size:w; +} + +NK_INTERN int +nk_strfmtv(char *buf, int buf_size, const char *fmt, va_list args) +{ + int w = vsnprintf(buf, (nk_size)buf_size, fmt, args); + buf[buf_size-1] = 0; + return (w == -1) ? (int)buf_size:w; +} +#endif + +NK_INTERN int +nk_string_float_limit(char *string, int prec) +{ + int dot = 0; + char *c = string; + while (*c) { + if (*c == '.') { + dot = 1; + c++; + continue; + } + if (dot == (prec+1)) { + *c = 0; + break; + } + if (dot > 0) dot++; + c++; + } + return (int)(c - string); +} + +NK_INTERN float +nk_pow(float x, int n) +{ + /* check the sign of n */ + float r = 1; + int plus = n >= 0; + n = (plus) ? n : -n; + while (n > 0) { + if ((n & 1) == 1) + r *= x; + n /= 2; + x *= x; + } + return plus ? r : 1.0f / r; +} + +NK_INTERN int +nk_ifloor(float x) +{ + x = (float)((int)x - ((x < 0.0) ? 1 : 0)); + return (int)x; +} + +NK_INTERN int +nk_iceil(float x) +{ + if (x >= 0) { + int i = (int)x; + return i; + } else { + int t = (int)x; + float r = x - (float)t; + return (r > 0.0f) ? t+1: t; + } +} + +NK_INTERN int +nk_log10(float n) +{ + int neg; + int ret; + int exp = 0; + + neg = (n < 0) ? 1 : 0; + ret = (neg) ? (int)-n : (int)n; + while ((ret / 10) > 0) { + ret /= 10; + exp++; + } + if (neg) exp = -exp; + return exp; +} + +NK_INTERN int +nk_ftos(char *s, float n) +{ + int useExp = 0; + int digit = 0, m = 0, m1 = 0; + char *c = s; + int neg = 0; + + if (n == 0.0) { + s[0] = '0'; s[1] = '\0'; + return 1; + } + + neg = (n < 0); + if (neg) n = -n; + + /* calculate magnitude */ + m = nk_log10(n); + useExp = (m >= 14 || (neg && m >= 9) || m <= -9); + if (neg) *(c++) = '-'; + + /* set up for scientific notation */ + if (useExp) { + if (m < 0) + m -= 1; + n = n / nk_pow(10.0, m); + m1 = m; + m = 0; + } + if (m < 1.0) { + m = 0; + } + + /* convert the number */ + while (n > NK_FLOAT_PRECISION || m >= 0) { + float weight = nk_pow(10.0, m); + if (weight > 0) { + float t = (float)n / weight; + digit = nk_ifloor(t); + n -= ((float)digit * weight); + *(c++) = (char)('0' + (char)digit); + } + if (m == 0 && n > 0) + *(c++) = '.'; + m--; + } + + if (useExp) { + /* convert the exponent */ + int i, j; + *(c++) = 'e'; + if (m1 > 0) { + *(c++) = '+'; + } else { + *(c++) = '-'; + m1 = -m1; + } + m = 0; + while (m1 > 0) { + *(c++) = (char)('0' + (char)(m1 % 10)); + m1 /= 10; + m++; + } + c -= m; + for (i = 0, j = m-1; i> (32 - r))) + union {const nk_uint *i; const nk_byte *b;} conv = {0}; + const nk_byte *data = (const nk_byte*)key; + const int nblocks = len/4; + nk_uint h1 = seed; + const nk_uint c1 = 0xcc9e2d51; + const nk_uint c2 = 0x1b873593; + const nk_byte *tail; + const nk_uint *blocks; + nk_uint k1; + int i; + + /* body */ + if (!key) return 0; + conv.b = (data + nblocks*4); + blocks = (const nk_uint*)conv.i; + for (i = -nblocks; i; ++i) { + k1 = blocks[i]; + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = NK_ROTL(h1,13); + h1 = h1*5+0xe6546b64; + } + + /* tail */ + tail = (const nk_byte*)(data + nblocks*4); + k1 = 0; + switch (len & 3) { + case 3: k1 ^= (nk_uint)(tail[2] << 16); + case 2: k1 ^= (nk_uint)(tail[1] << 8u); + case 1: k1 ^= tail[0]; + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + h1 ^= k1; + default: break; + } + + /* finalization */ + h1 ^= (nk_uint)len; + /* fmix32 */ + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + #undef NK_ROTL + return h1; +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_INTERN char* +nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc) +{ + char *buf; + FILE *fd; + long ret; + + NK_ASSERT(path); + NK_ASSERT(siz); + NK_ASSERT(alloc); + if (!path || !siz || !alloc) + return 0; + + fd = fopen(path, "rb"); + if (!fd) return 0; + fseek(fd, 0, SEEK_END); + ret = ftell(fd); + if (ret < 0) { + fclose(fd); + return 0; + } + *siz = (nk_size)ret; + fseek(fd, 0, SEEK_SET); + buf = (char*)alloc->alloc(alloc->userdata,0, *siz); + NK_ASSERT(buf); + if (!buf) { + fclose(fd); + return 0; + } + *siz = (nk_size)fread(buf, *siz, 1, fd); + fclose(fd); + return buf; +} +#endif + +/* + * ============================================================== + * + * COLOR + * + * =============================================================== + */ +NK_INTERN int +nk_parse_hex(const char *p, int length) +{ + int i = 0; + int len = 0; + while (len < length) { + i <<= 4; + if (p[len] >= 'a' && p[len] <= 'f') + i += ((p[len] - 'a') + 10); + else if (p[len] >= 'A' && p[len] <= 'F') { + i += ((p[len] - 'A') + 10); + } else i += (p[len] - '0'); + len++; + } + return i; +} + +NK_API struct nk_color +nk_rgba(int r, int g, int b, int a) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)NK_CLAMP(0, a, 255); + return ret; +} + +NK_API struct nk_color +nk_rgb_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = 255; + return col; +} + +NK_API struct nk_color +nk_rgba_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = (nk_byte)nk_parse_hex(c+6, 2); + return col; +} + +NK_API void +nk_color_hex_rgba(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0x0F)); + output[1] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[2] = (char)NK_TO_HEX((col.g & 0x0F)); + output[3] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[4] = (char)NK_TO_HEX((col.b & 0x0F)); + output[5] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[6] = (char)NK_TO_HEX((col.a & 0x0F)); + output[7] = (char)NK_TO_HEX((col.a & 0xF0) >> 4); + output[8] = '\0'; + #undef NK_TO_HEX +} + +NK_API void +nk_color_hex_rgb(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0x0F)); + output[1] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[2] = (char)NK_TO_HEX((col.g & 0x0F)); + output[3] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[4] = (char)NK_TO_HEX((col.b & 0x0F)); + output[5] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[6] = '\0'; + #undef NK_TO_HEX +} + +NK_API struct nk_color +nk_rgba_iv(const int *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgba_bv(const nk_byte *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgb(int r, int g, int b) +{ + struct nk_color ret; + ret.r =(nk_byte)NK_CLAMP(0, r, 255); + ret.g =(nk_byte)NK_CLAMP(0, g, 255); + ret.b =(nk_byte)NK_CLAMP(0, b, 255); + ret.a =(nk_byte)255; + return ret; +} + +NK_API struct nk_color +nk_rgb_iv(const int *c) +{ + return nk_rgb(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_rgb_bv(const nk_byte* c) +{ + return nk_rgb(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_rgba_u32(nk_uint in) +{ + struct nk_color ret; + ret.r = (in & 0xFF); + ret.g = ((in >> 8) & 0xFF); + ret.b = ((in >> 16) & 0xFF); + ret.a = (nk_byte)((in >> 24) & 0xFF); + return ret; +} + +NK_API struct nk_color +nk_rgba_f(float r, float g, float b, float a) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f); + return ret; +} + +NK_API struct nk_color +nk_rgba_fv(const float *c) +{ + return nk_rgba_f(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgb_f(float r, float g, float b) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = 255; + return ret; +} + +NK_API struct nk_color +nk_rgb_fv(const float *c) +{ + return nk_rgb_f(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv(int h, int s, int v) +{ + return nk_hsva(h, s, v, 255); +} + +NK_API struct nk_color +nk_hsv_iv(const int *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv_bv(const nk_byte *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv_f(float h, float s, float v) +{ + return nk_hsva_f(h, s, v, 1.0f); +} + +NK_API struct nk_color +nk_hsv_fv(const float *c) +{ + return nk_hsv_f(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsva(int h, int s, int v, int a) +{ + float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f; + float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f; + float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f; + float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f; + return nk_hsva_f(hf, sf, vf, af); +} + +NK_API struct nk_color +nk_hsva_iv(const int *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_hsva_bv(const nk_byte *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_hsva_f(float h, float s, float v, float a) +{ + struct nk_colorf {float r,g,b;} out = {0,0,0}; + float p, q, t, f; + int i; + + if (s <= 0.0f) { + out.r = v; out.g = v; out.b = v; + return nk_rgb_f(out.r, out.g, out.b); + } + + h = h / (60.0f/360.0f); + i = (int)h; + f = h - (float)i; + p = v * (1.0f - s); + q = v * (1.0f - (s * f)); + t = v * (1.0f - s * (1.0f - f)); + + switch (i) { + case 0: out.r = v; out.g = t; out.b = p; break; + case 1: out.r = q; out.g = v; out.b = p; break; + case 2: out.r = p; out.g = v; out.b = t; break; + case 3: out.r = p; out.g = q; out.b = v; break; + case 4: out.r = t; out.g = p; out.b = v; break; + case 5: default: out.r = v; out.g = p; out.b = q; break; + } + return nk_rgba_f(out.r, out.g, out.b, a); +} + +NK_API struct nk_color +nk_hsva_fv(const float *c) +{ + return nk_hsva_f(c[0], c[1], c[2], c[3]); +} + +NK_API nk_uint +nk_color_u32(struct nk_color in) +{ + nk_uint out = (nk_uint)in.r; + out |= ((nk_uint)in.g << 8); + out |= ((nk_uint)in.b << 16); + out |= ((nk_uint)in.a << 24); + return out; +} + +NK_API void +nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in) +{ + NK_STORAGE const float s = 1.0f/255.0f; + *r = (float)in.r * s; + *g = (float)in.g * s; + *b = (float)in.b * s; + *a = (float)in.a * s; +} + +NK_API void +nk_color_fv(float *c, struct nk_color in) +{ + nk_color_f(&c[0], &c[1], &c[2], &c[3], in); +} + +NK_API void +nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in) +{ + float a; + nk_color_hsva_f(out_h, out_s, out_v, &a, in); +} + +NK_API void +nk_color_hsv_fv(float *out, struct nk_color in) +{ + float a; + nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in); +} + +NK_API void +nk_color_hsva_f(float *out_h, float *out_s, + float *out_v, float *out_a, struct nk_color in) +{ + float chroma; + float K = 0.0f; + float r,g,b,a; + + nk_color_f(&r,&g,&b,&a, in); + if (g < b) { + const float t = g; g = b; b = t; + K = -1.f; + } + if (r < g) { + const float t = r; r = g; g = t; + K = -2.f/6.0f - K; + } + chroma = r - ((g < b) ? g: b); + *out_h = NK_ABS(K + (g - b)/(6.0f * chroma + 1e-20f)); + *out_s = chroma / (r + 1e-20f); + *out_v = r; + *out_a = (float)in.a / 255.0f; +} + +NK_API void +nk_color_hsva_fv(float *out, struct nk_color in) +{ + nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); +} + +NK_API void +nk_color_hsva_i(int *out_h, int *out_s, int *out_v, + int *out_a, struct nk_color in) +{ + float h,s,v,a; + nk_color_hsva_f(&h, &s, &v, &a, in); + *out_h = (nk_byte)(h * 255.0f); + *out_s = (nk_byte)(s * 255.0f); + *out_v = (nk_byte)(v * 255.0f); + *out_a = (nk_byte)(a * 255.0f); +} + +NK_API void +nk_color_hsva_iv(int *out, struct nk_color in) +{ + nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); +} + +NK_API void +nk_color_hsva_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; + out[3] = (nk_byte)tmp[3]; +} + +NK_API void +nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in) +{ + int a; + nk_color_hsva_i(out_h, out_s, out_v, &a, in); +} + +NK_API void +nk_color_hsv_iv(int *out, struct nk_color in) +{ + nk_color_hsv_i(&out[0], &out[1], &out[2], in); +} + +NK_API void +nk_color_hsv_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; +} +/* + * ============================================================== + * + * IMAGE + * + * =============================================================== + */ +NK_API nk_handle +nk_handle_ptr(void *ptr) +{ + nk_handle handle = {0}; + handle.ptr = ptr; + return handle; +} + +NK_API nk_handle +nk_handle_id(int id) +{ + nk_handle handle; + handle.id = id; + return handle; +} + +NK_API struct nk_image +nk_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.ptr = ptr; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_subimage_id(int id, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_image_ptr(void *ptr) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + NK_ASSERT(ptr); + s.handle.ptr = ptr; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API struct nk_image +nk_image_id(int id) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API int +nk_image_is_subimage(const struct nk_image* img) +{ + NK_ASSERT(img); + return !(img->w == 0 && img->h == 0); +} + +NK_INTERN void +nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, + float x1, float y1) +{ + NK_ASSERT(a); + NK_ASSERT(clip); + clip->x = NK_MAX(a->x, x0); + clip->y = NK_MAX(a->y, y0); + clip->w = NK_MIN(a->x + a->w, x1) - clip->x; + clip->h = NK_MIN(a->y + a->h, y1) - clip->y; + clip->w = NK_MAX(0, clip->w); + clip->h = NK_MAX(0, clip->h); +} + +NK_API void +nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, + float pad_x, float pad_y, enum nk_heading direction) +{ + float w_half, h_half; + NK_ASSERT(result); + + r.w = NK_MAX(2 * pad_x, r.w); + r.h = NK_MAX(2 * pad_y, r.h); + r.w = r.w - 2 * pad_x; + r.h = r.h - 2 * pad_y; + + r.x = r.x + pad_x; + r.y = r.y + pad_y; + + w_half = r.w / 2.0f; + h_half = r.h / 2.0f; + + if (direction == NK_UP) { + result[0] = nk_vec2(r.x + w_half, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + r.h); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_RIGHT) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + h_half); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_DOWN) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + w_half, r.y + r.h); + } else { + result[0] = nk_vec2(r.x, r.y + h_half); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + r.w, r.y + r.h); + } +} + +NK_INTERN int +nk_text_clamp(const struct nk_user_font *font, const char *text, + int text_len, float space, int *glyphs, float *text_width) +{ + int glyph_len = 0; + float last_width = 0; + nk_rune unicode = 0; + float width = 0; + int len = 0; + int g = 0; + + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && (width < space) && (len < text_len)) { + float s; + len += glyph_len; + s = font->width(font->userdata, font->height, text, len); + + last_width = width; + width = s; + glyph_len = nk_utf_decode(&text[len], &unicode, text_len - len); + g++; + } + + *glyphs = g; + *text_width = last_width; + return len; +} + +enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE}; +NK_INTERN struct nk_vec2 +nk_text_calculate_text_bounds(const struct nk_user_font *font, + const char *begin, int byte_len, float row_height, const char **remaining, + struct nk_vec2 *out_offset, int *glyphs, int op) +{ + float line_height = row_height; + struct nk_vec2 text_size = nk_vec2(0,0); + float line_width = 0.0f; + + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + if (!begin || byte_len <= 0 || !font) + return nk_vec2(0,row_height); + + glyph_len = nk_utf_decode(begin, &unicode, byte_len); + if (!glyph_len) return text_size; + glyph_width = font->width(font->userdata, font->height, begin, glyph_len); + + *glyphs = 0; + while ((text_len < byte_len) && glyph_len) { + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + text_size.y += line_height; + line_width = 0; + *glyphs+=1; + if (op == NK_STOP_ON_NEW_LINE) + break; + + text_len++; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + if (unicode == '\r') { + text_len++; + *glyphs+=1; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + *glyphs = *glyphs + 1; + text_len += glyph_len; + line_width += (float)glyph_width; + glyph_width = font->width(font->userdata, font->height, begin+text_len, glyph_len); + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + if (text_size.x < line_width) + text_size.x = line_width; + if (out_offset) + *out_offset = nk_vec2(line_width, text_size.y + line_height); + if (line_width > 0 || text_size.y == 0.0f) + text_size.y += line_height; + if (remaining) + *remaining = begin+text_len; + return text_size; +} + +/* ============================================================== + * + * UTF-8 + * + * ===============================================================*/ +NK_GLOBAL const nk_byte nk_utfbyte[NK_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +NK_GLOBAL const nk_byte nk_utfmask[NK_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +NK_GLOBAL const nk_uint nk_utfmin[NK_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x10000}; +NK_GLOBAL const nk_uint nk_utfmax[NK_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +NK_INTERN int +nk_utf_validate(nk_rune *u, int i) +{ + NK_ASSERT(u); + if (!u) return 0; + if (!NK_BETWEEN(*u, nk_utfmin[i], nk_utfmax[i]) || + NK_BETWEEN(*u, 0xD800, 0xDFFF)) + *u = NK_UTF_INVALID; + for (i = 1; *u > nk_utfmax[i]; ++i); + return i; +} + +NK_INTERN nk_rune +nk_utf_decode_byte(char c, int *i) +{ + NK_ASSERT(i); + if (!i) return 0; + for(*i = 0; *i < (int)NK_LEN(nk_utfmask); ++(*i)) { + if (((nk_byte)c & nk_utfmask[*i]) == nk_utfbyte[*i]) + return (nk_byte)(c & ~nk_utfmask[*i]); + } + return 0; +} + +NK_API int +nk_utf_decode(const char *c, nk_rune *u, int clen) +{ + int i, j, len, type=0; + nk_rune udecoded; + + NK_ASSERT(c); + NK_ASSERT(u); + + if (!c || !u) return 0; + if (!clen) return 0; + *u = NK_UTF_INVALID; + + udecoded = nk_utf_decode_byte(c[0], &len); + if (!NK_BETWEEN(len, 1, NK_UTF_SIZE)) + return 1; + + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | nk_utf_decode_byte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + nk_utf_validate(u, len); + return len; +} + +NK_INTERN char +nk_utf_encode_byte(nk_rune u, int i) +{ + return (char)((nk_utfbyte[i]) | ((nk_byte)u & ~nk_utfmask[i])); +} + +NK_API int +nk_utf_encode(nk_rune u, char *c, int clen) +{ + int len, i; + len = nk_utf_validate(&u, 0); + if (clen < len || !len || len > NK_UTF_SIZE) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = nk_utf_encode_byte(u, 0); + u >>= 6; + } + c[0] = nk_utf_encode_byte(u, len); + return len; +} + +NK_API int +nk_utf_len(const char *str, int len) +{ + const char *text; + int glyphs = 0; + int text_len; + int glyph_len; + int src_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + if (!str || !len) return 0; + + text = str; + text_len = len; + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && src_len < len) { + glyphs++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, &unicode, text_len - src_len); + } + return glyphs; +} + +NK_API const char* +nk_utf_at(const char *buffer, int length, int index, + nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + const char *text; + int text_len; + + NK_ASSERT(buffer); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!buffer || !unicode || !len) return 0; + if (index < 0) { + *unicode = NK_UTF_INVALID; + *len = 0; + return 0; + } + + text = buffer; + text_len = length; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == index) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != index) return 0; + return buffer + src_len; +} + +/* ============================================================== + * + * BUFFER + * + * ===============================================================*/ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_INTERN void* nk_malloc(nk_handle unused, void *old,nk_size size) +{NK_UNUSED(unused); NK_UNUSED(old); return malloc(size);} +NK_INTERN void nk_mfree(nk_handle unused, void *ptr) +{NK_UNUSED(unused); free(ptr);} + +NK_API void +nk_buffer_init_default(struct nk_buffer *buffer) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(buffer, &alloc, NK_BUFFER_DEFAULT_INITIAL_SIZE); +} +#endif + +NK_API void +nk_buffer_init(struct nk_buffer *b, const struct nk_allocator *a, + nk_size initial_size) +{ + NK_ASSERT(b); + NK_ASSERT(a); + NK_ASSERT(initial_size); + if (!b || !a || !initial_size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_DYNAMIC; + b->memory.ptr = a->alloc(a->userdata,0, initial_size); + b->memory.size = initial_size; + b->size = initial_size; + b->grow_factor = 2.0f; + b->pool = *a; +} + +NK_API void +nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size) +{ + NK_ASSERT(b); + NK_ASSERT(m); + NK_ASSERT(size); + if (!b || !m || !size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_FIXED; + b->memory.ptr = m; + b->memory.size = size; + b->size = size; +} + +NK_INTERN void* +nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, + enum nk_buffer_allocation_type type) +{ + void *memory = 0; + switch (type) { + default: + case NK_BUFFER_MAX: + case NK_BUFFER_FRONT: + if (align) { + memory = NK_ALIGN_PTR(unaligned, align); + *alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); + } else { + memory = unaligned; + *alignment = 0; + } + break; + case NK_BUFFER_BACK: + if (align) { + memory = NK_ALIGN_PTR_BACK(unaligned, align); + *alignment = (nk_size)((nk_byte*)unaligned - (nk_byte*)memory); + } else { + memory = unaligned; + *alignment = 0; + } + break; + } + return memory; +} + +NK_INTERN void* +nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size) +{ + void *temp; + nk_size buffer_size; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size || !b->pool.alloc || !b->pool.free) + return 0; + + buffer_size = b->memory.size; + temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity); + NK_ASSERT(temp); + if (!temp) return 0; + + *size = capacity; + if (temp != b->memory.ptr) { + NK_MEMCPY(temp, b->memory.ptr, buffer_size); + b->pool.free(b->pool.userdata, b->memory.ptr); + } + + if (b->size == buffer_size) { + /* no back buffer so just set correct size */ + b->size = capacity; + return temp; + } else { + /* copy back buffer to the end of the new buffer */ + void *dst, *src; + nk_size back_size; + back_size = buffer_size - b->size; + dst = nk_ptr_add(void, temp, capacity - back_size); + src = nk_ptr_add(void, temp, b->size); + NK_MEMCPY(dst, src, back_size); + b->size = capacity - back_size; + } + return temp; +} + +NK_INTERN void* +nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, + nk_size size, nk_size align) +{ + int full; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size) return 0; + b->needed += size; + + /* calculate total size with needed alignment + size */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + + /* check if buffer has enough memory*/ + if (type == NK_BUFFER_FRONT) + full = ((b->allocated + size + alignment) > b->size); + else full = ((b->size - (size + alignment)) <= b->allocated); + + if (full) { + nk_size capacity; + NK_ASSERT(b->type == NK_BUFFER_DYNAMIC); + NK_ASSERT(b->pool.alloc && b->pool.free); + if (b->type != NK_BUFFER_DYNAMIC || !b->pool.alloc || !b->pool.free) + return 0; + + /* buffer is full so allocate bigger buffer if dynamic */ + capacity = (nk_size)((float)b->memory.size * b->grow_factor); + capacity = NK_MAX(capacity, nk_round_up_pow2((nk_uint)(b->allocated + size))); + b->memory.ptr = nk_buffer_realloc(b, capacity, &b->memory.size); + if (!b->memory.ptr) return 0; + + /* align newly allocated pointer */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + } + + if (type == NK_BUFFER_FRONT) + b->allocated += size + alignment; + else b->size -= (size + alignment); + b->needed += alignment; + b->calls++; + return memory; +} + +NK_API void +nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type, + void *memory, nk_size size, nk_size align) +{ + void *mem = nk_buffer_alloc(b, type, size, align); + if (!mem) return; + NK_MEMCPY(mem, memory, size); +} + +NK_API void +nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + buffer->marker[type].active = nk_true; + if (type == NK_BUFFER_BACK) + buffer->marker[type].offset = buffer->size; + else buffer->marker[type].offset = buffer->allocated; +} + +NK_API void +nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + if (type == NK_BUFFER_BACK) { + /* reset back buffer either back to marker or empty */ + buffer->needed -= (buffer->memory.size - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->size = buffer->marker[type].offset; + else buffer->size = buffer->memory.size; + buffer->marker[type].active = nk_false; + } else { + /* reset front buffer either back to back marker or empty */ + buffer->needed -= (buffer->allocated - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->allocated = buffer->marker[type].offset; + else buffer->allocated = 0; + buffer->marker[type].active = nk_false; + } +} + +NK_API void +nk_buffer_clear(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b) return; + b->allocated = 0; + b->size = b->memory.size; + b->calls = 0; + b->needed = 0; +} + +NK_API void +nk_buffer_free(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b || !b->memory.ptr) return; + if (b->type == NK_BUFFER_FIXED) return; + if (!b->pool.free) return; + NK_ASSERT(b->pool.free); + b->pool.free(b->pool.userdata, b->memory.ptr); +} + +NK_API void +nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b) +{ + NK_ASSERT(b); + NK_ASSERT(s); + if (!s || !b) return; + s->allocated = b->allocated; + s->size = b->memory.size; + s->needed = b->needed; + s->memory = b->memory.ptr; + s->calls = b->calls; +} + +NK_API void* +nk_buffer_memory(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} + +NK_API const void* +nk_buffer_memory_const(const struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} + +NK_API nk_size +nk_buffer_total(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.size; +} + +/* + * ============================================================== + * + * STRING + * + * =============================================================== + */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_str_init_default(struct nk_str *str) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(&str->buffer, &alloc, 32); + str->len = 0; +} +#endif + +NK_API void +nk_str_init(struct nk_str *str, const struct nk_allocator *alloc, nk_size size) +{ + nk_buffer_init(&str->buffer, alloc, size); + str->len = 0; +} + +NK_API void +nk_str_init_fixed(struct nk_str *str, void *memory, nk_size size) +{ + nk_buffer_init_fixed(&str->buffer, memory, size); + str->len = 0; +} + +NK_API int +nk_str_append_text_char(struct nk_str *s, const char *str, int len) +{ + char *mem; + NK_ASSERT(s); + NK_ASSERT(str); + if (!s || !str || !len) return 0; + mem = (char*)nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len += nk_utf_len(str, len); + return len; +} + +NK_API int +nk_str_append_str_char(struct nk_str *s, const char *str) +{ + return nk_str_append_text_char(s, str, nk_strlen(str)); +} + +NK_API int +nk_str_append_text_utf8(struct nk_str *str, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_append_text_char(str, text, byte_len); + return len; +} + +NK_API int +nk_str_append_str_utf8(struct nk_str *str, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_append_text_char(str, text, byte_len); + return runes; +} + +NK_API int +nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(text[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_append_text_char(str, glyph, byte_len); + } + return len; +} + +NK_API int +nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_append_text_char(str, glyph, byte_len); + i++; + } + return i; +} + +NK_API int +nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len) +{ + int i; + void *mem; + char *src; + char *dst; + + int copylen; + NK_ASSERT(s); + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!s || !str || !len || (nk_size)pos > s->buffer.allocated) return 0; + if ((s->buffer.allocated + (nk_size)len >= s->buffer.memory.size) && + (s->buffer.type == NK_BUFFER_FIXED)) return 0; + + copylen = (int)s->buffer.allocated - pos; + if (!copylen) { + nk_str_append_text_char(s, str, len); + return 1; + } + mem = nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + + /* memmove */ + NK_ASSERT(((int)pos + (int)len + ((int)copylen - 1)) >= 0); + NK_ASSERT(((int)pos + ((int)copylen - 1)) >= 0); + dst = nk_ptr_add(char, s->buffer.memory.ptr, pos + len + (copylen - 1)); + src = nk_ptr_add(char, s->buffer.memory.ptr, pos + (copylen-1)); + for (i = 0; i < copylen; ++i) *dst-- = *src--; + mem = nk_ptr_add(void, s->buffer.memory.ptr, pos); + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len = nk_utf_len(s->buffer.memory.ptr, (int)s->buffer.allocated); + return 1; +} + +NK_API int +nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len) +{ + int glyph_len; + nk_rune unicode; + const char *begin; + const char *buffer; + + NK_ASSERT(str); + NK_ASSERT(cstr); + NK_ASSERT(len); + if (!str || !cstr || !len) return 0; + begin = nk_str_at_rune(str, pos, &unicode, &glyph_len); + buffer = nk_str_get_const(str); + if (!begin) return 0; + return nk_str_insert_text_char(str, (int)(begin - buffer), cstr, len); +} + +NK_API int nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len) +{return nk_str_insert_at_char(str, pos, text, len);} + +NK_API int nk_str_insert_str_char(struct nk_str *str, int pos, const char *text) +{return nk_str_insert_at_char(str, pos, text, nk_strlen(text));} + +NK_API int +nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(text); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_insert_at_rune(str, pos, text, byte_len); + return len; +} + +NK_API int +nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_insert_at_rune(str, pos, text, byte_len); + return runes; +} + +NK_API int +nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !runes || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + } + return len; +} + +NK_API int +nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + i++; + } + return i; +} + +NK_API void +nk_str_remove_chars(struct nk_str *s, int len) +{ + NK_ASSERT(s); + NK_ASSERT(len >= 0); + if (!s || len < 0 || (nk_size)len > s->buffer.allocated) return; + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + s->len = nk_utf_len(s->buffer.memory.ptr, (int)s->buffer.allocated); +} + +NK_API void +nk_str_remove_runes(struct nk_str *str, int len) +{ + int index; + const char *begin; + const char *end; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!str || len < 0) return; + if (len >= str->len) { + str->len = 0; + return; + } + + index = str->len - len; + begin = nk_str_at_rune(str, index, &unicode, &len); + end = (const char*)str->buffer.memory.ptr + str->buffer.allocated; + nk_str_remove_chars(str, (int)(end-begin)+1); +} + +NK_API void +nk_str_delete_chars(struct nk_str *s, int pos, int len) +{ + NK_ASSERT(s); + if (!s || !len || (nk_size)pos > s->buffer.allocated || + (nk_size)(pos + len) > s->buffer.allocated) return; + + if ((nk_size)(pos + len) < s->buffer.allocated) { + /* memmove */ + char *dst = nk_ptr_add(char, s->buffer.memory.ptr, pos); + char *src = nk_ptr_add(char, s->buffer.memory.ptr, pos + len); + NK_MEMCPY(dst, src, s->buffer.allocated - (nk_size)(pos + len)); + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + } else nk_str_remove_chars(s, len); + s->len = nk_utf_len(s->buffer.memory.ptr, (int)s->buffer.allocated); +} + +NK_API void +nk_str_delete_runes(struct nk_str *s, int pos, int len) +{ + char *temp; + nk_rune unicode; + char *begin; + char *end; + int unused; + + NK_ASSERT(s); + NK_ASSERT(s->len >= pos + len); + if (s->len < pos + len) + len = NK_CLAMP(0, (s->len - pos), s->len); + if (!len) return; + + temp = s->buffer.memory.ptr; + begin = nk_str_at_rune(s, pos, &unicode, &unused); + if (!begin) return; + s->buffer.memory.ptr = begin; + end = nk_str_at_rune(s, len, &unicode, &unused); + s->buffer.memory.ptr = temp; + if (!end) return; + nk_str_delete_chars(s, (int)(begin - temp), (int)(end - begin)); +} + +NK_API char* +nk_str_at_char(struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} + +NK_API char* +nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i+= glyph_len; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} + +NK_API const char* +nk_str_at_char_const(const struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} + +NK_API const char* +nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} + +NK_API nk_rune +nk_str_rune_at(const struct nk_str *str, int pos) +{ + int len; + nk_rune unicode = 0; + nk_str_at_const(str, pos, &unicode, &len); + return unicode; +} + +NK_API char* +nk_str_get(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (char*)s->buffer.memory.ptr; +} + +NK_API const char* +nk_str_get_const(const struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (const char*)s->buffer.memory.ptr; +} + +NK_API int +nk_str_len(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return s->len; +} + +NK_API int +nk_str_len_char(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (int)s->buffer.allocated; +} + +NK_API void +nk_str_clear(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_clear(&str->buffer); + str->len = 0; +} + +NK_API void +nk_str_free(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_free(&str->buffer); + str->len = 0; +} + +/* + * ============================================================== + * + * Command buffer + * + * =============================================================== +*/ +NK_INTERN void +nk_command_buffer_init(struct nk_command_buffer *cmdbuf, + struct nk_buffer *buffer, enum nk_command_clipping clip) +{ + NK_ASSERT(cmdbuf); + NK_ASSERT(buffer); + if (!cmdbuf || !buffer) return; + cmdbuf->base = buffer; + cmdbuf->use_clipping = clip; + cmdbuf->begin = buffer->allocated; + cmdbuf->end = buffer->allocated; + cmdbuf->last = buffer->allocated; +} + +NK_INTERN void +nk_command_buffer_reset(struct nk_command_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return; + buffer->begin = 0; + buffer->end = 0; + buffer->last = 0; + buffer->clip = nk_null_rect; +#ifdef NK_INCLUDE_COMMAND_USERDATA + buffer->userdata.ptr = 0; +#endif +} + +NK_INTERN void* +nk_command_buffer_push(struct nk_command_buffer* b, + enum nk_command_type t, nk_size size) +{ + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_command); + struct nk_command *cmd; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(b->base); + if (!b) return 0; + + cmd = (struct nk_command*)nk_buffer_alloc(b->base,NK_BUFFER_FRONT,size,align); + if (!cmd) return 0; + + /* make sure the offset to the next command is aligned */ + b->last = (nk_size)((nk_byte*)cmd - (nk_byte*)b->base->memory.ptr); + unaligned = (nk_byte*)cmd + size; + memory = NK_ALIGN_PTR(unaligned, align); + alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); + + cmd->type = t; + cmd->next = b->base->allocated + alignment; +#ifdef NK_INCLUDE_COMMAND_USERDATA + cmd->userdata = b->userdata; +#endif + b->end = cmd->next; + return cmd; +} + +NK_API void +nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r) +{ + struct nk_command_scissor *cmd; + NK_ASSERT(b); + if (!b) return; + + b->clip.x = r.x; + b->clip.y = r.y; + b->clip.w = r.w; + b->clip.h = r.h; + cmd = (struct nk_command_scissor*) + nk_command_buffer_push(b, NK_COMMAND_SCISSOR, sizeof(*cmd)); + + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); +} + +NK_API void +nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, + float x1, float y1, float line_thickness, struct nk_color c) +{ + struct nk_command_line *cmd; + NK_ASSERT(b); + if (!b) return; + cmd = (struct nk_command_line*) + nk_command_buffer_push(b, NK_COMMAND_LINE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)x0; + cmd->begin.y = (short)y0; + cmd->end.x = (short)x1; + cmd->end.y = (short)y1; + cmd->color = c; +} + +NK_API void +nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay, + float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y, + float bx, float by, float line_thickness, struct nk_color col) +{ + struct nk_command_curve *cmd; + NK_ASSERT(b); + if (!b || col.a == 0) return; + + cmd = (struct nk_command_curve*) + nk_command_buffer_push(b, NK_COMMAND_CURVE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)ax; + cmd->begin.y = (short)ay; + cmd->ctrl[0].x = (short)ctrl0x; + cmd->ctrl[0].y = (short)ctrl0y; + cmd->ctrl[1].x = (short)ctrl1x; + cmd->ctrl[1].y = (short)ctrl1y; + cmd->end.x = (short)bx; + cmd->end.y = (short)by; + cmd->color = col; +} + +NK_API void +nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, float line_thickness, struct nk_color c) +{ + struct nk_command_rect *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect*) + nk_command_buffer_push(b, NK_COMMAND_RECT, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} + +NK_API void +nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, struct nk_color c) +{ + struct nk_command_rect_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_filled*) + nk_command_buffer_push(b, NK_COMMAND_RECT_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} + +NK_API void +nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + struct nk_command_rect_multi_color *cmd; + NK_ASSERT(b); + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_multi_color*) + nk_command_buffer_push(b, NK_COMMAND_RECT_MULTI_COLOR, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->left = left; + cmd->top = top; + cmd->right = right; + cmd->bottom = bottom; +} + +NK_API void +nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r, + float line_thickness, struct nk_color c) +{ + struct nk_command_circle *cmd; + if (!b || c.a == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} + +NK_API void +nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c) +{ + struct nk_command_circle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle_filled*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} + +NK_API void +nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, float line_thickness, struct nk_color c) +{ + struct nk_command_arc *cmd; + if (!b || c.a == 0) return; + cmd = (struct nk_command_arc*) + nk_command_buffer_push(b, NK_COMMAND_ARC, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} + +NK_API void +nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, struct nk_color c) +{ + struct nk_command_arc_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + cmd = (struct nk_command_arc_filled*) + nk_command_buffer_push(b, NK_COMMAND_ARC_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} + +NK_API void +nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, float line_thickness, struct nk_color c) +{ + struct nk_command_triangle *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) || + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) || + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} + +NK_API void +nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, struct nk_color c) +{ + struct nk_command_triangle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) || + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) || + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle_filled*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} + +NK_API void +nk_stroke_polygon(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon*) nk_command_buffer_push(b, NK_COMMAND_POLYGON, size); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count, + struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon_filled *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon_filled*) + nk_command_buffer_push(b, NK_COMMAND_POLYGON_FILLED, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polyline *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polyline*) nk_command_buffer_push(b, NK_COMMAND_POLYLINE, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + cmd->line_thickness = (unsigned short)line_thickness; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_draw_image(struct nk_command_buffer *b, struct nk_rect r, + const struct nk_image *img) +{ + struct nk_command_image *cmd; + NK_ASSERT(b); + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + cmd = (struct nk_command_image*) + nk_command_buffer_push(b, NK_COMMAND_IMAGE, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); + cmd->img = *img; +} + +NK_API void +nk_draw_text(struct nk_command_buffer *b, struct nk_rect r, + const char *string, int length, const struct nk_user_font *font, + struct nk_color bg, struct nk_color fg) +{ + float text_width = 0; + struct nk_command_text *cmd; + + NK_ASSERT(b); + NK_ASSERT(font); + if (!b || !string || !length || (bg.a == 0 && fg.a == 0)) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + /* make sure text fits inside bounds */ + text_width = font->width(font->userdata, font->height, string, length); + if (text_width > r.w){ + float txt_width = (float)text_width; + int glyphs = 0; + length = nk_text_clamp(font, string, length, + r.w, &glyphs, &txt_width); + } + + if (!length) return; + cmd = (struct nk_command_text*) + nk_command_buffer_push(b, NK_COMMAND_TEXT, sizeof(*cmd) + (nk_size)(length + 1)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)r.w; + cmd->h = (unsigned short)r.h; + cmd->background = bg; + cmd->foreground = fg; + cmd->font = font; + cmd->length = length; + cmd->height = font->height; + NK_MEMCPY(cmd->string, string, (nk_size)length); + cmd->string[length] = '\0'; +} + +/* ============================================================== + * + * DRAW LIST + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_API void +nk_draw_list_init(struct nk_draw_list *list) +{ + nk_size i = 0; + nk_zero(list, sizeof(*list)); + for (i = 0; i < NK_LEN(list->circle_vtx); ++i) { + const float a = ((float)i / (float)NK_LEN(list->circle_vtx)) * 2 * NK_PI; + list->circle_vtx[i].x = (float)NK_COS(a); + list->circle_vtx[i].y = (float)NK_SIN(a); + } +} + +NK_API void +nk_draw_list_setup(struct nk_draw_list *canvas, float global_alpha, + enum nk_anti_aliasing line_AA, enum nk_anti_aliasing shape_AA, + struct nk_draw_null_texture null, struct nk_buffer *cmds, + struct nk_buffer *vertices, struct nk_buffer *elements) +{ + canvas->null = null; + canvas->clip_rect = nk_null_rect; + canvas->vertices = vertices; + canvas->elements = elements; + canvas->buffer = cmds; + canvas->line_AA = line_AA; + canvas->shape_AA = shape_AA; + canvas->global_alpha = global_alpha; +} + +NK_API const struct nk_draw_command* +nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) +{ + nk_byte *memory; + nk_size offset; + const struct nk_draw_command *cmd; + + NK_ASSERT(buffer); + if (!buffer || !buffer->size || !canvas->cmd_count) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + offset = buffer->memory.size - canvas->cmd_offset; + cmd = nk_ptr_add(const struct nk_draw_command, memory, offset); + return cmd; +} + +NK_API const struct nk_draw_command* +nk__draw_list_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_draw_list *canvas) +{ + nk_byte *memory; + nk_size size; + nk_size offset; + const struct nk_draw_command *end; + + NK_ASSERT(buffer); + NK_ASSERT(canvas); + if (!cmd || !buffer || !canvas) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + size = buffer->memory.size; + offset = size - canvas->cmd_offset; + end = nk_ptr_add(const struct nk_draw_command, memory, offset); + end -= (canvas->cmd_count-1); + + if (cmd <= end) return 0; + return (cmd-1); +} + +NK_API void +nk_draw_list_clear(struct nk_draw_list *list) +{ + NK_ASSERT(list); + if (!list) return; + if (list->buffer) + nk_buffer_clear(list->buffer); + if (list->elements) + nk_buffer_clear(list->vertices); + if (list->vertices) + nk_buffer_clear(list->elements); + + list->element_count = 0; + list->vertex_count = 0; + list->cmd_offset = 0; + list->cmd_count = 0; + list->path_count = 0; + list->vertices = 0; + list->elements = 0; + list->clip_rect = nk_null_rect; +} + +NK_INTERN struct nk_vec2* +nk_draw_list_alloc_path(struct nk_draw_list *list, int count) +{ + struct nk_vec2 *points; + NK_STORAGE const nk_size point_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size point_size = sizeof(struct nk_vec2); + points = (struct nk_vec2*) + nk_buffer_alloc(list->buffer, NK_BUFFER_FRONT, + point_size * (nk_size)count, point_align); + + if (!points) return 0; + if (!list->path_offset) { + void *memory = nk_buffer_memory(list->buffer); + list->path_offset = (unsigned int)((nk_byte*)points - (nk_byte*)memory); + } + list->path_count += (unsigned int)count; + return points; +} + +NK_INTERN struct nk_vec2 +nk_draw_list_path_last(struct nk_draw_list *list) +{ + void *memory; + struct nk_vec2 *point; + NK_ASSERT(list->path_count); + memory = nk_buffer_memory(list->buffer); + point = nk_ptr_add(struct nk_vec2, memory, list->path_offset); + point += (list->path_count-1); + return *point; +} + +NK_INTERN struct nk_draw_command* +nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip, + nk_handle texture) +{ + NK_STORAGE const nk_size cmd_align = NK_ALIGNOF(struct nk_draw_command); + NK_STORAGE const nk_size cmd_size = sizeof(struct nk_draw_command); + struct nk_draw_command *cmd; + + NK_ASSERT(list); + cmd = (struct nk_draw_command*) + nk_buffer_alloc(list->buffer, NK_BUFFER_BACK, cmd_size, cmd_align); + + if (!cmd) return 0; + if (!list->cmd_count) { + nk_byte *memory = (nk_byte*)nk_buffer_memory(list->buffer); + nk_size total = nk_buffer_total(list->buffer); + memory = nk_ptr_add(nk_byte, memory, total); + list->cmd_offset = (nk_size)(memory - (nk_byte*)cmd); + } + + cmd->elem_count = 0; + cmd->clip_rect = clip; + cmd->texture = texture; + + list->cmd_count++; + list->clip_rect = clip; + return cmd; +} + +NK_INTERN struct nk_draw_command* +nk_draw_list_command_last(struct nk_draw_list *list) +{ + void *memory; + nk_size size; + struct nk_draw_command *cmd; + NK_ASSERT(list->cmd_count); + + memory = nk_buffer_memory(list->buffer); + size = nk_buffer_total(list->buffer); + cmd = nk_ptr_add(struct nk_draw_command, memory, size - list->cmd_offset); + return (cmd - (list->cmd_count-1)); +} + +NK_INTERN void +nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, rect, list->null.texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) + prev->clip_rect = rect; + nk_draw_list_push_command(list, rect, prev->texture); + } +} + +NK_INTERN void +nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, nk_null_rect, list->null.texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) + prev->texture = texture; + else if (prev->texture.id != texture.id) + nk_draw_list_push_command(list, prev->clip_rect, texture); + } +} + +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + struct nk_draw_command *prev; + nk_draw_list_push_command(list, nk_null_rect, list->null.texture); + prev = nk_draw_list_command_last(list); + prev->userdata = userdata; + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) { + prev->userdata = userdata; + } else if (prev->userdata.ptr != userdata.ptr) { + nk_draw_list_push_command(list, prev->clip_rect, prev->texture); + prev = nk_draw_list_command_last(list); + prev->userdata = userdata; + } + } +} +#endif + +NK_INTERN struct nk_draw_vertex* +nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count) +{ + struct nk_draw_vertex *vtx; + NK_STORAGE const nk_size vtx_align = NK_ALIGNOF(struct nk_draw_vertex); + NK_STORAGE const nk_size vtx_size = sizeof(struct nk_draw_vertex); + NK_ASSERT(list); + if (!list) return 0; + + vtx = (struct nk_draw_vertex*) + nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, vtx_size*count, vtx_align); + if (!vtx) return 0; + list->vertex_count += (unsigned int)count; + return vtx; +} + +NK_INTERN nk_draw_index* +nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count) +{ + nk_draw_index *ids; + struct nk_draw_command *cmd; + NK_STORAGE const nk_size elem_align = NK_ALIGNOF(nk_draw_index); + NK_STORAGE const nk_size elem_size = sizeof(nk_draw_index); + NK_ASSERT(list); + if (!list) return 0; + + ids = (nk_draw_index*) + nk_buffer_alloc(list->elements, NK_BUFFER_FRONT, elem_size*count, elem_align); + if (!ids) return 0; + cmd = nk_draw_list_command_last(list); + list->element_count += (unsigned int)count; + cmd->elem_count += (unsigned int)count; + return ids; +} + +NK_INTERN struct nk_draw_vertex +nk_draw_vertex(struct nk_vec2 pos, struct nk_vec2 uv, nk_draw_vertex_color col) +{ + struct nk_draw_vertex out; + out.position = pos; + out.uv = uv; + out.col = col; + return out; +} + +NK_API void +nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *points, + const unsigned int points_count, struct nk_color color, enum nk_draw_list_stroke closed, + float thickness, enum nk_anti_aliasing aliasing) +{ + nk_size count; + int thick_line; + nk_draw_vertex_color col; + NK_ASSERT(list); + if (!list || points_count < 2) return; + + color.a = (nk_byte)((float)color.a * list->global_alpha); + col = nk_color_u32(color); + count = points_count; + if (!closed) count = points_count-1; + thick_line = thickness > 1.0f; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + if (aliasing == NK_ANTI_ALIASING_ON) { + /* ANTI-ALIASED STROKE */ + const float AA_SIZE = 1.0f; + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + const nk_draw_vertex_color col_trans = col & 0x00ffffff; + + /* allocate vertices and elements */ + nk_size i1 = 0; + nk_size index = list->vertex_count; + const nk_size idx_count = (thick_line) ? (count * 18) : (count * 12); + const nk_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3); + struct nk_draw_vertex *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + nk_size size; + struct nk_vec2 *normals, *temp; + if (!vtx || !ids) return; + + /* temporary allocate normals + points */ + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * ((thick_line) ? 5 : 3) * points_count; + normals = (struct nk_vec2*) + nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + if (!normals) return; + temp = normals + points_count; + + /* calculate normals */ + for (i1 = 0; i1 < count; ++i1) { + const nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + struct nk_vec2 diff = nk_vec2_sub(points[i2], points[i1]); + float len; + + /* vec2 inverted lenth */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + + diff = nk_vec2_muls(diff, len); + normals[i1].x = diff.y; + normals[i1].y = -diff.x; + } + + if (!closed) + normals[points_count-1] = normals[points_count-2]; + + if (!thick_line) { + nk_size idx1, i; + if (!closed) { + struct nk_vec2 d; + temp[0] = nk_vec2_add(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + temp[1] = nk_vec2_sub(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + d = nk_vec2_muls(normals[points_count-1], AA_SIZE); + temp[(points_count-1) * 2 + 0] = nk_vec2_add(points[points_count-1], d); + temp[(points_count-1) * 2 + 1] = nk_vec2_sub(points[points_count-1], d); + } + + /* fill elements */ + idx1 = index; + for (i1 = 0; i1 < count; i1++) { + struct nk_vec2 dm; + float dmr2; + nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3); + + /* average normals */ + dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm = nk_vec2_muls(dm, AA_SIZE); + temp[i2*2+0] = nk_vec2_add(points[i2], dm); + temp[i2*2+1] = nk_vec2_sub(points[i2], dm); + + ids[0] = (nk_draw_index)(idx2 + 0); ids[1] = (nk_draw_index)(idx1+0); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+0); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11]= (nk_draw_index)(idx2+1); + ids += 12; + idx1 = idx2; + } + + /* fill vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->null.uv; + vtx[0] = nk_draw_vertex(points[i], uv, col); + vtx[1] = nk_draw_vertex(temp[i*2+0], uv, col_trans); + vtx[2] = nk_draw_vertex(temp[i*2+1], uv, col_trans); + vtx += 3; + } + } else { + nk_size idx1, i; + const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + if (!closed) { + struct nk_vec2 d1 = nk_vec2_muls(normals[0], half_inner_thickness + AA_SIZE); + struct nk_vec2 d2 = nk_vec2_muls(normals[0], half_inner_thickness); + + temp[0] = nk_vec2_add(points[0], d1); + temp[1] = nk_vec2_add(points[0], d2); + temp[2] = nk_vec2_sub(points[0], d2); + temp[3] = nk_vec2_sub(points[0], d1); + + d1 = nk_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE); + d2 = nk_vec2_muls(normals[points_count-1], half_inner_thickness); + + temp[(points_count-1)*4+0] = nk_vec2_add(points[points_count-1], d1); + temp[(points_count-1)*4+1] = nk_vec2_add(points[points_count-1], d2); + temp[(points_count-1)*4+2] = nk_vec2_sub(points[points_count-1], d2); + temp[(points_count-1)*4+3] = nk_vec2_sub(points[points_count-1], d1); + } + + /* add all elements */ + idx1 = index; + for (i1 = 0; i1 < count; ++i1) { + struct nk_vec2 dm_out, dm_in; + const nk_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4); + + /* average normals */ + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + float dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm_out = nk_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE)); + dm_in = nk_vec2_muls(dm, half_inner_thickness); + temp[i2*4+0] = nk_vec2_add(points[i2], dm_out); + temp[i2*4+1] = nk_vec2_add(points[i2], dm_in); + temp[i2*4+2] = nk_vec2_sub(points[i2], dm_in); + temp[i2*4+3] = nk_vec2_sub(points[i2], dm_out); + + /* add indexes */ + ids[0] = (nk_draw_index)(idx2 + 1); ids[1] = (nk_draw_index)(idx1+1); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+1); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11] = (nk_draw_index)(idx2+1); + ids[12]= (nk_draw_index)(idx2 + 2); ids[13] = (nk_draw_index)(idx1+2); + ids[14]= (nk_draw_index)(idx1 + 3); ids[15] = (nk_draw_index)(idx1+3); + ids[16]= (nk_draw_index)(idx2 + 3); ids[17] = (nk_draw_index)(idx2+2); + ids += 18; + idx1 = idx2; + } + + /* add vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->null.uv; + vtx[0] = nk_draw_vertex(temp[i*4+0], uv, col_trans); + vtx[1] = nk_draw_vertex(temp[i*4+1], uv, col); + vtx[2] = nk_draw_vertex(temp[i*4+2], uv, col); + vtx[3] = nk_draw_vertex(temp[i*4+3], uv, col_trans); + vtx += 4; + } + } + + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + /* NON ANTI-ALIASED STROKE */ + nk_size i1 = 0; + nk_size idx = list->vertex_count; + const nk_size idx_count = count * 6; + const nk_size vtx_count = count * 4; + struct nk_draw_vertex *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + if (!vtx || !ids) return; + + for (i1 = 0; i1 < count; ++i1) { + float dx, dy; + const struct nk_vec2 uv = list->null.uv; + const nk_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; + const struct nk_vec2 p1 = points[i1]; + const struct nk_vec2 p2 = points[i2]; + struct nk_vec2 diff = nk_vec2_sub(p2, p1); + float len; + + /* vec2 inverted lenth */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + /* add vertices */ + dx = diff.x * (thickness * 0.5f); + dy = diff.y * (thickness * 0.5f); + + vtx[0] = nk_draw_vertex(nk_vec2(p1.x + dy, p1.y - dx), uv, col); + vtx[1] = nk_draw_vertex(nk_vec2(p2.x + dy, p2.y - dx), uv, col); + vtx[2] = nk_draw_vertex(nk_vec2(p2.x - dy, p2.y + dx), uv, col); + vtx[3] = nk_draw_vertex(nk_vec2(p1.x - dy, p1.y + dx), uv, col); + vtx += 4; + + ids[0] = (nk_draw_index)(idx+0); ids[1] = (nk_draw_index)(idx+1); + ids[2] = (nk_draw_index)(idx+2); ids[3] = (nk_draw_index)(idx+0); + ids[4] = (nk_draw_index)(idx+2); ids[5] = (nk_draw_index)(idx+3); + ids += 6; + idx += 4; + } + } +} + +NK_API void +nk_draw_list_fill_poly_convex(struct nk_draw_list *list, + const struct nk_vec2 *points, const unsigned int points_count, + struct nk_color color, enum nk_anti_aliasing aliasing) +{ + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + nk_draw_vertex_color col; + NK_ASSERT(list); + if (!list || points_count < 3) return; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + color.a = (nk_byte)((float)color.a * list->global_alpha); + col = nk_color_u32(color); + if (aliasing == NK_ANTI_ALIASING_ON) { + nk_size i = 0; + nk_size i0 = 0; + nk_size i1 = 0; + + const float AA_SIZE = 1.0f; + const nk_draw_vertex_color col_trans = col & 0x00ffffff; + nk_size index = list->vertex_count; + const nk_size idx_count = (points_count-2)*3 + points_count*6; + const nk_size vtx_count = (points_count*2); + struct nk_draw_vertex *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + unsigned int vtx_inner_idx = (unsigned int)(index + 0); + unsigned int vtx_outer_idx = (unsigned int)(index + 1); + struct nk_vec2 *normals = 0; + nk_size size = 0; + if (!vtx || !ids) return; + + /* temporary allocate normals */ + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * points_count; + normals = (struct nk_vec2*) + nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + if (!normals) return; + + /* add elements */ + for (i = 2; i < points_count; i++) { + ids[0] = (nk_draw_index)(vtx_inner_idx); + ids[1] = (nk_draw_index)(vtx_inner_idx + ((i-1) << 1)); + ids[2] = (nk_draw_index)(vtx_inner_idx + (i << 1)); + ids += 3; + } + + /* compute normals */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + struct nk_vec2 p0 = points[i0]; + struct nk_vec2 p1 = points[i1]; + struct nk_vec2 diff = nk_vec2_sub(p1, p0); + + /* vec2 inverted lenth */ + float len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + normals[i0].x = diff.y; + normals[i0].y = -diff.x; + } + + /* add vertices + indexes */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + const struct nk_vec2 uv = list->null.uv; + struct nk_vec2 n0 = normals[i0]; + struct nk_vec2 n1 = normals[i1]; + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(n0, n1), 0.5f); + + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f / dmr2; + scale = NK_MIN(scale, 100.0f); + dm = nk_vec2_muls(dm, scale); + } + dm = nk_vec2_muls(dm, AA_SIZE * 0.5f); + + /* add vertices */ + vtx[0] = nk_draw_vertex(nk_vec2_sub(points[i1], dm), uv, col); + vtx[1] = nk_draw_vertex(nk_vec2_add(points[i1], dm), uv, col_trans); + vtx += 2; + + /* add indexes */ + ids[0] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids[1] = (nk_draw_index)(vtx_inner_idx+(i0<<1)); + ids[2] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[3] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[4] = (nk_draw_index)(vtx_outer_idx+(i1<<1)); + ids[5] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids += 6; + } + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + nk_size i = 0; + nk_size index = list->vertex_count; + const nk_size idx_count = (points_count-2)*3; + const nk_size vtx_count = points_count; + struct nk_draw_vertex *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + if (!vtx || !ids) return; + for (i = 0; i < vtx_count; ++i) { + vtx[0] = nk_draw_vertex(points[i], list->null.uv, col); + vtx++; + } + for (i = 2; i < points_count; ++i) { + ids[0] = (nk_draw_index)index; + ids[1] = (nk_draw_index)(index+ i - 1); + ids[2] = (nk_draw_index)(index+i); + ids += 3; + } + } +} + +NK_API void +nk_draw_list_path_clear(struct nk_draw_list *list) +{ + NK_ASSERT(list); + if (!list) return; + nk_buffer_reset(list->buffer, NK_BUFFER_FRONT); + list->path_count = 0; + list->path_offset = 0; +} + +NK_API void +nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) +{ + struct nk_vec2 *points = 0; + struct nk_draw_command *cmd = 0; + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) + nk_draw_list_add_clip(list, nk_null_rect); + + cmd = nk_draw_list_command_last(list); + if (cmd && cmd->texture.ptr != list->null.texture.ptr) + nk_draw_list_push_image(list, list->null.texture); + + points = nk_draw_list_alloc_path(list, 1); + if (!points) return; + points[0] = pos; +} + +NK_API void +nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center, + float radius, int a_min, int a_max) +{ + NK_ASSERT(list); + if (!list) return; + if (a_min <= a_max) { + int a = 0; + for (a = a_min; a <= a_max; a++) { + const struct nk_vec2 c = list->circle_vtx[(nk_size)a % NK_LEN(list->circle_vtx)]; + const float x = center.x + c.x * radius; + const float y = center.y + c.y * radius; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + } + } +} + +NK_API void +nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center, + float radius, float a_min, float a_max, unsigned int segments) +{ + unsigned int i = 0; + NK_ASSERT(list); + if (!list) return; + if (radius == 0.0f) return; + for (i = 0; i <= segments; ++i) { + const float a = a_min + ((float)i / ((float)segments) * (a_max - a_min)); + const float x = center.x + (float)NK_COS(a) * radius; + const float y = center.y + (float)NK_SIN(a) * radius; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + } +} + +NK_API void +nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, float rounding) +{ + float r; + NK_ASSERT(list); + if (!list) return; + r = rounding; + r = NK_MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x)); + r = NK_MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y)); + + if (r == 0.0f) { + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, nk_vec2(b.x,a.y)); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, nk_vec2(a.x,b.y)); + } else { + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, a.y + r), r, 6, 9); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, a.y + r), r, 9, 12); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, b.y - r), r, 0, 3); + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, b.y - r), r, 3, 6); + } +} + +NK_API void +nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2, + struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments) +{ + unsigned int i_step; + float t_step; + struct nk_vec2 p1; + + NK_ASSERT(list); + NK_ASSERT(list->path_count); + if (!list || !list->path_count) return; + num_segments = NK_MAX(num_segments, 1); + + p1 = nk_draw_list_path_last(list); + t_step = 1.0f/(float)num_segments; + for (i_step = 1; i_step <= num_segments; ++i_step) { + float t = t_step * (float)i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t * t *t; + float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; + float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; + nk_draw_list_path_line_to(list, nk_vec2(x,y)); + } +} + +NK_API void +nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_fill_poly_convex(list, points, list->path_count, color, list->shape_AA); + nk_draw_list_path_clear(list); +} + +NK_API void +nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color, + enum nk_draw_list_stroke closed, float thickness) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_stroke_poly_line(list, points, list->path_count, color, + closed, thickness, list->line_AA); + nk_draw_list_path_clear(list); +} + +NK_API void +nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, nk_vec2_add(a, nk_vec2(0.5f, 0.5f))); + nk_draw_list_path_line_to(list, nk_vec2_add(b, nk_vec2(0.5f, 0.5f))); + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} + +NK_API void +nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_rect_to(list, nk_vec2(rect.x + 0.5f, rect.y + 0.5f), + nk_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_rect_to(list, nk_vec2(rect.x + 0.5f, rect.y + 0.5f), + nk_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + nk_draw_vertex_color col_left = nk_color_u32(left); + nk_draw_vertex_color col_top = nk_color_u32(top); + nk_draw_vertex_color col_right = nk_color_u32(right); + nk_draw_vertex_color col_bottom = nk_color_u32(bottom); + + struct nk_draw_vertex *vtx; + nk_draw_index *idx; + nk_draw_index index; + NK_ASSERT(list); + if (!list) return; + + nk_draw_list_push_image(list, list->null.texture); + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx[0] = nk_draw_vertex(nk_vec2(rect.x, rect.y), list->null.uv, col_left); + vtx[1] = nk_draw_vertex(nk_vec2(rect.x + rect.w, rect.y), list->null.uv, col_top); + vtx[2] = nk_draw_vertex(nk_vec2(rect.x + rect.w, rect.y + rect.h), list->null.uv, col_right); + vtx[3] = nk_draw_vertex(nk_vec2(rect.x, rect.y + rect.h), list->null.uv, col_bottom); +} + +NK_API void +nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs, float thickness) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0, + struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, + struct nk_color col, unsigned int segments, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, p0); + nk_draw_list_path_curve_to(list, cp0, cp1, p1, segments); + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} + +NK_INTERN void +nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 c, struct nk_vec2 uva, struct nk_vec2 uvc, + struct nk_color color) +{ + nk_draw_vertex_color col = nk_color_u32(color); + struct nk_draw_vertex *vtx; + struct nk_vec2 uvb; + struct nk_vec2 uvd; + struct nk_vec2 b; + struct nk_vec2 d; + nk_draw_index *idx; + nk_draw_index index; + NK_ASSERT(list); + if (!list) return; + + uvb = nk_vec2(uvc.x, uva.y); + uvd = nk_vec2(uva.x, uvc.y); + b = nk_vec2(c.x, a.y); + d = nk_vec2(a.x, c.y); + + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx[0] = nk_draw_vertex(a, uva, col); + vtx[1] = nk_draw_vertex(b, uvb, col); + vtx[2] = nk_draw_vertex(c, uvc, col); + vtx[3] = nk_draw_vertex(d, uvd, col); +} + +NK_API void +nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture, + struct nk_rect rect, struct nk_color color) +{ + NK_ASSERT(list); + if (!list) return; + /* push new command with given texture */ + nk_draw_list_push_image(list, texture.handle); + if (nk_image_is_subimage(&texture)) { + /* add region inside of the texture */ + struct nk_vec2 uv[2]; + uv[0].x = (float)texture.region[0]/(float)texture.w; + uv[0].y = (float)texture.region[1]/(float)texture.h; + uv[1].x = (float)(texture.region[0] + texture.region[2])/(float)texture.w; + uv[1].y = (float)(texture.region[1] + texture.region[3])/(float)texture.h; + nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), uv[0], uv[1], color); + } else nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), + nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color); +} + +NK_API void +nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font, + struct nk_rect rect, const char *text, int len, float font_height, + struct nk_color fg) +{ + float x; + int text_len; + nk_rune unicode; + nk_rune next; + int glyph_len; + int next_glyph_len; + struct nk_user_font_glyph g; + + NK_ASSERT(list); + if (!list || !len || !text) return; + if (rect.x > (list->clip_rect.x + list->clip_rect.w) || + rect.y > (list->clip_rect.y + list->clip_rect.h) || + rect.x < list->clip_rect.x || rect.y < list->clip_rect.y) + return; + + nk_draw_list_push_image(list, font->texture); + x = rect.x; + glyph_len = text_len = nk_utf_decode(text, &unicode, len); + if (!glyph_len) return; + + /* draw every glyph image */ + while (text_len <= len && glyph_len) { + float gx, gy, gh, gw; + float char_width = 0; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + next_glyph_len = nk_utf_decode(text + text_len, &next, (int)len - text_len); + font->query(font->userdata, font_height, &g, unicode, + (next == NK_UTF_INVALID) ? '\0' : next); + + /* calculate and draw glyph drawing rectangle and image */ + gx = x + g.offset.x; + /*gy = rect.y + (rect.h/2) - (font->height/2) + g.offset.y;*/ + gy = rect.y + g.offset.y; + gw = g.width; gh = g.height; + char_width = g.xadvance; + fg.a = (nk_byte)((float)fg.a * list->global_alpha); + nk_draw_list_push_rect_uv(list, nk_vec2(gx,gy), nk_vec2(gx + gw, gy+ gh), + g.uv[0], g.uv[1], fg); + + /* offset next glyph */ + text_len += glyph_len; + x += char_width; + glyph_len = next_glyph_len; + unicode = next; + } +} + +NK_API void +nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *vertices, struct nk_buffer *elements, + const struct nk_convert_config *config) +{ + const struct nk_command *cmd; + NK_ASSERT(ctx); + NK_ASSERT(cmds); + NK_ASSERT(vertices); + NK_ASSERT(elements); + if (!ctx || !cmds || !vertices || !elements) + return; + + nk_draw_list_setup(&ctx->draw_list, config->global_alpha, config->line_AA, + config->shape_AA, config->null, cmds, vertices, elements); + nk_foreach(cmd, ctx) + { +#ifdef NK_INCLUDE_COMMAND_USERDATA + list->userdata = cmd->userdata; +#endif + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s = (const struct nk_command_scissor*)cmd; + nk_draw_list_add_clip(&ctx->draw_list, nk_rect(s->x, s->y, s->w, s->h)); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line*)cmd; + nk_draw_list_stroke_line(&ctx->draw_list, nk_vec2(l->begin.x, l->begin.y), + nk_vec2(l->end.x, l->end.y), l->color, l->line_thickness); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve*)cmd; + nk_draw_list_stroke_curve(&ctx->draw_list, nk_vec2(q->begin.x, q->begin.y), + nk_vec2(q->ctrl[0].x, q->ctrl[0].y), nk_vec2(q->ctrl[1].x, + q->ctrl[1].y), nk_vec2(q->end.x, q->end.y), q->color, + config->curve_segment_count, q->line_thickness); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect*)cmd; + nk_draw_list_stroke_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding, r->line_thickness); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled*)cmd; + nk_draw_list_fill_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: { + const struct nk_command_rect_multi_color *r = (const struct nk_command_rect_multi_color*)cmd; + nk_draw_list_fill_rect_multi_color(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->left, r->top, r->right, r->bottom); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle*)cmd; + nk_draw_list_stroke_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count, c->line_thickness); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_draw_list_fill_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count); + } break; + case NK_COMMAND_ARC: { + const struct nk_command_arc *c = (const struct nk_command_arc*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_stroke(&ctx->draw_list, c->color, NK_STROKE_CLOSED, c->line_thickness); + } break; + case NK_COMMAND_ARC_FILLED: { + const struct nk_command_arc_filled *c = (const struct nk_command_arc_filled*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_fill(&ctx->draw_list, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd; + nk_draw_list_stroke_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color, + t->line_thickness); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled*)cmd; + nk_draw_list_fill_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color); + } break; + case NK_COMMAND_POLYGON: { + int i; + const struct nk_command_polygon*p = (const struct nk_command_polygon*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_CLOSED, p->line_thickness); + } break; + case NK_COMMAND_POLYGON_FILLED: { + int i; + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_fill(&ctx->draw_list, p->color); + } break; + case NK_COMMAND_POLYLINE: { + int i; + const struct nk_command_polyline *p = (const struct nk_command_polyline*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_OPEN, p->line_thickness); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_draw_list_add_text(&ctx->draw_list, t->font, nk_rect(t->x, t->y, t->w, t->h), + t->string, t->length, t->height, t->foreground); + } break; + case NK_COMMAND_IMAGE: { + const struct nk_command_image *i = (const struct nk_command_image*)cmd; + nk_draw_list_add_image(&ctx->draw_list, i->img, nk_rect(i->x, i->y, i->w, i->h), + nk_rgb(255, 255, 255)); + } break; + default: break; + } + } +} + +NK_API const struct nk_draw_command* +nk__draw_begin(const struct nk_context *ctx, + const struct nk_buffer *buffer) +{return nk__draw_list_begin(&ctx->draw_list, buffer);} + +NK_API const struct nk_draw_command* +nk__draw_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_context *ctx) +{return nk__draw_list_next(cmd, buffer, &ctx->draw_list);} + +#endif + +/* + * ============================================================== + * + * FONT HANDLING + * + * =============================================================== + */ +#ifdef NK_INCLUDE_FONT_BAKING +/* ------------------------------------------------------------- + * + * RECT PACK + * + * --------------------------------------------------------------*/ +/* stb_rect_pack.h - v0.05 - public domain - rectangle packing */ +/* Sean Barrett 2014 */ +#define NK_RP__MAXVAL 0xffff +typedef unsigned short nk_rp_coord; + +struct nk_rp_rect { + /* reserved for your use: */ + int id; + /* input: */ + nk_rp_coord w, h; + /* output: */ + nk_rp_coord x, y; + int was_packed; + /* non-zero if valid packing */ +}; /* 16 bytes, nominally */ + +struct nk_rp_node { + nk_rp_coord x,y; + struct nk_rp_node *next; +}; + +struct nk_rp_context { + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + struct nk_rp_node *active_head; + struct nk_rp_node *free_head; + struct nk_rp_node extra[2]; + /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */ +}; + +struct nk_rp__findresult { + int x,y; + struct nk_rp_node **prev_link; +}; + +enum NK_RP_HEURISTIC { + NK_RP_HEURISTIC_Skyline_default=0, + NK_RP_HEURISTIC_Skyline_BL_sortHeight = NK_RP_HEURISTIC_Skyline_default, + NK_RP_HEURISTIC_Skyline_BF_sortHeight +}; +enum NK_RP_INIT_STATE{NK_RP__INIT_skyline = 1}; + +NK_INTERN void +nk_rp_setup_allow_out_of_mem(struct nk_rp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + /* if it's ok to run out of memory, then don't bother aligning them; */ + /* this gives better packing, but may fail due to OOM (even though */ + /* the rectangles easily fit). @TODO a smarter approach would be to only */ + /* quantize once we've hit OOM, then we could get rid of this parameter. */ + context->align = 1; + else { + /* if it's not ok to run out of memory, then quantize the widths */ + /* so that num_nodes is always enough nodes. */ + /* */ + /* I.e. num_nodes * align >= width */ + /* align >= width / num_nodes */ + /* align = ceil(width/num_nodes) */ + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +NK_INTERN void +nk_rp_init_target(struct nk_rp_context *context, int width, int height, + struct nk_rp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + NK_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = 0; + context->init_mode = NK_RP__INIT_skyline; + context->heuristic = NK_RP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + nk_rp_setup_allow_out_of_mem(context, 0); + + /* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */ + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (nk_rp_coord) width; + context->extra[1].y = 65535; + context->extra[1].next = 0; +} + +/* find minimum y position if it starts at x1 */ +NK_INTERN int +nk_rp__skyline_find_min_y(struct nk_rp_context *c, struct nk_rp_node *first, + int x0, int width, int *pwaste) +{ + struct nk_rp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + NK_ASSERT(first->x <= x0); + NK_UNUSED(c); + + NK_ASSERT(node->next->x > x0); + /* we ended up handling this in the caller for efficiency */ + NK_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) + { + if (node->y > min_y) { + /* raise min_y higher. */ + /* we've accounted for all waste up to min_y, */ + /* but we'll now add more waste for everything we've visted */ + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + /* the first time through, visited_width might be reduced */ + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + /* add waste area */ + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + *pwaste = waste_area; + return min_y; +} + +NK_INTERN struct nk_rp__findresult +nk_rp__skyline_find_best_pos(struct nk_rp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + struct nk_rp__findresult fr; + struct nk_rp_node **prev, *node, *tail, **best = 0; + + /* align to multiple of c->align */ + width = (width + c->align - 1); + width -= width % c->align; + NK_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = nk_rp__skyline_find_min_y(c, node, node->x, width, &waste); + /* actually just want to test BL */ + if (c->heuristic == NK_RP_HEURISTIC_Skyline_BL_sortHeight) { + /* bottom left */ + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + /* best-fit */ + if (y + height <= c->height) { + /* can only use it if it first vertically */ + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + best_x = (best == 0) ? 0 : (*best)->x; + + /* if doing best-fit (BF), we also have to try aligning right edge to each node position */ + /* */ + /* e.g, if fitting */ + /* */ + /* ____________________ */ + /* |____________________| */ + /* */ + /* into */ + /* */ + /* | | */ + /* | ____________| */ + /* |____________| */ + /* */ + /* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */ + /* */ + /* This makes BF take about 2x the time */ + if (c->heuristic == NK_RP_HEURISTIC_Skyline_BF_sortHeight) + { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + /* find first node that's admissible */ + while (tail->x < width) + tail = tail->next; + while (tail) + { + int xpos = tail->x - width; + int y,waste; + NK_ASSERT(xpos >= 0); + /* find the left position that matches this */ + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + NK_ASSERT(node->next->x > xpos && node->x <= xpos); + y = nk_rp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + NK_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +NK_INTERN struct nk_rp__findresult +nk_rp__skyline_pack_rectangle(struct nk_rp_context *context, int width, int height) +{ + /* find best position according to heuristic */ + struct nk_rp__findresult res = nk_rp__skyline_find_best_pos(context, width, height); + struct nk_rp_node *node, *cur; + + /* bail if: */ + /* 1. it failed */ + /* 2. the best node doesn't fit (we don't always check this) */ + /* 3. we're out of memory */ + if (res.prev_link == 0 || res.y + height > context->height || context->free_head == 0) { + res.prev_link = 0; + return res; + } + + /* on success, create new node */ + node = context->free_head; + node->x = (nk_rp_coord) res.x; + node->y = (nk_rp_coord) (res.y + height); + + context->free_head = node->next; + + /* insert the new node into the right starting point, and */ + /* let 'cur' point to the remaining nodes needing to be */ + /* stiched back in */ + cur = *res.prev_link; + if (cur->x < res.x) { + /* preserve the existing one, so start testing with the next one */ + struct nk_rp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + /* from here, traverse cur and free the nodes, until we get to one */ + /* that shouldn't be freed */ + while (cur->next && cur->next->x <= res.x + width) { + struct nk_rp_node *next = cur->next; + /* move the current node to the free list */ + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + /* stitch the list back in */ + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (nk_rp_coord) (res.x + width); + return res; +} + +NK_INTERN int +nk_rect_height_compare(const void *a, const void *b) +{ + const struct nk_rp_rect *p = (const struct nk_rp_rect *) a; + const struct nk_rp_rect *q = (const struct nk_rp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +NK_INTERN int +nk_rect_original_order(const void *a, const void *b) +{ + const struct nk_rp_rect *p = (const struct nk_rp_rect *) a; + const struct nk_rp_rect *q = (const struct nk_rp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +static void +nk_rp_qsort(struct nk_rp_rect *array, unsigned int len, int(*cmp)(const void*,const void*)) +{ + /* iterative quick sort */ + #define NK_MAX_SORT_STACK 64 + unsigned right, left = 0, stack[NK_MAX_SORT_STACK], pos = 0; + unsigned seed = len/2 * 69069+1; + for (;;) { + for (; left+1 < len; len++) { + struct nk_rp_rect pivot, tmp; + if (pos == NK_MAX_SORT_STACK) len = stack[pos = 0]; + pivot = array[left+seed%(len-left)]; + seed = seed * 69069 + 1; + stack[pos++] = len; + for (right = left-1;;) { + while (cmp(&array[++right], &pivot) < 0); + while (cmp(&pivot, &array[--len]) < 0); + if (right >= len) break; + tmp = array[right]; + array[right] = array[len]; + array[len] = tmp; + } + } + if (pos == 0) break; + left = len; + len = stack[--pos]; + } + #undef NK_MAX_SORT_STACK +} + +NK_INTERN void +nk_rp_pack_rects(struct nk_rp_context *context, struct nk_rp_rect *rects, int num_rects) +{ + int i; + /* we use the 'was_packed' field internally to allow sorting/unsorting */ + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + } + + /* sort according to heuristic */ + nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_height_compare); + + for (i=0; i < num_rects; ++i) { + struct nk_rp__findresult fr = nk_rp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (nk_rp_coord) fr.x; + rects[i].y = (nk_rp_coord) fr.y; + } else { + rects[i].x = rects[i].y = NK_RP__MAXVAL; + } + } + + /* unsort */ + nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_original_order); + + /* set was_packed flags */ + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == NK_RP__MAXVAL && rects[i].y == NK_RP__MAXVAL); +} + +/* + * ============================================================== + * + * TRUETYPE + * + * =============================================================== + */ +/* stb_truetype.h - v1.07 - public domain */ +#define NK_TT_MAX_OVERSAMPLE 8 +#define NK_TT__OVER_MASK (NK_TT_MAX_OVERSAMPLE-1) + +struct nk_tt_bakedchar { + unsigned short x0,y0,x1,y1; + /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; +}; + +struct nk_tt_aligned_quad{ + float x0,y0,s0,t0; /* top-left */ + float x1,y1,s1,t1; /* bottom-right */ +}; + +struct nk_tt_packedchar { + unsigned short x0,y0,x1,y1; + /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; + float xoff2,yoff2; +}; + +struct nk_tt_pack_range { + float font_size; + int first_unicode_codepoint_in_range; + /* if non-zero, then the chars are continuous, and this is the first codepoint */ + int *array_of_unicode_codepoints; + /* if non-zero, then this is an array of unicode codepoints */ + int num_chars; + struct nk_tt_packedchar *chardata_for_range; /* output */ + unsigned char h_oversample, v_oversample; + /* don't set these, they're used internally */ +}; + +struct nk_tt_pack_context { + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +struct nk_tt_fontinfo { + const unsigned char* data; /* pointer to .ttf file */ + int fontstart;/* offset of start of font */ + int numGlyphs;/* number of glyphs, needed for range checking */ + int loca,head,glyf,hhea,hmtx,kern; /* table locations as offset from start of .ttf */ + int index_map; /* a cmap mapping for our chosen character encoding */ + int indexToLocFormat; /* format needed to map from glyph index to glyph */ +}; + +enum { + NK_TT_vmove=1, + NK_TT_vline, + NK_TT_vcurve +}; + +struct nk_tt_vertex { + short x,y,cx,cy; + unsigned char type,padding; +}; + +struct nk_tt__bitmap{ + int w,h,stride; + unsigned char *pixels; +}; + +struct nk_tt__hheap_chunk { + struct nk_tt__hheap_chunk *next; +}; +struct nk_tt__hheap { + struct nk_allocator alloc; + struct nk_tt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +}; + +struct nk_tt__edge { + float x0,y0, x1,y1; + int invert; +}; + +struct nk_tt__active_edge { + struct nk_tt__active_edge *next; + float fx,fdx,fdy; + float direction; + float sy; + float ey; +}; +struct nk_tt__point {float x,y;}; + +#define NK_TT_MACSTYLE_DONTCARE 0 +#define NK_TT_MACSTYLE_BOLD 1 +#define NK_TT_MACSTYLE_ITALIC 2 +#define NK_TT_MACSTYLE_UNDERSCORE 4 +#define NK_TT_MACSTYLE_NONE 8 +/* <= not same as 0, this makes us check the bitfield is 0 */ + +enum { /* platformID */ + NK_TT_PLATFORM_ID_UNICODE =0, + NK_TT_PLATFORM_ID_MAC =1, + NK_TT_PLATFORM_ID_ISO =2, + NK_TT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_UNICODE */ + NK_TT_UNICODE_EID_UNICODE_1_0 =0, + NK_TT_UNICODE_EID_UNICODE_1_1 =1, + NK_TT_UNICODE_EID_ISO_10646 =2, + NK_TT_UNICODE_EID_UNICODE_2_0_BMP=3, + NK_TT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_MICROSOFT */ + NK_TT_MS_EID_SYMBOL =0, + NK_TT_MS_EID_UNICODE_BMP =1, + NK_TT_MS_EID_SHIFTJIS =2, + NK_TT_MS_EID_UNICODE_FULL =10 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_MAC; same as Script Manager codes */ + NK_TT_MAC_EID_ROMAN =0, NK_TT_MAC_EID_ARABIC =4, + NK_TT_MAC_EID_JAPANESE =1, NK_TT_MAC_EID_HEBREW =5, + NK_TT_MAC_EID_CHINESE_TRAD =2, NK_TT_MAC_EID_GREEK =6, + NK_TT_MAC_EID_KOREAN =3, NK_TT_MAC_EID_RUSSIAN =7 +}; + +enum { /* languageID for NK_TT_PLATFORM_ID_MICROSOFT; same as LCID... */ + /* problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */ + NK_TT_MS_LANG_ENGLISH =0x0409, NK_TT_MS_LANG_ITALIAN =0x0410, + NK_TT_MS_LANG_CHINESE =0x0804, NK_TT_MS_LANG_JAPANESE =0x0411, + NK_TT_MS_LANG_DUTCH =0x0413, NK_TT_MS_LANG_KOREAN =0x0412, + NK_TT_MS_LANG_FRENCH =0x040c, NK_TT_MS_LANG_RUSSIAN =0x0419, + NK_TT_MS_LANG_GERMAN =0x0407, NK_TT_MS_LANG_SPANISH =0x0409, + NK_TT_MS_LANG_HEBREW =0x040d, NK_TT_MS_LANG_SWEDISH =0x041D +}; + +enum { /* languageID for NK_TT_PLATFORM_ID_MAC */ + NK_TT_MAC_LANG_ENGLISH =0 , NK_TT_MAC_LANG_JAPANESE =11, + NK_TT_MAC_LANG_ARABIC =12, NK_TT_MAC_LANG_KOREAN =23, + NK_TT_MAC_LANG_DUTCH =4 , NK_TT_MAC_LANG_RUSSIAN =32, + NK_TT_MAC_LANG_FRENCH =1 , NK_TT_MAC_LANG_SPANISH =6 , + NK_TT_MAC_LANG_GERMAN =2 , NK_TT_MAC_LANG_SWEDISH =5 , + NK_TT_MAC_LANG_HEBREW =10, NK_TT_MAC_LANG_CHINESE_SIMPLIFIED =33, + NK_TT_MAC_LANG_ITALIAN =3 , NK_TT_MAC_LANG_CHINESE_TRAD =19 +}; + +#define nk_ttBYTE(p) (* (const nk_byte *) (p)) +#define nk_ttCHAR(p) (* (const char *) (p)) + +#if defined(NK_BIGENDIAN) && !defined(NK_ALLOW_UNALIGNED_TRUETYPE) + #define nk_ttUSHORT(p) (* (nk_ushort *) (p)) + #define nk_ttSHORT(p) (* (nk_short *) (p)) + #define nk_ttULONG(p) (* (nk_uint *) (p)) + #define nk_ttLONG(p) (* (nk_int *) (p)) +#else + static nk_ushort nk_ttUSHORT(const nk_byte *p) { return (nk_ushort)(p[0]*256 + p[1]); } + static nk_short nk_ttSHORT(const nk_byte *p) { return (nk_short)(p[0]*256 + p[1]); } + static nk_uint nk_ttULONG(const nk_byte *p) { return (nk_uint)((p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]); } +#endif + +#define nk_tt_tag4(p,c0,c1,c2,c3)\ + ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define nk_tt_tag(p,str) nk_tt_tag4(p,str[0],str[1],str[2],str[3]) + +NK_INTERN int nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc, + int glyph_index, struct nk_tt_vertex **pvertices); + +NK_INTERN nk_uint +nk_tt__find_table(const nk_byte *data, nk_uint fontstart, const char *tag) +{ + /* @OPTIMIZE: binary search */ + nk_int num_tables = nk_ttUSHORT(data+fontstart+4); + nk_uint tabledir = fontstart + 12; + nk_int i; + for (i = 0; i < num_tables; ++i) { + nk_uint loc = tabledir + (nk_uint)(16*i); + if (nk_tt_tag(data+loc+0, tag)) + return nk_ttULONG(data+loc+8); + } + return 0; +} + +NK_INTERN int +nk_tt_InitFont(struct nk_tt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + nk_uint cmap, t; + nk_int i,numTables; + const nk_byte *data = (const nk_byte *) data2; + + info->data = data; + info->fontstart = fontstart; + + cmap = nk_tt__find_table(data, (nk_uint)fontstart, "cmap"); /* required */ + info->loca = (int)nk_tt__find_table(data, (nk_uint)fontstart, "loca"); /* required */ + info->head = (int)nk_tt__find_table(data, (nk_uint)fontstart, "head"); /* required */ + info->glyf = (int)nk_tt__find_table(data, (nk_uint)fontstart, "glyf"); /* required */ + info->hhea = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hhea"); /* required */ + info->hmtx = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hmtx"); /* required */ + info->kern = (int)nk_tt__find_table(data, (nk_uint)fontstart, "kern"); /* not required */ + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = nk_tt__find_table(data, (nk_uint)fontstart, "maxp"); + if (t) info->numGlyphs = nk_ttUSHORT(data+t+4); + else info->numGlyphs = 0xffff; + + /* find a cmap encoding table we understand *now* to avoid searching */ + /* later. (todo: could make this installable) */ + /* the same regardless of glyph. */ + numTables = nk_ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) + { + nk_uint encoding_record = cmap + 4 + 8 * (nk_uint)i; + /* find an encoding we understand: */ + switch(nk_ttUSHORT(data+encoding_record)) { + case NK_TT_PLATFORM_ID_MICROSOFT: + switch (nk_ttUSHORT(data+encoding_record+2)) { + case NK_TT_MS_EID_UNICODE_BMP: + case NK_TT_MS_EID_UNICODE_FULL: + /* MS/Unicode */ + info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4)); + break; + default: break; + } break; + case NK_TT_PLATFORM_ID_UNICODE: + /* Mac/iOS has these */ + /* all the encodingIDs are unicode, so we don't bother to check it */ + info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4)); + break; + default: break; + } + } + if (info->index_map == 0) + return 0; + info->indexToLocFormat = nk_ttUSHORT(data+info->head + 50); + return 1; +} + +NK_INTERN int +nk_tt_FindGlyphIndex(const struct nk_tt_fontinfo *info, int unicode_codepoint) +{ + const nk_byte *data = info->data; + nk_uint index_map = (nk_uint)info->index_map; + + nk_ushort format = nk_ttUSHORT(data + index_map + 0); + if (format == 0) { /* apple byte encoding */ + nk_int bytes = nk_ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return nk_ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + nk_uint first = nk_ttUSHORT(data + index_map + 6); + nk_uint count = nk_ttUSHORT(data + index_map + 8); + if ((nk_uint) unicode_codepoint >= first && (nk_uint) unicode_codepoint < first+count) + return nk_ttUSHORT(data + index_map + 10 + (unicode_codepoint - (int)first)*2); + return 0; + } else if (format == 2) { + NK_ASSERT(0); /* @TODO: high-byte mapping for japanese/chinese/korean */ + return 0; + } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */ + nk_ushort segcount = nk_ttUSHORT(data+index_map+6) >> 1; + nk_ushort searchRange = nk_ttUSHORT(data+index_map+8) >> 1; + nk_ushort entrySelector = nk_ttUSHORT(data+index_map+10); + nk_ushort rangeShift = nk_ttUSHORT(data+index_map+12) >> 1; + + /* do a binary search of the segments */ + nk_uint endCount = index_map + 14; + nk_uint search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + /* they lie from endCount .. endCount + segCount */ + /* but searchRange is the nearest power of two, so... */ + if (unicode_codepoint >= nk_ttUSHORT(data + search + rangeShift*2)) + search += (nk_uint)(rangeShift*2); + + /* now decrement to bias correctly to find smallest */ + search -= 2; + while (entrySelector) { + nk_ushort end; + searchRange >>= 1; + end = nk_ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += (nk_uint)(searchRange*2); + --entrySelector; + } + search += 2; + + { + nk_ushort offset, start; + nk_ushort item = (nk_ushort) ((search - endCount) >> 1); + + NK_ASSERT(unicode_codepoint <= nk_ttUSHORT(data + endCount + 2*item)); + start = nk_ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = nk_ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (nk_ushort) (unicode_codepoint + nk_ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return nk_ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + nk_uint ngroups = nk_ttULONG(data+index_map+12); + nk_int low,high; + low = 0; high = (nk_int)ngroups; + /* Binary search the right group. */ + while (low < high) { + nk_int mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */ + nk_uint start_char = nk_ttULONG(data+index_map+16+mid*12); + nk_uint end_char = nk_ttULONG(data+index_map+16+mid*12+4); + if ((nk_uint) unicode_codepoint < start_char) + high = mid; + else if ((nk_uint) unicode_codepoint > end_char) + low = mid+1; + else { + nk_uint start_glyph = nk_ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return (int)start_glyph + (int)unicode_codepoint - (int)start_char; + else /* format == 13 */ + return (int)start_glyph; + } + } + return 0; /* not found */ + } + /* @TODO */ + NK_ASSERT(0); + return 0; +} + +NK_INTERN void +nk_tt_setvertex(struct nk_tt_vertex *v, nk_byte type, nk_int x, nk_int y, nk_int cx, nk_int cy) +{ + v->type = type; + v->x = (nk_short) x; + v->y = (nk_short) y; + v->cx = (nk_short) cx; + v->cy = (nk_short) cy; +} + +NK_INTERN int +nk_tt__GetGlyfOffset(const struct nk_tt_fontinfo *info, int glyph_index) +{ + int g1,g2; + if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */ + if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */ + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ +} + +NK_INTERN int +nk_tt_GetGlyphBox(const struct nk_tt_fontinfo *info, int glyph_index, + int *x0, int *y0, int *x1, int *y1) +{ + int g = nk_tt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = nk_ttSHORT(info->data + g + 2); + if (y0) *y0 = nk_ttSHORT(info->data + g + 4); + if (x1) *x1 = nk_ttSHORT(info->data + g + 6); + if (y1) *y1 = nk_ttSHORT(info->data + g + 8); + return 1; +} + +NK_INTERN int +stbtt__close_shape(struct nk_tt_vertex *vertices, int num_vertices, int was_off, + int start_off, nk_int sx, nk_int sy, nk_int scx, nk_int scy, nk_int cx, nk_int cy) +{ + if (start_off) { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve,sx,sy,cx,cy); + else + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline,sx,sy,0,0); + } + return num_vertices; +} + +NK_INTERN int +nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc, + int glyph_index, struct nk_tt_vertex **pvertices) +{ + nk_short numberOfContours; + const nk_byte *endPtsOfContours; + const nk_byte *data = info->data; + struct nk_tt_vertex *vertices=0; + int num_vertices=0; + int g = nk_tt__GetGlyfOffset(info, glyph_index); + *pvertices = 0; + + if (g < 0) return 0; + numberOfContours = nk_ttSHORT(data + g); + if (numberOfContours > 0) { + nk_byte flags=0,flagcount; + nk_int ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + nk_int x,y,cx,cy,sx,sy, scx,scy; + const nk_byte *points; + endPtsOfContours = (data + g + 10); + ins = nk_ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+nk_ttUSHORT(endPtsOfContours + numberOfContours*2-2); + m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */ + vertices = (struct nk_tt_vertex *)alloc->alloc(alloc->userdata, 0, (nk_size)m * sizeof(vertices[0])); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + /* in first pass, we load uninterpreted data into the allocated array */ + /* above, shifted to the end of the array so we won't overwrite it when */ + /* we create our final data starting from the front */ + off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */ + + /* first load flags */ + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else --flagcount; + vertices[off+i].type = flags; + } + + /* now load x coordinates */ + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + nk_short dx = *points++; + x += (flags & 16) ? dx : -dx; /* ??? */ + } else { + if (!(flags & 16)) { + x = x + (nk_short) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (nk_short) x; + } + + /* now load y coordinates */ + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + nk_short dy = *points++; + y += (flags & 32) ? dy : -dy; /* ??? */ + } else { + if (!(flags & 32)) { + y = y + (nk_short) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (nk_short) y; + } + + /* now convert them to our format */ + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) + { + flags = vertices[off+i].type; + x = (nk_short) vertices[off+i].x; + y = (nk_short) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + /* now start the new one */ + start_off = !(flags & 1); + if (start_off) { + /* if we start off with an off-curve point, then when we need to find a point on the curve */ + /* where we can start, and we need to save some state for when we wraparound. */ + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + /* next point is also a curve point, so interpolate an on-point curve */ + sx = (x + (nk_int) vertices[off+i+1].x) >> 1; + sy = (y + (nk_int) vertices[off+i+1].y) >> 1; + } else { + /* otherwise just use the next point as our start point */ + sx = (nk_int) vertices[off+i+1].x; + sy = (nk_int) vertices[off+i+1].y; + ++i; /* we're using point i+1 as the starting point, so skip it */ + } + } else { + sx = x; + sy = y; + } + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + nk_ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) + { /* if it's a curve */ + if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */ + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, x,y, cx, cy); + else nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + /* Compound shapes. */ + int more = 1; + const nk_byte *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + + while (more) + { + nk_ushort flags, gidx; + int comp_num_verts = 0, i; + struct nk_tt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = (nk_ushort)nk_ttSHORT(comp); comp+=2; + gidx = (nk_ushort)nk_ttSHORT(comp); comp+=2; + + if (flags & 2) { /* XY values */ + if (flags & 1) { /* shorts */ + mtx[4] = nk_ttSHORT(comp); comp+=2; + mtx[5] = nk_ttSHORT(comp); comp+=2; + } else { + mtx[4] = nk_ttCHAR(comp); comp+=1; + mtx[5] = nk_ttCHAR(comp); comp+=1; + } + } else { + /* @TODO handle matching point */ + NK_ASSERT(0); + } + if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */ + mtx[0] = mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */ + mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */ + mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + } + + /* Find transformation scales. */ + m = (float) NK_SQRT(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) NK_SQRT(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + /* Get indexed glyph. */ + comp_num_verts = nk_tt_GetGlyphShape(info, alloc, gidx, &comp_verts); + if (comp_num_verts > 0) + { + /* Transform vertices. */ + for (i = 0; i < comp_num_verts; ++i) { + struct nk_tt_vertex* v = &comp_verts[i]; + short x,y; + x=v->x; y=v->y; + v->x = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + /* Append vertices. */ + tmp = (struct nk_tt_vertex*)alloc->alloc(alloc->userdata, 0, + (nk_size)(num_vertices+comp_num_verts)*sizeof(struct nk_tt_vertex)); + if (!tmp) { + if (vertices) alloc->free(alloc->userdata, vertices); + if (comp_verts) alloc->free(alloc->userdata, comp_verts); + return 0; + } + if (num_vertices > 0) NK_MEMCPY(tmp, vertices, (nk_size)num_vertices*sizeof(struct nk_tt_vertex)); + NK_MEMCPY(tmp+num_vertices, comp_verts, (nk_size)comp_num_verts*sizeof(struct nk_tt_vertex)); + if (vertices) alloc->free(alloc->userdata,vertices); + vertices = tmp; + alloc->free(alloc->userdata,comp_verts); + num_vertices += comp_num_verts; + } + /* More components ? */ + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + /* @TODO other compound variations? */ + NK_ASSERT(0); + } else { + /* numberOfCounters == 0, do nothing */ + } + *pvertices = vertices; + return num_vertices; +} + +NK_INTERN void +nk_tt_GetGlyphHMetrics(const struct nk_tt_fontinfo *info, int glyph_index, + int *advanceWidth, int *leftSideBearing) +{ + nk_ushort numOfLongHorMetrics = nk_ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) + *advanceWidth = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) + *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) + *advanceWidth = nk_ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) + *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +NK_INTERN void +nk_tt_GetFontVMetrics(const struct nk_tt_fontinfo *info, + int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = nk_ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = nk_ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = nk_ttSHORT(info->data+info->hhea + 8); +} + +NK_INTERN float +nk_tt_ScaleForPixelHeight(const struct nk_tt_fontinfo *info, float height) +{ + int fheight = nk_ttSHORT(info->data + info->hhea + 4) - nk_ttSHORT(info->data + info->hhea + 6); + return (float) height / (float)fheight; +} + +NK_INTERN float +nk_tt_ScaleForMappingEmToPixels(const struct nk_tt_fontinfo *info, float pixels) +{ + int unitsPerEm = nk_ttUSHORT(info->data + info->head + 18); + return pixels / (float)unitsPerEm; +} + +/*------------------------------------------------------------- + * antialiasing software rasterizer + * --------------------------------------------------------------*/ +NK_INTERN void +nk_tt_GetGlyphBitmapBoxSubpixel(const struct nk_tt_fontinfo *font, + int glyph, float scale_x, float scale_y,float shift_x, float shift_y, + int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!nk_tt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + /* e.g. space character */ + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */ + if (ix0) *ix0 = nk_ifloor((float)x0 * scale_x + shift_x); + if (iy0) *iy0 = nk_ifloor((float)-y1 * scale_y + shift_y); + if (ix1) *ix1 = nk_iceil ((float)x1 * scale_x + shift_x); + if (iy1) *iy1 = nk_iceil ((float)-y0 * scale_y + shift_y); + } +} + +NK_INTERN void +nk_tt_GetGlyphBitmapBox(const struct nk_tt_fontinfo *font, int glyph, + float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + nk_tt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +/*------------------------------------------------------------- + * Rasterizer + * --------------------------------------------------------------*/ +NK_INTERN void* +nk_tt__hheap_alloc(struct nk_tt__hheap *hh, nk_size size) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + struct nk_tt__hheap_chunk *c = (struct nk_tt__hheap_chunk *) + hh->alloc.alloc(hh->alloc.userdata, 0, + sizeof(struct nk_tt__hheap_chunk) + size * (nk_size)count); + if (c == 0) return 0; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * (nk_size)hh->num_remaining_in_head_chunk; + } +} + +NK_INTERN void +nk_tt__hheap_free(struct nk_tt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +NK_INTERN void +nk_tt__hheap_cleanup(struct nk_tt__hheap *hh) +{ + struct nk_tt__hheap_chunk *c = hh->head; + while (c) { + struct nk_tt__hheap_chunk *n = c->next; + hh->alloc.free(hh->alloc.userdata, c); + c = n; + } +} + +NK_INTERN struct nk_tt__active_edge* +nk_tt__new_active(struct nk_tt__hheap *hh, struct nk_tt__edge *e, + int off_x, float start_point) +{ + struct nk_tt__active_edge *z = (struct nk_tt__active_edge *) + nk_tt__hheap_alloc(hh, sizeof(*z)); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + /*STBTT_assert(e->y0 <= start_point); */ + if (!z) return z; + z->fdx = dxdy; + z->fdy = (1/dxdy); + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= (float)off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} + +NK_INTERN void +nk_tt__handle_clipped_edge(float *scanline, int x, struct nk_tt__active_edge *e, + float x0, float y0, float x1, float y1) +{ + if (!(y0>y1) && !(y0sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (!(x0 > x) && !(x0 < x)) NK_ASSERT(x1 <= x+1); + else if (!(x0 > x+1) && !(x0 < x+1)) NK_ASSERT(x1 >= x); + else if (x0 <= x) NK_ASSERT(x1 <= x); + else if (x0 >= x+1) NK_ASSERT(x1 >= x+1); + else NK_ASSERT(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1); + else { + NK_ASSERT(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + /* coverage = 1 - average x position */ + scanline[x] += (float)e->direction * (float)(y1-y0) * (1.0f-((x0-(float)x)+(x1-(float)x))/2.0f); + } +} + +NK_INTERN void +nk_tt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, + struct nk_tt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + while (e) + { + /* brute force every pixel */ + /* compute intersection points with top & bottom */ + NK_ASSERT(e->ey >= y_top); + if (!(e->fdx > 0) && (e->fdx < 0)) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + nk_tt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + nk_tt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + nk_tt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float y0,y1; + float dy = e->fdy; + NK_ASSERT(e->sy <= y_bottom && e->ey >= y_top); + + /* compute endpoints of line segment clipped to this scanline (if the */ + /* line segment starts on this scanline. x0 is the intersection of the */ + /* line with y_top, but that may be off the line segment. */ + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + y0 = e->sy; + } else { + x_top = x0; + y0 = y_top; + } + + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + y1 = e->ey; + } else { + x_bottom = xb; + y1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) + { + /* from here on, we don't have to range check x values */ + if ((int) x_top == (int) x_bottom) { + float height; + /* simple case, only spans one pixel */ + int x = (int) x_top; + height = y1 - y0; + NK_ASSERT(x >= 0 && x < len); + scanline[x] += e->direction * (1.0f-(((float)x_top - (float)x) + ((float)x_bottom-(float)x))/2.0f) * (float)height; + scanline_fill[x] += e->direction * (float)height; /* everything right of this pixel is filled */ + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + /* covers 2+ pixels */ + if (x_top > x_bottom) + { + /* flip scanline vertically; signed area is the same */ + float t; + y0 = y_bottom - (y0 - y_top); + y1 = y_bottom - (y1 - y_top); + t = y0, y0 = y1, y1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + /* compute intersection with y axis at x1+1 */ + y_crossing = ((float)x1+1 - (float)x0) * (float)dy + (float)y_top; + + sign = e->direction; + /* area of the rectangle covered from y0..y_crossing */ + area = sign * (y_crossing-y0); + /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */ + scanline[x1] += area * (1.0f-((float)((float)x_top - (float)x1)+(float)(x1+1-x1))/2.0f); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += (float)dy * (float)(x2 - (x1+1)); + + scanline[x2] += area + sign * (1.0f-((float)(x2-x2)+((float)x_bottom-(float)x2))/2.0f) * (y1-y_crossing); + scanline_fill[x2] += sign * (y1-y0); + } + } + else + { + /* if edge goes outside of box we're drawing, we require */ + /* clipping logic. since this does not match the intended use */ + /* of this library, we use a different, very slow brute */ + /* force implementation */ + int x; + for (x=0; x < len; ++x) + { + /* cases: */ + /* */ + /* there can be up to two intersections with the pixel. any intersection */ + /* with left or right edges can be handled by splitting into two (or three) */ + /* regions. intersections with top & bottom do not necessitate case-wise logic. */ + /* */ + /* the old way of doing this found the intersections with the left & right edges, */ + /* then used some simple logic to produce up to three segments in sorted order */ + /* from top-to-bottom. however, this had a problem: if an x edge was epsilon */ + /* across the x border, then the corresponding y position might not be distinct */ + /* from the other y segment, and it might ignored as an empty segment. to avoid */ + /* that, we need to explicitly produce segments based on x positions. */ + + /* rename variables to clear pairs */ + float ya = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + float yb,y2; + + yb = ((float)x - x0) / dx + y_top; + y2 = ((float)x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { /* three segments descending down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { /* one segment */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x3,y3); + } + } + } + } + e = e->next; + } +} + +/* directly AA rasterize edges w/o supersampling */ +NK_INTERN void +nk_tt__rasterize_sorted_edges(struct nk_tt__bitmap *result, struct nk_tt__edge *e, + int n, int vsubsample, int off_x, int off_y, struct nk_allocator *alloc) +{ + struct nk_tt__hheap hh; + struct nk_tt__active_edge *active = 0; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + NK_UNUSED(vsubsample); + nk_zero_struct(hh); + hh.alloc = *alloc; + + if (result->w > 64) + scanline = (float *) alloc->alloc(alloc->userdata,0, (nk_size)(result->w*2+1) * sizeof(float)); + else scanline = scanline_data; + + scanline2 = scanline + result->w; + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) + { + /* find center of pixel for this scanline */ + float scan_y_top = (float)y + 0.0f; + float scan_y_bottom = (float)y + 1.0f; + struct nk_tt__active_edge **step = &active; + + NK_MEMSET(scanline , 0, (nk_size)result->w*sizeof(scanline[0])); + NK_MEMSET(scanline2, 0, (nk_size)(result->w+1)*sizeof(scanline[0])); + + /* update all active edges; */ + /* remove all active edges that terminate before the top of this scanline */ + while (*step) { + struct nk_tt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; /* delete from list */ + NK_ASSERT(z->direction); + z->direction = 0; + nk_tt__hheap_free(&hh, z); + } else { + step = &((*step)->next); /* advance through list */ + } + } + + /* insert all edges that start before the bottom of this scanline */ + while (e->y0 <= scan_y_bottom) { + struct nk_tt__active_edge *z = nk_tt__new_active(&hh, e, off_x, scan_y_top); + NK_ASSERT(z->ey >= scan_y_top); + /* insert at front */ + z->next = active; + active = z; + ++e; + } + + /* now process all active edges */ + if (active) + nk_tt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) NK_ABS(k) * 255.0f + 0.5f; + m = (int) k; + if (m > 255.0f) m = 255.0f; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + /* advance all the edges */ + step = &active; + while (*step) { + struct nk_tt__active_edge *z = *step; + z->fx += z->fdx; /* advance to position for current scanline */ + step = &((*step)->next); /* advance through list */ + } + ++y; + ++j; + } + nk_tt__hheap_cleanup(&hh); + if (scanline != scanline_data) + alloc->free(alloc->userdata, scanline); +} + +#define NK_TT__COMPARE(a,b) ((a)->y0 < (b)->y0) +NK_INTERN void +nk_tt__sort_edges_ins_sort(struct nk_tt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + struct nk_tt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + struct nk_tt__edge *b = &p[j-1]; + int c = NK_TT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +NK_INTERN void +nk_tt__sort_edges_quicksort(struct nk_tt__edge *p, int n) +{ + /* threshhold for transitioning to insertion sort */ + while (n > 12) { + struct nk_tt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = NK_TT__COMPARE(&p[0],&p[m]); + c12 = NK_TT__COMPARE(&p[m],&p[n-1]); + + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = NK_TT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!NK_TT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!NK_TT__COMPARE(&p[0], &p[j])) break; + } + + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + + } + + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + nk_tt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + nk_tt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +NK_INTERN void +nk_tt__sort_edges(struct nk_tt__edge *p, int n) +{ + nk_tt__sort_edges_quicksort(p, n); + nk_tt__sort_edges_ins_sort(p, n); +} + +NK_INTERN void +nk_tt__rasterize(struct nk_tt__bitmap *result, struct nk_tt__point *pts, + int *wcount, int windings, float scale_x, float scale_y, + float shift_x, float shift_y, int off_x, int off_y, int invert, + struct nk_allocator *alloc) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + struct nk_tt__edge *e; + int n,i,j,k,m; + int vsubsample = 1; + /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity */ + + /* now we have to blow out the windings into explicit edge lists */ + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (struct nk_tt__edge*) + alloc->alloc(alloc->userdata, 0,(sizeof(*e) * (nk_size)(n+1))); + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) + { + struct nk_tt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + /* skip the edge if horizontal */ + if (!(p[j].y > p[k].y) && !(p[j].y < p[k].y)) + continue; + + /* add edge from j to k to the list */ + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * (float)vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * (float)vsubsample; + ++n; + } + } + + /* now sort the edges by their highest point (should snap to integer, and then by x) */ + /*STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */ + nk_tt__sort_edges(e, n); + /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ + nk_tt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, alloc); + alloc->free(alloc->userdata, e); +} + +NK_INTERN void +nk_tt__add_point(struct nk_tt__point *points, int n, float x, float y) +{ + if (!points) return; /* during first pass, it's unallocated */ + points[n].x = x; + points[n].y = y; +} + +NK_INTERN int +nk_tt__tesselate_curve(struct nk_tt__point *points, int *num_points, + float x0, float y0, float x1, float y1, float x2, float y2, + float objspace_flatness_squared, int n) +{ + /* tesselate until threshhold p is happy... + * @TODO warped to compensate for non-linear stretching */ + /* midpoint */ + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + /* versus directly drawn line */ + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) /* 65536 segments on one curve better be enough! */ + return 1; + + /* half-pixel error allowed... need to be smaller if AA */ + if (dx*dx+dy*dy > objspace_flatness_squared) { + nk_tt__tesselate_curve(points, num_points, x0,y0, + (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + nk_tt__tesselate_curve(points, num_points, mx,my, + (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + nk_tt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +/* returns number of contours */ +NK_INTERN struct nk_tt__point* +nk_tt_FlattenCurves(struct nk_tt_vertex *vertices, int num_verts, + float objspace_flatness, int **contour_lengths, int *num_contours, + struct nk_allocator *alloc) +{ + struct nk_tt__point *points=0; + int num_points=0; + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i; + int n=0; + int start=0; + int pass; + + /* count how many "moves" there are to get the contour count */ + for (i=0; i < num_verts; ++i) + if (vertices[i].type == NK_TT_vmove) ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) + alloc->alloc(alloc->userdata,0, (sizeof(**contour_lengths) * (nk_size)n)); + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + /* make two passes through the points so we don't need to realloc */ + for (pass=0; pass < 2; ++pass) + { + float x=0,y=0; + if (pass == 1) { + points = (struct nk_tt__point *) + alloc->alloc(alloc->userdata,0, (nk_size)num_points * sizeof(points[0])); + if (points == 0) goto error; + } + num_points = 0; + n= -1; + + for (i=0; i < num_verts; ++i) + { + switch (vertices[i].type) { + case NK_TT_vmove: + /* start the next contour */ + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + nk_tt__add_point(points, num_points++, x,y); + break; + case NK_TT_vline: + x = vertices[i].x, y = vertices[i].y; + nk_tt__add_point(points, num_points++, x, y); + break; + case NK_TT_vcurve: + nk_tt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + default: break; + } + } + (*contour_lengths)[n] = num_points - start; + } + return points; + +error: + alloc->free(alloc->userdata, points); + alloc->free(alloc->userdata, *contour_lengths); + *contour_lengths = 0; + *num_contours = 0; + return 0; +} + +NK_INTERN void +nk_tt_Rasterize(struct nk_tt__bitmap *result, float flatness_in_pixels, + struct nk_tt_vertex *vertices, int num_verts, + float scale_x, float scale_y, float shift_x, float shift_y, + int x_off, int y_off, int invert, struct nk_allocator *alloc) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + struct nk_tt__point *windings = nk_tt_FlattenCurves(vertices, num_verts, + flatness_in_pixels / scale, &winding_lengths, &winding_count, alloc); + + NK_ASSERT(alloc); + if (windings) { + nk_tt__rasterize(result, windings, winding_lengths, winding_count, + scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, alloc); + alloc->free(alloc->userdata, winding_lengths); + alloc->free(alloc->userdata, windings); + } +} + +NK_INTERN void +nk_tt_MakeGlyphBitmapSubpixel(const struct nk_tt_fontinfo *info, unsigned char *output, + int out_w, int out_h, int out_stride, float scale_x, float scale_y, + float shift_x, float shift_y, int glyph, struct nk_allocator *alloc) +{ + int ix0,iy0; + struct nk_tt_vertex *vertices; + int num_verts = nk_tt_GetGlyphShape(info, alloc, glyph, &vertices); + struct nk_tt__bitmap gbm; + + nk_tt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, + shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + nk_tt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, + shift_x, shift_y, ix0,iy0, 1, alloc); + alloc->free(alloc->userdata, vertices); +} + +/*------------------------------------------------------------- + * Bitmap baking + * --------------------------------------------------------------*/ +NK_INTERN int +nk_tt_PackBegin(struct nk_tt_pack_context *spc, unsigned char *pixels, + int pw, int ph, int stride_in_bytes, int padding, struct nk_allocator *alloc) +{ + int num_nodes = pw - padding; + struct nk_rp_context *context = (struct nk_rp_context *) + alloc->alloc(alloc->userdata,0, sizeof(*context)); + struct nk_rp_node *nodes = (struct nk_rp_node*) + alloc->alloc(alloc->userdata,0, (sizeof(*nodes ) * (nk_size)num_nodes)); + + if (context == 0 || nodes == 0) { + if (context != 0) alloc->free(alloc->userdata, context); + if (nodes != 0) alloc->free(alloc->userdata, nodes); + return 0; + } + + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + nk_rp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + if (pixels) + NK_MEMSET(pixels, 0, (nk_size)(pw*ph)); /* background of 0 around pixels */ + return 1; +} + +NK_INTERN void +nk_tt_PackEnd(struct nk_tt_pack_context *spc, struct nk_allocator *alloc) +{ + alloc->free(alloc->userdata, spc->nodes); + alloc->free(alloc->userdata, spc->pack_info); +} + +NK_INTERN void +nk_tt_PackSetOversampling(struct nk_tt_pack_context *spc, + unsigned int h_oversample, unsigned int v_oversample) +{ + NK_ASSERT(h_oversample <= NK_TT_MAX_OVERSAMPLE); + NK_ASSERT(v_oversample <= NK_TT_MAX_OVERSAMPLE); + if (h_oversample <= NK_TT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= NK_TT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +NK_INTERN void +nk_tt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, + int kernel_width) +{ + unsigned char buffer[NK_TT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + + for (j=0; j < h; ++j) + { + int i; + unsigned int total; + NK_MEMSET(buffer, 0, (nk_size)kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)pixels[i] - buffer[i & NK_TT__OVER_MASK]; + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / (unsigned int)kernel_width); + } + break; + } + + for (; i < w; ++i) { + NK_ASSERT(pixels[i] == 0); + total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]); + pixels[i] = (unsigned char) (total / (unsigned int)kernel_width); + } + pixels += stride_in_bytes; + } +} + +NK_INTERN void +nk_tt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, + int kernel_width) +{ + unsigned char buffer[NK_TT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + + for (j=0; j < w; ++j) + { + int i; + unsigned int total; + NK_MEMSET(buffer, 0, (nk_size)kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width); + } + break; + } + + for (; i < h; ++i) { + NK_ASSERT(pixels[i*stride_in_bytes] == 0); + total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]); + pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width); + } + pixels += 1; + } +} + +NK_INTERN float +nk_tt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + /* The prefilter is a box filter of width "oversample", */ + /* which shifts phase by (oversample - 1)/2 pixels in */ + /* oversampled space. We want to shift in the opposite */ + /* direction to counter this. */ + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +/* rects array must be big enough to accommodate all characters in the given ranges */ +NK_INTERN int +nk_tt_PackFontRangesGatherRects(struct nk_tt_pack_context *spc, + struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, + int num_ranges, struct nk_rp_rect *rects) +{ + int i,j,k; + k = 0; + + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = (fh > 0) ? nk_tt_ScaleForPixelHeight(info, fh): + nk_tt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? + ranges[i].first_unicode_codepoint_in_range + j : + ranges[i].array_of_unicode_codepoints[j]; + + int glyph = nk_tt_FindGlyphIndex(info, codepoint); + nk_tt_GetGlyphBitmapBoxSubpixel(info,glyph, scale * (float)spc->h_oversample, + scale * (float)spc->v_oversample, 0,0, &x0,&y0,&x1,&y1); + rects[k].w = (nk_rp_coord) (x1-x0 + spc->padding + (int)spc->h_oversample-1); + rects[k].h = (nk_rp_coord) (y1-y0 + spc->padding + (int)spc->v_oversample-1); + ++k; + } + } + return k; +} + +NK_INTERN int +nk_tt_PackFontRangesRenderIntoRects(struct nk_tt_pack_context *spc, + struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, + int num_ranges, struct nk_rp_rect *rects, struct nk_allocator *alloc) +{ + int i,j,k, return_value = 1; + /* save current values */ + int old_h_over = (int)spc->h_oversample; + int old_v_over = (int)spc->v_oversample; + /* rects array must be big enough to accommodate all characters in the given ranges */ + + k = 0; + for (i=0; i < num_ranges; ++i) + { + float fh = ranges[i].font_size; + float recip_h,recip_v,sub_x,sub_y; + float scale = fh > 0 ? nk_tt_ScaleForPixelHeight(info, fh): + nk_tt_ScaleForMappingEmToPixels(info, -fh); + + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + + recip_h = 1.0f / (float)spc->h_oversample; + recip_v = 1.0f / (float)spc->v_oversample; + + sub_x = nk_tt__oversample_shift((int)spc->h_oversample); + sub_y = nk_tt__oversample_shift((int)spc->v_oversample); + + for (j=0; j < ranges[i].num_chars; ++j) + { + struct nk_rp_rect *r = &rects[k]; + if (r->was_packed) + { + struct nk_tt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? + ranges[i].first_unicode_codepoint_in_range + j : + ranges[i].array_of_unicode_codepoints[j]; + int glyph = nk_tt_FindGlyphIndex(info, codepoint); + nk_rp_coord pad = (nk_rp_coord) spc->padding; + + /* pad on left and top */ + r->x = (nk_rp_coord)((int)r->x + (int)pad); + r->y = (nk_rp_coord)((int)r->y + (int)pad); + r->w = (nk_rp_coord)((int)r->w - (int)pad); + r->h = (nk_rp_coord)((int)r->h - (int)pad); + + nk_tt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + nk_tt_GetGlyphBitmapBox(info, glyph, scale * (float)spc->h_oversample, + (scale * (float)spc->v_oversample), &x0,&y0,&x1,&y1); + nk_tt_MakeGlyphBitmapSubpixel(info, spc->pixels + r->x + r->y*spc->stride_in_bytes, + (int)(r->w - spc->h_oversample+1), (int)(r->h - spc->v_oversample+1), + spc->stride_in_bytes, scale * (float)spc->h_oversample, + scale * (float)spc->v_oversample, 0,0, glyph, alloc); + + if (spc->h_oversample > 1) + nk_tt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, (int)spc->h_oversample); + + if (spc->v_oversample > 1) + nk_tt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, (int)spc->v_oversample); + + bc->x0 = (nk_ushort) r->x; + bc->y0 = (nk_ushort) r->y; + bc->x1 = (nk_ushort) (r->x + r->w); + bc->y1 = (nk_ushort) (r->y + r->h); + bc->xadvance = scale * (float)advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = ((float)x0 + r->w) * recip_h + sub_x; + bc->yoff2 = ((float)y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; /* if any fail, report failure */ + } + ++k; + } + } + /* restore original values */ + spc->h_oversample = (unsigned int)old_h_over; + spc->v_oversample = (unsigned int)old_v_over; + return return_value; +} + +NK_INTERN void +nk_tt_GetPackedQuad(struct nk_tt_packedchar *chardata, int pw, int ph, + int char_index, float *xpos, float *ypos, struct nk_tt_aligned_quad *q, + int align_to_integer) +{ + float ipw = 1.0f / (float)pw, iph = 1.0f / (float)ph; + struct nk_tt_packedchar *b = (struct nk_tt_packedchar*)(chardata + char_index); + if (align_to_integer) { + int tx = nk_ifloor((*xpos + b->xoff) + 0.5f); + int ty = nk_ifloor((*ypos + b->yoff) + 0.5f); + + float x = (float)tx; + float y = (float)ty; + + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + *xpos += b->xadvance; +} + +/* ------------------------------------------------------------- + * + * FONT BAKING + * + * --------------------------------------------------------------*/ +struct nk_font_bake_data { + struct nk_tt_fontinfo info; + struct nk_rp_rect *rects; + struct nk_tt_pack_range *ranges; + nk_rune range_count; +}; + +struct nk_font_baker { + struct nk_allocator alloc; + struct nk_tt_pack_context spc; + struct nk_font_bake_data *build; + struct nk_tt_packedchar *packed_chars; + struct nk_rp_rect *rects; + struct nk_tt_pack_range *ranges; +}; + +NK_GLOBAL const nk_size nk_rect_align = NK_ALIGNOF(struct nk_rp_rect); +NK_GLOBAL const nk_size nk_range_align = NK_ALIGNOF(struct nk_tt_pack_range); +NK_GLOBAL const nk_size nk_char_align = NK_ALIGNOF(struct nk_tt_packedchar); +NK_GLOBAL const nk_size nk_build_align = NK_ALIGNOF(struct nk_font_bake_data); +NK_GLOBAL const nk_size nk_baker_align = NK_ALIGNOF(struct nk_font_baker); + +NK_INTERN int +nk_range_count(const nk_rune *range) +{ + const nk_rune *iter = range; + NK_ASSERT(range); + if (!range) return 0; + while (*(iter++) != 0); + return (iter == range) ? 0 : (int)((iter - range)/2); +} + +NK_INTERN int +nk_range_glyph_count(const nk_rune *range, int count) +{ + int i = 0; + int total_glyphs = 0; + for (i = 0; i < count; ++i) { + int diff; + nk_rune f = range[(i*2)+0]; + nk_rune t = range[(i*2)+1]; + NK_ASSERT(t >= f); + diff = (int)((t - f) + 1); + total_glyphs += diff; + } + return total_glyphs; +} + +NK_API const nk_rune* +nk_font_default_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = {0x0020, 0x00FF, 0}; + return ranges; +} + +NK_API const nk_rune* +nk_font_chinese_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3000, 0x30FF, + 0x31F0, 0x31FF, + 0xFF00, 0xFFEF, + 0x4e00, 0x9FAF, + 0 + }; + return ranges; +} + +NK_API const nk_rune* +nk_font_cyrillic_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x0400, 0x052F, + 0x2DE0, 0x2DFF, + 0xA640, 0xA69F, + 0 + }; + return ranges; +} + +NK_API const nk_rune* +nk_font_korean_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3131, 0x3163, + 0xAC00, 0xD79D, + 0 + }; + return ranges; +} + +NK_API void +nk_font_bake_memory(nk_size *temp, int *glyph_count, + struct nk_font_config *config, int count) +{ + int i; + int range_count = 0; + NK_ASSERT(config); + NK_ASSERT(glyph_count); + if (!config) { + *temp = 0; + *glyph_count = 0; + return; + } + + *glyph_count = 0; + if (!config->range) + config->range = nk_font_default_glyph_ranges(); + for (i = 0; i < count; ++i) { + range_count += nk_range_count(config[i].range); + *glyph_count += nk_range_glyph_count(config[i].range, range_count); + } + + *temp = (nk_size)*glyph_count * sizeof(struct nk_rp_rect); + *temp += (nk_size)range_count * sizeof(struct nk_tt_pack_range); + *temp += (nk_size)*glyph_count * sizeof(struct nk_tt_packedchar); + *temp += (nk_size)count * sizeof(struct nk_font_bake_data); + *temp += sizeof(struct nk_font_baker); + *temp += nk_rect_align + nk_range_align + nk_char_align; + *temp += nk_build_align + nk_baker_align; +} + +NK_INTERN struct nk_font_baker* +nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *alloc) +{ + struct nk_font_baker *baker; + if (!memory) return 0; + /* setup baker inside a memory block */ + baker = (struct nk_font_baker*)NK_ALIGN_PTR(memory, nk_baker_align); + baker->build = (struct nk_font_bake_data*)NK_ALIGN_PTR((baker + 1), nk_build_align); + baker->packed_chars = (struct nk_tt_packedchar*)NK_ALIGN_PTR((baker->build + count), nk_char_align); + baker->rects = (struct nk_rp_rect*)NK_ALIGN_PTR((baker->packed_chars + glyph_count), nk_rect_align); + baker->ranges = (struct nk_tt_pack_range*)NK_ALIGN_PTR((baker->rects + glyph_count), nk_range_align); + baker->alloc = *alloc; + return baker; +} + +NK_API int +nk_font_bake_pack(nk_size *image_memory, int *width, int *height, + struct nk_recti *custom, void *temp, nk_size temp_size, + const struct nk_font_config *config, int count, + struct nk_allocator *alloc) +{ + NK_STORAGE const nk_size max_height = 1024 * 32; + struct nk_font_baker* baker; + int total_glyph_count = 0; + int total_range_count = 0; + int i = 0; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config); + NK_ASSERT(temp); + NK_ASSERT(temp_size); + NK_ASSERT(count); + NK_ASSERT(alloc); + if (!image_memory || !width || !height || !config || !temp || + !temp_size || !count) return nk_false; + + for (i = 0; i < count; ++i) { + total_range_count += nk_range_count(config[i].range); + total_glyph_count += nk_range_glyph_count(config[i].range, total_range_count); + } + + /* setup font baker from temporary memory */ + nk_zero(temp, temp_size); + baker = nk_font_baker(temp, total_glyph_count, count, alloc); + if (!baker) return nk_false; + for (i = 0; i < count; ++i) { + const struct nk_font_config *cfg = &config[i]; + if (!nk_tt_InitFont(&baker->build[i].info, (const unsigned char*)cfg->ttf_blob, 0)) + return nk_false; + } + + *height = 0; + *width = (total_glyph_count > 1000) ? 1024 : 512; + nk_tt_PackBegin(&baker->spc, 0, (int)*width, (int)max_height, 0, 1, alloc); + { + int input_i = 0; + int range_n = 0; + int rect_n = 0; + int char_n = 0; + + /* pack custom user data first so it will be in the upper left corner*/ + if (custom) { + struct nk_rp_rect custom_space; + nk_zero(&custom_space, sizeof(custom_space)); + custom_space.w = (nk_rp_coord)((custom->w * 2) + 1); + custom_space.h = (nk_rp_coord)(custom->h + 1); + + nk_tt_PackSetOversampling(&baker->spc, 1, 1); + nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, &custom_space, 1); + *height = NK_MAX(*height, (int)(custom_space.y + custom_space.h)); + + custom->x = (short)custom_space.x; + custom->y = (short)custom_space.y; + custom->w = (short)custom_space.w; + custom->h = (short)custom_space.h; + } + + /* first font pass: pack all glyphs */ + for (input_i = 0; input_i < count; input_i++) { + int n = 0; + const nk_rune *in_range; + const struct nk_font_config *cfg = &config[input_i]; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + int glyph_count; + int range_count; + + /* count glyphs + ranges in current font */ + glyph_count = 0; range_count = 0; + for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) { + glyph_count += (int)(in_range[1] - in_range[0]) + 1; + range_count++; + } + + /* setup ranges */ + tmp->ranges = baker->ranges + range_n; + tmp->range_count = (nk_rune)range_count; + range_n += range_count; + for (i = 0; i < range_count; ++i) { + in_range = &cfg->range[i * 2]; + tmp->ranges[i].font_size = cfg->size; + tmp->ranges[i].first_unicode_codepoint_in_range = (int)in_range[0]; + tmp->ranges[i].num_chars = (int)(in_range[1]- in_range[0]) + 1; + tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n; + char_n += tmp->ranges[i].num_chars; + } + + /* pack */ + tmp->rects = baker->rects + rect_n; + rect_n += glyph_count; + nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + n = nk_tt_PackFontRangesGatherRects(&baker->spc, &tmp->info, + tmp->ranges, (int)tmp->range_count, tmp->rects); + nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, tmp->rects, (int)n); + + /* texture height */ + for (i = 0; i < n; ++i) { + if (tmp->rects[i].was_packed) + *height = NK_MAX(*height, tmp->rects[i].y + tmp->rects[i].h); + } + } + NK_ASSERT(rect_n == total_glyph_count); + NK_ASSERT(char_n == total_glyph_count); + NK_ASSERT(range_n == total_range_count); + } + *height = (int)nk_round_up_pow2((nk_uint)*height); + *image_memory = (nk_size)(*width) * (nk_size)(*height); + return nk_true; +} + +NK_API void +nk_font_bake(void *image_memory, int width, int height, + void *temp, nk_size temp_size, struct nk_font_glyph *glyphs, + int glyphs_count, const struct nk_font_config *config, int font_count) +{ + int input_i = 0; + struct nk_font_baker* baker; + nk_rune glyph_n = 0; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config); + NK_ASSERT(temp); + NK_ASSERT(temp_size); + NK_ASSERT(font_count); + NK_ASSERT(glyphs_count); + if (!image_memory || !width || !height || !config || !temp || + !temp_size || !font_count || !glyphs || !glyphs_count) + return; + + /* second font pass: render glyphs */ + baker = (struct nk_font_baker*)NK_ALIGN_PTR(temp, nk_baker_align); + nk_zero(image_memory, (nk_size)((nk_size)width * (nk_size)height)); + baker->spc.pixels = (unsigned char*)image_memory; + baker->spc.height = (int)height; + for (input_i = 0; input_i < font_count; ++input_i) { + const struct nk_font_config *cfg = &config[input_i]; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + nk_tt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges, + (int)tmp->range_count, tmp->rects, &baker->alloc); + } + nk_tt_PackEnd(&baker->spc, &baker->alloc); + + /* third pass: setup font and glyphs */ + for (input_i = 0; input_i < font_count; ++input_i) + { + nk_size i = 0; + int char_idx = 0; + nk_rune glyph_count = 0; + const struct nk_font_config *cfg = &config[input_i]; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + struct nk_baked_font *dst_font = cfg->font; + + float font_scale = nk_tt_ScaleForPixelHeight(&tmp->info, cfg->size); + int unscaled_ascent, unscaled_descent, unscaled_line_gap; + nk_tt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent, + &unscaled_line_gap); + + /* fill baked font */ + if (!cfg->merge_mode) { + dst_font->ranges = cfg->range; + dst_font->height = cfg->size; + dst_font->ascent = ((float)unscaled_ascent * font_scale); + dst_font->descent = ((float)unscaled_descent * font_scale); + dst_font->glyph_offset = glyph_n; + } + + /* fill own baked font glyph array */ + for (i = 0; i < tmp->range_count; ++i) + { + struct nk_tt_pack_range *range = &tmp->ranges[i]; + for (char_idx = 0; char_idx < range->num_chars; char_idx++) + { + nk_rune codepoint = 0; + float dummy_x = 0, dummy_y = 0; + struct nk_tt_aligned_quad q; + struct nk_font_glyph *glyph; + + /* query glyph bounds from stb_truetype */ + const struct nk_tt_packedchar *pc = &range->chardata_for_range[char_idx]; + glyph_count++; + if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue; + codepoint = (nk_rune)(range->first_unicode_codepoint_in_range + char_idx); + nk_tt_GetPackedQuad(range->chardata_for_range, (int)width, + (int)height, char_idx, &dummy_x, &dummy_y, &q, 0); + + /* fill own glyph type with data */ + glyph = &glyphs[dst_font->glyph_offset + (unsigned int)char_idx]; + glyph->codepoint = codepoint; + glyph->x0 = q.x0; glyph->y0 = q.y0; + glyph->x1 = q.x1; glyph->y1 = q.y1; + glyph->y0 += (dst_font->ascent + 0.5f); + glyph->y1 += (dst_font->ascent + 0.5f); + glyph->w = glyph->x1 - glyph->x0 + 0.5f; + glyph->h = glyph->y1 - glyph->y0; + + if (cfg->coord_type == NK_COORD_PIXEL) { + glyph->u0 = q.s0 * (float)width; + glyph->v0 = q.t0 * (float)height; + glyph->u1 = q.s1 * (float)width; + glyph->v1 = q.t1 * (float)height; + } else { + glyph->u0 = q.s0; + glyph->v0 = q.t0; + glyph->u1 = q.s1; + glyph->v1 = q.t1; + } + glyph->xadvance = (pc->xadvance + cfg->spacing.x); + if (cfg->pixel_snap) + glyph->xadvance = (float)(int)(glyph->xadvance + 0.5f); + } + } + dst_font->glyph_count = glyph_count; + glyph_n += dst_font->glyph_count; + } +} + +NK_API void +nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, + struct nk_recti img_dst, const char *texture_data_mask, int tex_width, + int tex_height, char white, char black) +{ + nk_byte *pixels; + int y = 0; + int x = 0; + int n = 0; + + NK_ASSERT(img_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + NK_ASSERT(texture_data_mask); + NK_UNUSED(tex_height); + if (!img_memory || !img_width || !img_height || !texture_data_mask) + return; + + pixels = (nk_byte*)img_memory; + for (y = 0, n = 0; y < tex_height; ++y) { + for (x = 0; x < tex_width; ++x, ++n) { + const int off0 = ((img_dst.x + x) + (img_dst.y + y) * img_width); + const int off1 = off0 + 1 + tex_width; + pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00; + pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00; + } + } +} + +NK_API void +nk_font_bake_convert(void *out_memory, int img_width, int img_height, + const void *in_memory) +{ + int n = 0; + const nk_byte *src; + nk_rune *dst; + + NK_ASSERT(out_memory); + NK_ASSERT(in_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + if (!out_memory || !in_memory || !img_height || !img_width) return; + + dst = (nk_rune*)out_memory; + src = (const nk_byte*)in_memory; + for (n = (int)(img_width * img_height); n > 0; n--) + *dst++ = ((nk_rune)(*src++) << 24) | 0x00FFFFFF; +} + +/* ------------------------------------------------------------- + * + * FONT + * + * --------------------------------------------------------------*/ +NK_INTERN float +nk_font_text_width(nk_handle handle, float height, const char *text, int len) +{ + nk_rune unicode; + int text_len = 0; + float text_width = 0; + int glyph_len = 0; + float scale = 0; + + struct nk_font *font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !text || !len) + return 0; + + scale = height/font->info.height; + glyph_len = text_len = nk_utf_decode(text, &unicode, (int)len); + if (!glyph_len) return 0; + while (text_len <= (int)len && glyph_len) { + const struct nk_font_glyph *g; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + g = nk_font_find_glyph(font, unicode); + text_width += g->xadvance * scale; + + /* offset next glyph */ + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)len - text_len); + text_len += glyph_len; + } + return text_width; +} + +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_INTERN void +nk_font_query_font_glyph(nk_handle handle, float height, + struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) +{ + float scale; + const struct nk_font_glyph *g; + struct nk_font *font; + + NK_ASSERT(glyph); + NK_UNUSED(next_codepoint); + + font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !glyph) + return; + + scale = height/font->info.height; + g = nk_font_find_glyph(font, codepoint); + glyph->width = (g->x1 - g->x0) * scale; + glyph->height = (g->y1 - g->y0) * scale; + glyph->offset = nk_vec2(g->x0 * scale, g->y0 * scale); + glyph->xadvance = (g->xadvance * scale); + glyph->uv[0] = nk_vec2(g->u0, g->v0); + glyph->uv[1] = nk_vec2(g->u1, g->v1); +} +#endif + +NK_API const struct nk_font_glyph* +nk_font_find_glyph(struct nk_font *font, nk_rune unicode) +{ + int i = 0; + int count; + int total_glyphs = 0; + const struct nk_font_glyph *glyph = 0; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + + glyph = font->fallback; + count = nk_range_count(font->info.ranges); + for (i = 0; i < count; ++i) { + int diff; + nk_rune f = font->info.ranges[(i*2)+0]; + nk_rune t = font->info.ranges[(i*2)+1]; + diff = (int)((t - f) + 1); + if (unicode >= f && unicode <= t) + return &font->glyphs[((nk_rune)total_glyphs + (unicode - f))]; + total_glyphs += diff; + } + return glyph; +} + +NK_API void +nk_font_init(struct nk_font *font, float pixel_height, + nk_rune fallback_codepoint, struct nk_font_glyph *glyphs, + const struct nk_baked_font *baked_font, nk_handle atlas) +{ + struct nk_baked_font baked; + NK_ASSERT(font); + NK_ASSERT(glyphs); + NK_ASSERT(baked_font); + if (!font || !glyphs || !baked_font) + return; + + baked = *baked_font; + nk_zero(font, sizeof(*font)); + font->info = baked; + font->scale = (float)pixel_height / (float)font->info.height; + font->glyphs = &glyphs[baked_font->glyph_offset]; + font->texture = atlas; + font->fallback_codepoint = fallback_codepoint; + font->fallback = nk_font_find_glyph(font, fallback_codepoint); + + font->handle.height = font->info.height * font->scale; + font->handle.width = nk_font_text_width; + font->handle.userdata.ptr = font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + font->handle.query = nk_font_query_font_glyph; + font->handle.texture = font->texture; +#endif +} + +/* --------------------------------------------------------------------------- + * + * DEFAULT FONT + * + * ProggyClean.ttf + * Copyright (c) 2004, 2005 Tristan Grimmer + * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) + * Download and more information at http://upperbounds.net + *-----------------------------------------------------------------------------*/ +#ifdef NK_INCLUDE_DEFAULT_FONT + + #ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverlength-strings" +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" +#endif + +NK_GLOBAL const char nk_proggy_clean_ttf_compressed_data_base85[11980+1] = + "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" + "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" + "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." + "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" + "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" + "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" + "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" + "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" + "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" + "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" + "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" + "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" + "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" + "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" + "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" + "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" + "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" + "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" + "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" + "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" + "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" + ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" + "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" + "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" + "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" + "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" + "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" + "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" + "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" + "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" + "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" + "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" + "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" + "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" + "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" + "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" + "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" + ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" + "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" + "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" + "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" + "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" + "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" + "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" + ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" + "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" + "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" + "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" + "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" + "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; + +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#elif _MSC_VER +#pragma warning (pop) +#endif + +#endif /* NK_INCLUDE_DEFAULT_FONT */ + +NK_INTERN unsigned int +nk_decompress_length(unsigned char *input) +{ + return (unsigned int)((input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]); +} + +NK_GLOBAL unsigned char *nk__barrier; +NK_GLOBAL unsigned char *nk__barrier2; +NK_GLOBAL unsigned char *nk__barrier3; +NK_GLOBAL unsigned char *nk__barrier4; +NK_GLOBAL unsigned char *nk__dout; + +NK_INTERN void +nk__match(unsigned char *data, unsigned int length) +{ + /* INVERSE of memmove... write each byte before copying the next...*/ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier4) { nk__dout = nk__barrier+1; return; } + while (length--) *nk__dout++ = *data++; +} + +NK_INTERN void +nk__lit(unsigned char *data, unsigned int length) +{ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier2) { nk__dout = nk__barrier+1; return; } + NK_MEMCPY(nk__dout, data, length); + nk__dout += length; +} + +#define nk__in2(x) ((i[x] << 8) + i[(x)+1]) +#define nk__in3(x) ((i[x] << 16) + nk__in2((x)+1)) +#define nk__in4(x) ((i[x] << 24) + nk__in3((x)+1)) + +NK_INTERN unsigned char* +nk_decompress_token(unsigned char *i) +{ + if (*i >= 0x20) { /* use fewer if's for cases that expand small */ + if (*i >= 0x80) nk__match(nk__dout-i[1]-1, (unsigned int)i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) nk__match(nk__dout-(nk__in2(0) - 0x4000 + 1), (unsigned int)i[2]+1), i += 3; + else /* *i >= 0x20 */ nk__lit(i+1, (unsigned int)i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { /* more ifs for cases that expand large, since overhead is amortized */ + if (*i >= 0x18) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x180000 + 1), (unsigned int)i[3]+1), i += 4; + else if (*i >= 0x10) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x100000 + 1), (unsigned int)nk__in2(3)+1), i += 5; + else if (*i >= 0x08) nk__lit(i+2, (unsigned int)nk__in2(0) - 0x0800 + 1), i += 2 + (nk__in2(0) - 0x0800 + 1); + else if (*i == 0x07) nk__lit(i+3, (unsigned int)nk__in2(1) + 1), i += 3 + (nk__in2(1) + 1); + else if (*i == 0x06) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), i[4]+1u), i += 5; + else if (*i == 0x04) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), (unsigned int)nk__in2(4)+1u), i += 6; + } + return i; +} + +NK_INTERN unsigned int +nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= (unsigned int)blocklen; + blocklen = 5552; + } + return (unsigned int)(s2 << 16) + (unsigned int)s1; +} + +NK_INTERN unsigned int +nk_decompress(unsigned char *output, unsigned char *i, unsigned int length) +{ + unsigned int olen; + if (nk__in4(0) != 0x57bC0000) return 0; + if (nk__in4(4) != 0) return 0; /* error! stream is > 4GB */ + olen = nk_decompress_length(i); + nk__barrier2 = i; + nk__barrier3 = i+length; + nk__barrier = output + olen; + nk__barrier4 = output; + i += 16; + + nk__dout = output; + for (;;) { + unsigned char *old_i = i; + i = nk_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + NK_ASSERT(nk__dout == output + olen); + if (nk__dout != output + olen) return 0; + if (nk_adler32(1, output, olen) != (unsigned int) nk__in4(2)) + return 0; + return olen; + } else { + NK_ASSERT(0); /* NOTREACHED */ + return 0; + } + } + NK_ASSERT(nk__dout <= output + olen); + if (nk__dout > output + olen) + return 0; + } +} + +NK_INTERN unsigned int +nk_decode_85_byte(char c) +{ return (unsigned int)((c >= '\\') ? c-36 : c-35); } + +NK_INTERN void +nk_decode_85(unsigned char* dst, const unsigned char* src) +{ + while (*src) + { + unsigned int tmp = + nk_decode_85_byte((char)src[0]) + + 85 * (nk_decode_85_byte((char)src[1]) + + 85 * (nk_decode_85_byte((char)src[2]) + + 85 * (nk_decode_85_byte((char)src[3]) + + 85 * nk_decode_85_byte((char)src[4])))); + + /* we can't assume little-endianess. */ + dst[0] = (unsigned char)((tmp >> 0) & 0xFF); + dst[1] = (unsigned char)((tmp >> 8) & 0xFF); + dst[2] = (unsigned char)((tmp >> 16) & 0xFF); + dst[3] = (unsigned char)((tmp >> 24) & 0xFF); + + src += 5; + dst += 4; + } +} + +/* ------------------------------------------------------------- + * + * FONT ATLAS + * + * --------------------------------------------------------------*/ +NK_API struct nk_font_config +nk_font_config(float pixel_height) +{ + struct nk_font_config cfg; + nk_zero_struct(cfg); + cfg.ttf_blob = 0; + cfg.ttf_size = 0; + cfg.ttf_data_owned_by_atlas = 0; + cfg.size = pixel_height; + cfg.oversample_h = 1; + cfg.oversample_v = 1; + cfg.pixel_snap = 0; + cfg.coord_type = NK_COORD_UV; + cfg.spacing = nk_vec2(0,0); + cfg.range = nk_font_default_glyph_ranges(); + cfg.fallback_glyph = '?'; + cfg.font = 0; + cfg.merge_mode = 0; + return cfg; +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_font_atlas_init_default(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + if (!atlas) return; + nk_zero_struct(*atlas); + atlas->alloc.userdata.ptr = 0; + atlas->alloc.alloc = nk_malloc; + atlas->alloc.free = nk_mfree; +} +#endif + +NK_API void +nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc) +{ + NK_ASSERT(atlas); + NK_ASSERT(alloc); + if (!atlas || !alloc) return; + nk_zero_struct(*atlas); + atlas->alloc = *alloc; +} + +NK_API void +nk_font_atlas_begin(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc && atlas->alloc.free); + if (!atlas || !atlas->alloc.alloc || !atlas->alloc.free) return; + if (atlas->glyphes) { + atlas->alloc.free(atlas->alloc.userdata, atlas->glyphes); + atlas->glyphes = 0; + } + if (atlas->pixel) { + atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); + atlas->pixel = 0; + } +} + +NK_API struct nk_font* +nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *config) +{ + struct nk_font *font = 0; + NK_ASSERT(atlas); + NK_ASSERT(config); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + NK_ASSERT(config->ttf_blob); + NK_ASSERT(config->ttf_size); + NK_ASSERT(config->size > 0.0f); + if (!atlas || !config || !config->ttf_blob || !config->ttf_size || config->size <= 0.0f|| + !atlas->alloc.alloc || !atlas->alloc.free) + return 0; + + /* allocate new font */ + if (!config->merge_mode) { + font = (struct nk_font*)atlas->alloc.alloc(atlas->alloc.userdata,0, sizeof(struct nk_font)); + NK_ASSERT(font); + if (!font) return 0; + } else { + NK_ASSERT(atlas->font_num); + font = atlas->fonts[atlas->font_num-1]; + } + + /* make sure enough memory */ + if (!atlas->config || atlas->font_num >= atlas->font_cap) { + void *tmp_font, *tmp_config; + atlas->font_cap = (!atlas->config) ? 32: (int)((float)atlas->font_cap * 2.7f); + tmp_font = atlas->alloc.alloc(atlas->alloc.userdata,0, + ((nk_size)atlas->font_cap * sizeof(struct nk_font*))); + tmp_config = atlas->alloc.alloc(atlas->alloc.userdata,0, + ((nk_size)atlas->font_cap * sizeof(struct nk_font_config))); + + if (!atlas->config) { + atlas->fonts = (struct nk_font**)tmp_font; + atlas->config = (struct nk_font_config*)tmp_config; + } else { + /* realloc */ + NK_MEMCPY(tmp_font, atlas->fonts, + sizeof(struct nk_font*) * (nk_size)atlas->font_num); + NK_MEMCPY(tmp_config, atlas->config, + sizeof(struct nk_font_config) * (nk_size)atlas->font_num); + + atlas->alloc.free(atlas->alloc.userdata, atlas->fonts); + atlas->alloc.free(atlas->alloc.userdata, atlas->config); + + atlas->fonts = (struct nk_font**)tmp_font; + atlas->config = (struct nk_font_config*)tmp_config; + } + } + + /* add font and font config into atlas */ + atlas->config[atlas->font_num] = *config; + if (!config->merge_mode) { + atlas->fonts[atlas->font_num] = font; + atlas->config[atlas->font_num].font = &font->info; + } + + /* create own copy of .TTF font blob */ + if (!config->ttf_data_owned_by_atlas) { + struct nk_font_config *c = &atlas->config[atlas->font_num]; + c->ttf_blob = atlas->alloc.alloc(atlas->alloc.userdata,0, c->ttf_size); + NK_ASSERT(c->ttf_blob); + if (!c->ttf_blob) { + atlas->font_num++; + return 0; + } + NK_MEMCPY(c->ttf_blob, config->ttf_blob, c->ttf_size); + c->ttf_data_owned_by_atlas = 1; + } + atlas->font_num++; + return font; +} + +NK_API struct nk_font* +nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, + nk_size size, float height, const struct nk_font_config *config) +{ + struct nk_font_config cfg; + NK_ASSERT(memory); + NK_ASSERT(size); + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + if (!atlas || !atlas->alloc.alloc || !atlas->alloc.free || !memory || !size) + return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 0; + return nk_font_atlas_add(atlas, &cfg); +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* +nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, + float height, const struct nk_font_config *config) +{ + nk_size size; + char *memory; + struct nk_font_config cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + if (!atlas || !file_path) return 0; + memory = nk_file_load(file_path, &size, &atlas->alloc); + if (!memory) return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} +#endif + +NK_API struct nk_font* +nk_font_atlas_add_compressed(struct nk_font_atlas *atlas, + void *compressed_data, nk_size compressed_size, float height, + const struct nk_font_config *config) +{ + unsigned int decompressed_size; + void *decompressed_data; + struct nk_font_config cfg; + + NK_ASSERT(compressed_data); + NK_ASSERT(compressed_size); + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + if (!atlas || !compressed_data || !atlas->alloc.alloc || !atlas->alloc.free) + return 0; + + decompressed_size = nk_decompress_length((unsigned char*)compressed_data); + decompressed_data = atlas->alloc.alloc(atlas->alloc.userdata,0,decompressed_size); + NK_ASSERT(decompressed_data); + if (!decompressed_data) return 0; + nk_decompress((unsigned char*)decompressed_data, (unsigned char*)compressed_data, + (unsigned int)compressed_size); + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = decompressed_data; + cfg.ttf_size = decompressed_size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} + +NK_API struct nk_font* +nk_font_atlas_add_compressed_base85(struct nk_font_atlas *atlas, + const char *data_base85, float height, const struct nk_font_config *config) +{ + int compressed_size; + void *compressed_data; + struct nk_font *font; + + NK_ASSERT(data_base85); + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + if (!atlas || !data_base85 || !atlas->alloc.alloc || !atlas->alloc.free) + return 0; + + compressed_size = (((int)nk_strlen(data_base85) + 4) / 5) * 4; + compressed_data = atlas->alloc.alloc(atlas->alloc.userdata,0, (nk_size)compressed_size); + NK_ASSERT(compressed_data); + if (!compressed_data) return 0; + nk_decode_85((unsigned char*)compressed_data, (const unsigned char*)data_base85); + font = nk_font_atlas_add_compressed(atlas, compressed_data, + (nk_size)compressed_size, height, config); + atlas->alloc.free(atlas->alloc.userdata, compressed_data); + return font; +} + +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* +nk_font_atlas_add_default(struct nk_font_atlas *atlas, + float pixel_height, const struct nk_font_config *config) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + return nk_font_atlas_add_compressed_base85(atlas, + nk_proggy_clean_ttf_compressed_data_base85, pixel_height, config); +} +#endif + +NK_API const void* +nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height, + enum nk_font_atlas_format fmt) +{ + int i = 0; + void *tmp = 0; + const char *custom_data = "...."; + nk_size tmp_size, img_size; + + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + if (!atlas || !width || !height || !atlas->alloc.alloc || !atlas->alloc.free) + return 0; + +#ifdef NK_INCLUDE_DEFAULT_FONT + /* no font added so just use default font */ + if (!atlas->font_num) + atlas->default_font = nk_font_atlas_add_default(atlas, 14.0f, 0); +#endif + if (!atlas->font_num) return 0; + + /* allocate temporary memory required for the baking process */ + nk_font_bake_memory(&tmp_size, &atlas->glyph_count, atlas->config, atlas->font_num); + tmp = atlas->alloc.alloc(atlas->alloc.userdata,0, tmp_size); + NK_ASSERT(tmp); + if (!tmp) goto failed; + + /* allocate memory glyphes for all fonts */ + atlas->glyphes = (struct nk_font_glyph*) + atlas->alloc.alloc(atlas->alloc.userdata,0, + sizeof(struct nk_font_glyph) * (nk_size)atlas->glyph_count); + NK_ASSERT(atlas->glyphes); + if (!atlas->glyphes) + goto failed; + + /* pack all glyphes into a tight fit space */ + atlas->custom.w = 2; atlas->custom.h = 2; + if (!nk_font_bake_pack(&img_size, width, height, &atlas->custom, tmp, tmp_size, + atlas->config, atlas->font_num, &atlas->alloc)) + goto failed; + + /* allocate memory for the baked image font atlas */ + atlas->pixel = atlas->alloc.alloc(atlas->alloc.userdata,0, img_size); + NK_ASSERT(atlas->pixel); + if (!atlas->pixel) + goto failed; + + /* bake glyphes and custom white pixel into image */ + nk_font_bake(atlas->pixel, *width, *height, tmp, tmp_size, atlas->glyphes, + atlas->glyph_count, atlas->config, atlas->font_num); + nk_font_bake_custom_data(atlas->pixel, *width, *height, atlas->custom, + custom_data, 2, 2, '.', 'X'); + + /* convert alpha8 image into rgba32 image */ + if (fmt == NK_FONT_ATLAS_RGBA32) { + void *img_rgba = atlas->alloc.alloc(atlas->alloc.userdata,0, + (nk_size)(*width * *height * 4)); + NK_ASSERT(img_rgba); + if (!img_rgba) goto failed; + nk_font_bake_convert(img_rgba, *width, *height, atlas->pixel); + atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); + atlas->pixel = img_rgba; + } + atlas->tex_width = *width; + atlas->tex_height = *height; + + /* initialize each font */ + for (i = 0; i < atlas->font_num; ++i) { + nk_font_init(atlas->fonts[i], atlas->config[i].size, + atlas->config[i].fallback_glyph, atlas->glyphes, + atlas->config[i].font, nk_handle_ptr(0)); + } + + /* free temporary memory */ + atlas->alloc.free(atlas->alloc.userdata, tmp); + return atlas->pixel; + +failed: + /* error so cleanup all memory */ + if (tmp) atlas->alloc.free(atlas->alloc.userdata, tmp); + if (atlas->glyphes) { + atlas->alloc.free(atlas->alloc.userdata, atlas->glyphes); + atlas->glyphes = 0; + } + if (atlas->pixel) { + atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); + atlas->pixel = 0; + } + return 0; +} + +NK_API void +nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture, + struct nk_draw_null_texture *null) +{ + int i = 0; + NK_ASSERT(atlas); + if (!atlas) { + if (!null) return; + null->texture = texture; + null->uv = nk_vec2(0.5f,0.5f); + } + if (null) { + null->texture = texture; + null->uv = nk_vec2((atlas->custom.x + 0.5f)/(float)atlas->tex_width, + (atlas->custom.y + 0.5f)/(float)atlas->tex_height); + } + for (i = 0; i < atlas->font_num; ++i) { + atlas->fonts[i]->texture = texture; + atlas->fonts[i]->handle.texture = texture; + } + + atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); + atlas->pixel = 0; + atlas->tex_width = 0; + atlas->tex_height = 0; + atlas->custom.x = 0; + atlas->custom.y = 0; + atlas->custom.w = 0; + atlas->custom.h = 0; +} + +NK_API void +nk_font_atlas_clear(struct nk_font_atlas *atlas) +{ + int i = 0; + NK_ASSERT(atlas); + NK_ASSERT(atlas->alloc.alloc); + NK_ASSERT(atlas->alloc.free); + if (!atlas || !atlas->alloc.alloc || !atlas->alloc.free) + return; + + if (atlas->fonts) { + for (i = 0; i < atlas->font_num; ++i) + atlas->alloc.free(atlas->alloc.userdata, atlas->fonts[i]); + atlas->alloc.free(atlas->alloc.userdata, atlas->fonts); + } + if (atlas->config) { + for (i = 0; i < atlas->font_num; ++i) + atlas->alloc.free(atlas->alloc.userdata, atlas->config[i].ttf_blob); + atlas->alloc.free(atlas->alloc.userdata, atlas->config); + } + if (atlas->glyphes) + atlas->alloc.free(atlas->alloc.userdata, atlas->glyphes); + if (atlas->pixel) + atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); + nk_zero_struct(*atlas); +} +#endif +/* ============================================================== + * + * INPUT + * + * ===============================================================*/ +NK_API void +nk_input_begin(struct nk_context *ctx) +{ + int i; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + + in = &ctx->input; + for (i = 0; i < NK_BUTTON_MAX; ++i) + in->mouse.buttons[i].clicked = 0; + in->keyboard.text_len = 0; + in->mouse.scroll_delta = 0; + in->mouse.prev.x = in->mouse.pos.x; + in->mouse.prev.y = in->mouse.pos.y; + for (i = 0; i < NK_KEY_MAX; i++) + in->keyboard.keys[i].clicked = 0; +} + +NK_API void +nk_input_motion(struct nk_context *ctx, int x, int y) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + in->mouse.pos.x = (float)x; + in->mouse.pos.y = (float)y; + in->mouse.delta = nk_vec2_sub(in->mouse.pos, in->mouse.prev); +} + +NK_API void +nk_input_key(struct nk_context *ctx, enum nk_keys key, int down) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->keyboard.keys[key].down == down) return; + in->keyboard.keys[key].down = down; + in->keyboard.keys[key].clicked++; +} + +NK_API void +nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, int down) +{ + struct nk_mouse_button *btn; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->mouse.buttons[id].down == down) return; + + btn = &in->mouse.buttons[id]; + btn->clicked_pos.x = (float)x; + btn->clicked_pos.y = (float)y; + btn->down = down; + btn->clicked++; +} + +NK_API void +nk_input_scroll(struct nk_context *ctx, float y) +{ + NK_ASSERT(ctx); + if (!ctx) return; + ctx->input.mouse.scroll_delta += y; +} + +NK_API void +nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph) +{ + int len = 0; + nk_rune unicode; + + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + + len = nk_utf_decode(glyph, &unicode, NK_UTF_SIZE); + if (len && ((in->keyboard.text_len + len) < NK_INPUT_MAX)) { + nk_utf_encode(unicode, &in->keyboard.text[in->keyboard.text_len], + NK_INPUT_MAX - in->keyboard.text_len); + in->keyboard.text_len += len; + } +} + +NK_API void +nk_input_char(struct nk_context *ctx, char c) +{ + nk_glyph glyph; + NK_ASSERT(ctx); + if (!ctx) return; + glyph[0] = c; + nk_input_glyph(ctx, glyph); +} + +NK_API void +nk_input_unicode(struct nk_context *ctx, nk_rune unicode) +{ + nk_glyph rune; + NK_ASSERT(ctx); + if (!ctx) return; + nk_utf_encode(unicode, rune, NK_UTF_SIZE); + nk_input_glyph(ctx, rune); +} + +NK_API void +nk_input_end(struct nk_context *ctx) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + in->mouse.delta = nk_vec2_sub(in->mouse.pos, in->mouse.prev); +} + +NK_API int +nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + if (!NK_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h)) + return nk_false; + return nk_true; +} + +NK_API int +nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, int down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return nk_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down); +} + +NK_API int +nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_false) && + btn->clicked) ? nk_true : nk_false; +} + +NK_API int +nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, int down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, down) && + btn->clicked) ? nk_true : nk_false; +} + +NK_API int +nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b) +{ + int i, down = 0; + for (i = 0; i < NK_BUTTON_MAX; ++i) + down = down || nk_input_is_mouse_click_in_rect(in, (enum nk_buttons)i, b); + return down; +} + +NK_API int +nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h); +} + +NK_API int +nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h); +} + +NK_API int +nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_rect rect) +{ + if (!i) return nk_false; + if (!nk_input_is_mouse_hovering_rect(i, rect)) return nk_false; + return nk_input_is_mouse_click_in_rect(i, id, rect); +} + +NK_API int +nk_input_is_mouse_down(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return i->mouse.buttons[id].down; +} + +NK_API int +nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id) +{ + const struct nk_mouse_button *b; + if (!i) return nk_false; + b = &i->mouse.buttons[id]; + if (b->down && b->clicked) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_mouse_released(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked); +} + +NK_API int +nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if (k->down && k->clicked) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_key_released(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if (!k->down && k->clicked) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if (k->down) return nk_true; + return nk_false; +} + +/* + * ============================================================== + * + * TEXT EDITOR + * + * =============================================================== + */ +/* stb_textedit.h - v1.8 - public domain - Sean Barrett */ +struct nk_text_find { + float x,y; /* position of n'th character */ + float height; /* height of line */ + int first_char, length; /* first char of row, and length */ + int prev_first; /*_ first char of previous row */ +}; + +struct nk_text_edit_row { + float x0,x1; + /* starting x location, end x location (allows for align=right, etc) */ + float baseline_y_delta; + /* position of baseline relative to previous row's baseline*/ + float ymin,ymax; + /* height of row above and below baseline */ + int num_chars; +}; + +/* forward declarations */ +NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int); +#define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +NK_INTERN float +nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id, + const struct nk_user_font *font) +{ + int len = 0; + nk_rune unicode = 0; + const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len); + return font->width(font->userdata, font->height, str, len); +} + +NK_INTERN void +nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit, + int line_start_id, float row_height, const struct nk_user_font *font) +{ + int l; + int glyphes = 0; + nk_rune unicode; + const char *remaining; + int len = nk_str_len_char(&edit->string); + const char *end = nk_str_get_const(&edit->string) + len; + const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l); + const struct nk_vec2 size = nk_text_calculate_text_bounds(font, + text, (int)(end - text), row_height, &remaining, 0, &glyphes, NK_STOP_ON_NEW_LINE); + + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = glyphes; +} + +NK_INTERN int +nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y, + const struct nk_user_font *font, float row_height) +{ + struct nk_text_edit_row r; + int n = edit->string.len; + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + /* search rows to find one that straddles 'y' */ + while (i < n) { + nk_textedit_layout_row(&r, edit, i, row_height, font); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + /* below all text, return 'after' last character */ + if (i >= n) + return n; + + /* check if it's before the beginning of the line */ + if (x < r.x0) + return i; + + /* check if it's before the end of the line */ + if (x < r.x1) { + /* search characters in row for one that straddles 'x' */ + k = i; + prev_x = r.x0; + for (i=0; i < r.num_chars; ++i) { + float w = nk_textedit_get_width(edit, k, i, font); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else return k+i+1; + } + prev_x += w; + } + /* shouldn't happen, but if it does, fall through to end-of-line case */ + } + + /* if the last character is a newline, return that. + * otherwise return 'after' the last character */ + if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n') + return i+r.num_chars-1; + else return i+r.num_chars; +} + +NK_INTERN void +nk_textedit_click(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API click: on mouse down, move the cursor to the clicked location, + * and reset the selection */ + state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} + +NK_INTERN void +nk_textedit_drag(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API drag: on mouse drag, move the cursor and selection endpoint + * to the clicked location */ + int p = nk_textedit_locate_coord(state, x, y, font, row_height); + if (state->select_start == state->select_end) + state->select_start = state->cursor; + state->cursor = state->select_end = p; +} + +NK_INTERN void +nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state, + int n, int single_line, const struct nk_user_font *font, float row_height) +{ + /* find the x/y location of a character, and remember info about the previous + * row in case we get a move-up event (for page up, we'll have to rescan) */ + struct nk_text_edit_row r; + int prev_start = 0; + int z = state->string.len; + int i=0, first; + + if (n == z) { + /* if it's at the end, then find the last line -- simpler than trying to + explicitly handle this case in the regular code */ + if (single_line) { + nk_textedit_layout_row(&r, state, 0, row_height, font); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; + } else { + find->y = 0; + find->x = 0; + find->height = 1; + + while (i < z) { + nk_textedit_layout_row(&r, state, i, row_height, font); + prev_start = i; + i += r.num_chars; + } + + find->first_char = i; + find->length = 0; + find->prev_first = prev_start; + } + return; + } + + /* search rows to find the one that straddles character n */ + find->y = 0; + + for(;;) { + nk_textedit_layout_row(&r, state, i, row_height, font); + if (n < i + r.num_chars) break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + /* now scan to find xpos */ + find->x = r.x0; + for (i=0; first+i < n; ++i) + find->x += nk_textedit_get_width(state, first, i, font); +} + +NK_INTERN void +nk_textedit_clamp(struct nk_text_edit *state) +{ + /* make the selection/cursor state valid if client altered the string */ + int n = state->string.len; + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + /* if clamping forced them to be equal, move the cursor to match */ + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} + +NK_API void +nk_textedit_delete(struct nk_text_edit *state, int where, int len) +{ + /* delete characters while updating undo */ + nk_textedit_makeundo_delete(state, where, len); + nk_str_delete_runes(&state->string, where, len); + state->has_preferred_x = 0; +} + +NK_API void +nk_textedit_delete_selection(struct nk_text_edit *state) +{ + /* delete the section */ + nk_textedit_clamp(state); + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + nk_textedit_delete(state, state->select_start, + state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + nk_textedit_delete(state, state->select_end, + state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} + +NK_INTERN void +nk_textedit_sortselection(struct nk_text_edit *state) +{ + /* canoncialize the selection so start <= end */ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} + +NK_INTERN void +nk_textedit_move_to_first(struct nk_text_edit *state) +{ + /* move cursor to first character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} + +NK_INTERN void +nk_textedit_move_to_last(struct nk_text_edit *state) +{ + /* move cursor to last character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} + +NK_INTERN int +nk_is_word_boundary( struct nk_text_edit *state, int idx) +{ + int len; + nk_rune c; + if (idx <= 0) return 1; + if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1; + return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' || + c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || + c == '|'); +} + +NK_INTERN int +nk_textedit_move_to_word_previous(struct nk_text_edit *state) +{ + int c = state->cursor - 1; + while( c >= 0 && !nk_is_word_boundary(state, c)) + --c; + + if( c < 0 ) + c = 0; + + return c; +} + +NK_INTERN int +nk_textedit_move_to_word_next(struct nk_text_edit *state) +{ + const int len = state->string.len; + int c = state->cursor+1; + while( c < len && !nk_is_word_boundary(state, c)) + ++c; + + if( c > len ) + c = len; + + return c; +} + +NK_INTERN void +nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state) +{ + /* update selection and cursor to match each other */ + if (!NK_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else state->cursor = state->select_end; +} + +NK_API int +nk_textedit_cut(struct nk_text_edit *state) +{ + /* API cut: delete selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_delete_selection(state); /* implicity clamps */ + state->has_preferred_x = 0; + return 1; + } + return 0; +} + +NK_API int +nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len) +{ + /* API paste: replace existing selection with passed-in text */ + int glyphes; + const char *text = (const char *) ctext; + /* if there's a selection, the paste should delete it */ + nk_textedit_clamp(state); + nk_textedit_delete_selection(state); + /* try to insert the characters */ + glyphes = nk_utf_len(ctext, len); + if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) { + nk_textedit_makeundo_insert(state, state->cursor, glyphes); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + /* remove the undo since we didn't actually insert the characters */ + if (state->undo.undo_point) + --state->undo.undo_point; + return 0; +} + +NK_API void +nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) +{ + nk_rune unicode; + int glyph_len; + int text_len = 0; + + NK_ASSERT(state); + NK_ASSERT(text); + if (!text || !total_len || !state->insert_mode) return; + + glyph_len = nk_utf_decode(text, &unicode, total_len); + if (!glyph_len) return; + while ((text_len < total_len) && glyph_len) + { + /* can't add newline in single-line mode */ + if (unicode == '\n' && state->single_line) + break; + + /* filter incoming text */ + if (state->filter && !state->filter(state, unicode)) { + glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); + text_len += glyph_len; + continue; + } + + if (state->insert_mode && !NK_TEXT_HAS_SELECTION(state) && + state->cursor < state->string.len) + { + nk_textedit_makeundo_replace(state, state->cursor, 1, 1); + nk_str_delete_runes(&state->string, state->cursor, 1); + if (nk_str_insert_text_char(&state->string, state->cursor, + text+text_len, glyph_len)) + { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + nk_textedit_delete_selection(state); /* implicity clamps */ + if (nk_str_insert_text_char(&state->string, state->cursor, + text+text_len, glyph_len)) + { + nk_textedit_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); + text_len += glyph_len; + } +} + +NK_INTERN void +nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, + const struct nk_user_font *font, float row_height) +{ +retry: + switch (key) + { + case NK_KEY_NONE: + case NK_KEY_CTRL: + case NK_KEY_ENTER: + case NK_KEY_SHIFT: + case NK_KEY_TAB: + case NK_KEY_COPY: + case NK_KEY_CUT: + case NK_KEY_PASTE: + case NK_KEY_MAX: + default: break; + case NK_KEY_TEXT_UNDO: + nk_textedit_undo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_REDO: + nk_textedit_redo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_INSERT_MODE: + state->insert_mode = !state->insert_mode; + break; + + case NK_KEY_LEFT: + if (shift_mod) { + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + /* move selection left */ + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to start of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + } break; + + case NK_KEY_RIGHT: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + /* move selection right */ + ++state->select_end; + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to end of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else ++state->cursor; + nk_textedit_clamp(state); + state->has_preferred_x = 0; + } break; + + case NK_KEY_TEXT_WORD_LEFT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_previous(state); + state->select_end = state->cursor; + nk_textedit_clamp(state ); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else { + state->cursor = nk_textedit_move_to_word_previous(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_TEXT_WORD_RIGHT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_next(state); + state->select_end = state->cursor; + nk_textedit_clamp(state); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else { + state->cursor = nk_textedit_move_to_word_next(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_DOWN: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down in single-line behave like left&right */ + key = NK_KEY_RIGHT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* now find character position down a row */ + if (find.length) + { + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + int start = find.first_char + find.length; + + state->cursor = start; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars; ++i) { + float dx = nk_textedit_get_width(state, start, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) + state->select_end = state->cursor; + } + } break; + + case NK_KEY_UP: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down become left&right */ + key = NK_KEY_LEFT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* can only go up if there's a previous row */ + if (find.prev_first != find.first_char) { + /* now find character position up a row */ + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + + state->cursor = find.prev_first; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars; ++i) { + float dx = nk_textedit_get_width(state, find.prev_first, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) state->select_end = state->cursor; + } + } break; + + case NK_KEY_DEL: + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + int n = state->string.len; + if (state->cursor < n) + nk_textedit_delete(state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case NK_KEY_BACKSPACE: + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + nk_textedit_clamp(state); + if (state->cursor > 0) { + nk_textedit_delete(state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_START: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + } else { + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_END: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = state->string.len; + state->has_preferred_x = 0; + } else { + state->cursor = state->string.len; + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_LINE_START: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + nk_textedit_find_charpos(&find, state,state->cursor, state->single_line, + font, row_height); + state->cursor = state->select_end = find.first_char; + state->has_preferred_x = 0; + } else { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->cursor = find.first_char; + state->has_preferred_x = 0; + } + } break; + + case NK_KEY_TEXT_LINE_END: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + state->select_end = state->cursor; + } else { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + }} break; + } +} + +NK_INTERN void +nk_textedit_flush_redo(struct nk_text_undo_state *state) +{ + state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; +} + +NK_INTERN void +nk_textedit_discard_undo(struct nk_text_undo_state *state) +{ + /* discard the oldest entry in the undo list */ + if (state->undo_point > 0) { + /* if the 0th undo state has characters, clean those up */ + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + /* delete n characters from all other records */ + state->undo_char_point = (short)(state->undo_char_point - n); + NK_MEMCPY(state->undo_char, state->undo_char + n, + (nk_size)state->undo_char_point*sizeof(nk_rune)); + for (i=0; i < state->undo_point; ++i) { + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage - n); + } + } + --state->undo_point; + NK_MEMCPY(state->undo_rec, state->undo_rec+1, + (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0]))); + } +} + +NK_INTERN void +nk_textedit_discard_redo(struct nk_text_undo_state *state) +{ +/* discard the oldest entry in the redo list--it's bad if this + ever happens, but because undo & redo have to store the actual + characters in different cases, the redo character buffer can + fill up even though the undo buffer didn't */ + nk_size num; + int k = NK_TEXTEDIT_UNDOSTATECOUNT-1; + if (state->redo_point <= k) { + /* if the k'th undo state has characters, clean those up */ + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + /* delete n characters from all other records */ + state->redo_char_point = (short)(state->redo_char_point + n); + num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point); + NK_MEMCPY(state->undo_char + state->redo_char_point, + state->undo_char + state->redo_char_point-n, num * sizeof(char)); + for (i = state->redo_point; i < k; ++i) { + if (state->undo_rec[i].char_storage >= 0) { + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage + n); + } + } + } + ++state->redo_point; + num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point); + if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1, + state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0])); + } +} + +NK_INTERN struct nk_text_undo_record* +nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars) +{ + /* any time we create a new undo record, we discard redo*/ + nk_textedit_flush_redo(state); + + /* if we have no free records, we have to make room, + * by sliding the existing records down */ + if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + nk_textedit_discard_undo(state); + + /* if the characters to store won't possibly fit in the buffer, + * we can't undo */ + if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return 0; + } + + /* if we don't have enough free characters in the buffer, + * we have to make room */ + while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT) + nk_textedit_discard_undo(state); + return &state->undo_rec[state->undo_point++]; +} + +NK_INTERN nk_rune* +nk_textedit_createundo(struct nk_text_undo_state *state, int pos, + int insert_len, int delete_len) +{ + struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len); + if (r == 0) + return 0; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return 0; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = (short)(state->undo_char_point + insert_len); + return &state->undo_char[r->char_storage]; + } +} + +NK_API void +nk_textedit_undo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record u, *r; + if (s->undo_point == 0) + return; + + /* we need to do two things: apply the undo record, and create a redo record */ + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) + { + /* if the undo record says to delete characters, then the redo record will + need to re-insert the characters that get deleted, so we need to store + them. + there are three cases: + - there's enough room to store the characters + - characters stored for *redoing* don't leave room for redo + - characters stored for *undoing* don't leave room for redo + if the last is true, we have to bail */ + if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) { + /* the undo records take up too much character space; there's no space + * to store the redo characters */ + r->insert_length = 0; + } else { + int i; + /* there's definitely room to store the characters eventually */ + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + /* there's currently not enough room, so discard a redo record */ + nk_textedit_discard_redo(s); + /* should never happen: */ + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + } + + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = (short)(s->redo_char_point - u.delete_length); + s->redo_char_point = (short)(s->redo_char_point - u.delete_length); + + /* now save the characters */ + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = + nk_str_rune_at(&state->string, u.where + i); + } + /* now we can carry out the deletion */ + nk_str_delete_runes(&state->string, u.where, u.delete_length); + } + + /* check type of recorded action: */ + if (u.insert_length) { + /* easy case: was a deletion, so we need to insert n characters */ + nk_str_insert_text_runes(&state->string, u.where, + &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point = (short)(s->undo_char_point - u.insert_length); + } + state->cursor = (short)(u.where + u.insert_length); + + s->undo_point--; + s->redo_point--; +} + +NK_API void +nk_textedit_redo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record *u, r; + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + + /* we need to do two things: apply the redo record, and create an undo record */ + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + /* we KNOW there must be room for the undo record, because the redo record + was derived from an undo record */ + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + /* the redo record requires us to delete characters, so the undo record + needs to store the characters */ + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = (short)(s->undo_char_point + u->insert_length); + + /* now save the characters */ + for (i=0; i < u->insert_length; ++i) { + s->undo_char[u->char_storage + i] = + nk_str_rune_at(&state->string, u->where + i); + } + } + nk_str_delete_runes(&state->string, r.where, r.delete_length); + } + + if (r.insert_length) { + /* easy case: need to insert n characters */ + nk_str_insert_text_runes(&state->string, r.where, + &s->undo_char[r.char_storage], r.insert_length); + } + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} + +NK_INTERN void +nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length) +{ + nk_textedit_createundo(&state->undo, where, 0, length); +} + +NK_INTERN void +nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} + +NK_INTERN void +nk_textedit_makeundo_replace(struct nk_text_edit *state, int where, + int old_length, int new_length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} + +NK_INTERN void +nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, + nk_filter filter) +{ + /* reset the state to default */ + state->undo.undo_point = 0; + state->undo.undo_char_point = 0; + state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE); + state->insert_mode = 0; + state->filter = filter; +} + +NK_API void +nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(memory); + if (!state || !memory || !size) return; + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_fixed(&state->string, memory, size); +} + +NK_API void +nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(alloc); + if (!state || !alloc) return; + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init(&state->string, alloc, size); +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_textedit_init_default(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_default(&state->string); +} +#endif + +NK_API void +nk_textedit_select_all(struct nk_text_edit *state) +{ + NK_ASSERT(state); + state->select_start = 0; + state->select_end = state->string.len; +} + +NK_API void +nk_textedit_free(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + nk_str_free(&state->string); +} + +/* =============================================================== + * + * TEXT WIDGET + * + * ===============================================================*/ +struct nk_text { + struct nk_vec2 padding; + struct nk_color background; + struct nk_color text; +}; + +NK_INTERN void +nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + nk_flags a, const struct nk_user_font *f) +{ + struct nk_rect label; + float text_width; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + b.h = NK_MAX(b.h, 2 * t->padding.y); + label.x = 0; label.w = 0; + label.y = b.y + t->padding.y; + label.h = b.h - 2 * t->padding.y; + + text_width = f->width(f->userdata, f->height, (const char*)string, len); + text_width += (2.0f * t->padding.x); + + /* align in x-axis */ + if (a & NK_TEXT_ALIGN_LEFT) { + label.x = b.x + t->padding.x; + label.w = NK_MAX(0, b.w - 2 * t->padding.x); + } else if (a & NK_TEXT_ALIGN_CENTERED) { + label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width); + label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); + label.x = NK_MAX(b.x + t->padding.x, label.x); + label.w = NK_MIN(b.x + b.w, label.x + label.w); + if (label.w >= label.x) label.w -= label.x; + } else if (a & NK_TEXT_ALIGN_RIGHT) { + label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); + label.w = (float)text_width + 2 * t->padding.x; + } else return; + + /* align in y-axis */ + if (a & NK_TEXT_ALIGN_MIDDLE) { + label.y = b.y + b.h/2.0f - (float)f->height/2.0f; + label.h = b.h - (b.h/2.0f + f->height/2.0f); + } else if (a & NK_TEXT_ALIGN_BOTTOM) { + label.y = b.y + b.h - f->height; + label.h = f->height; + } + nk_draw_text(o, label, (const char*)string, + len, f, t->background, t->text); +} + +NK_INTERN void +nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + const struct nk_user_font *f) +{ + float width; + int glyphs = 0; + int fitting = 0; + int done = 0; + struct nk_rect line; + struct nk_text text; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + text.padding = nk_vec2(0,0); + text.background = t->background; + text.text = t->text; + + b.w = NK_MAX(b.w, 2 * t->padding.x); + b.h = NK_MAX(b.h, 2 * t->padding.y); + b.h = b.h - 2 * t->padding.y; + + line.x = b.x + t->padding.x; + line.y = b.y + t->padding.y; + line.w = b.w - 2 * t->padding.x; + line.h = 2 * t->padding.y + f->height; + + fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width); + while (done < len) { + if (!fitting || line.y + line.h >= (b.y + b.h)) break; + nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f); + done += fitting; + line.y += f->height + 2 * t->padding.y; + fitting = nk_text_clamp(f, &string[done], len - done, + line.w, &glyphs, &width); + } +} + +/* =============================================================== + * + * BUTTON + * + * ===============================================================*/ +NK_INTERN void +nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, + struct nk_rect content, struct nk_color background, struct nk_color foreground, + float border_width, const struct nk_user_font *font) +{ + switch (type) { + case NK_SYMBOL_X: + case NK_SYMBOL_UNDERSCORE: + case NK_SYMBOL_PLUS: + case NK_SYMBOL_MINUS: { + /* single character text symbol */ + const char *X = (type == NK_SYMBOL_X) ? "x": + (type == NK_SYMBOL_UNDERSCORE) ? "_": + (type == NK_SYMBOL_PLUS) ? "+": "-"; + struct nk_text text; + text.padding = nk_vec2(0,0); + text.background = background; + text.text = foreground; + nk_widget_text(out, content, X, 1, &text, NK_TEXT_CENTERED, font); + } break; + case NK_SYMBOL_CIRCLE: + case NK_SYMBOL_CIRCLE_FILLED: + case NK_SYMBOL_RECT: + case NK_SYMBOL_RECT_FILLED: { + /* simple empty/filled shapes */ + if (type == NK_SYMBOL_RECT || type == NK_SYMBOL_RECT_FILLED) { + nk_fill_rect(out, content, 0, foreground); + if (type == NK_SYMBOL_RECT_FILLED) + nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background); + } else { + nk_fill_circle(out, content, foreground); + if (type == NK_SYMBOL_CIRCLE_FILLED) + nk_fill_circle(out, nk_shrink_rect(content, 1), background); + } + } break; + case NK_SYMBOL_TRIANGLE_UP: + case NK_SYMBOL_TRIANGLE_DOWN: + case NK_SYMBOL_TRIANGLE_LEFT: + case NK_SYMBOL_TRIANGLE_RIGHT: { + enum nk_heading heading; + struct nk_vec2 points[3]; + heading = (type == NK_SYMBOL_TRIANGLE_RIGHT) ? NK_RIGHT : + (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_LEFT: + (type == NK_SYMBOL_TRIANGLE_UP) ? NK_UP: NK_DOWN; + nk_triangle_from_direction(points, content, 0, 0, heading); + nk_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y, + points[2].x, points[2].y, foreground); + } break; + default: + case NK_SYMBOL_NONE: + case NK_SYMBOL_MAX: break; + } +} + +NK_INTERN int +nk_button_behavior(nk_flags *state, struct nk_rect r, + const struct nk_input *i, enum nk_button_behavior behavior) +{ + int ret = 0; + *state = NK_WIDGET_STATE_INACTIVE; + if (!i) return 0; + if (nk_input_is_mouse_hovering_rect(i, r)) { + *state = NK_WIDGET_STATE_HOVERED; + if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) + *state = NK_WIDGET_STATE_ACTIVE; + if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { + ret = (behavior != NK_BUTTON_DEFAULT) ? + nk_input_is_mouse_down(i, NK_BUTTON_LEFT): + nk_input_is_mouse_released(i, NK_BUTTON_LEFT); + } + } + if (*state == NK_WIDGET_STATE_HOVERED && !nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_LEFT; + return ret; +} + +NK_INTERN const struct nk_style_item* +nk_draw_button(struct nk_command_buffer *out, + const struct nk_rect *bounds, nk_flags state, + const struct nk_style_button *style) +{ + const struct nk_style_item *background; + if (state & NK_WIDGET_STATE_HOVERED) + background = &style->hover; + else if (state & NK_WIDGET_STATE_ACTIVE) + background = &style->active; + else background = &style->normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image); + } else { + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds, style->border), style->rounding, + background->data.color); + } + return background; +} + +NK_INTERN int +nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, + const struct nk_style_button *style, const struct nk_input *in, + enum nk_button_behavior behavior, struct nk_rect *content) +{ + struct nk_vec2 pad; + struct nk_rect bounds; + + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(out); + if (!out || !style) + return nk_false; + + /* calculate button content space */ + pad.x = style->padding.x + style->border; + pad.y = style->padding.y + style->border; + + content->x = r.x + style->padding.x; + content->y = r.y + style->padding.y; + content->w = r.w - 2 * style->padding.x; + content->h = r.h - 2 * style->padding.y; + + /* execute button behavior */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + return nk_button_behavior(state, bounds, in, behavior); +} + +NK_INTERN void +nk_draw_button_text(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, + const struct nk_style_button *style, const char *txt, int len, + nk_flags text_alignment, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors/images */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVERED) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVE) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *content, txt, len, &text, text_alignment, font); +} + +NK_INTERN int +nk_do_button_text(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + const char *string, int len, nk_flags align, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect content; + int ret = nk_false; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(string); + NK_ASSERT(font); + if (!out || !style || !font || !string) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw.button_text) + style->draw.button_text(out, &bounds, &content, *state, style, + string, len, align, font); + else nk_draw_button_text(out, &bounds, &content, *state, style, + string, len, align, font); + if (style->draw_end) + style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, + enum nk_symbol_type type, const struct nk_user_font *font) +{ + struct nk_color sym, bg; + const struct nk_style_item *background; + + /* select correct colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + bg = background->data.color; + else bg = style->text_background; + + if (state & NK_WIDGET_STATE_HOVERED) + sym = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVE) + sym = style->text_active; + else sym = style->text_normal; + nk_draw_symbol(out, type, *content, bg, sym, 1, font); +} + +NK_INTERN int +nk_do_button_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !style || !font || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw.button_symbol) + style->draw.button_symbol(out, &bounds, &content, *state, style, symbol, font); + else nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); + if (style->draw_end) + style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, const struct nk_image *img) +{ + nk_draw_button(out, bounds, state, style); + nk_draw_image(out, *content, img); +} + +NK_INTERN int +nk_do_button_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, enum nk_button_behavior b, + const struct nk_style_button *style, const struct nk_input *in) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, b, &content); + content.x += style->image_padding.x; + content.y += style->image_padding.y; + content.w -= 2 * style->image_padding.x; + content.h -= 2 * style->image_padding.y; + + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw.button_image) + style->draw.button_image(out, &bounds, &content, *state, style, &img); + else nk_draw_button_image(out, &bounds, &content, *state, style, &img); + if (style->draw_end) + style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_text_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, + const char *str, int len, enum nk_symbol_type type, + const struct nk_user_font *font) +{ + struct nk_color sym; + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + + /* select correct text colors */ + if (state & NK_WIDGET_STATE_HOVERED) { + sym = style->text_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVE) { + sym = style->text_active; + text.text = style->text_active; + } else { + sym = style->text_normal; + text.text = style->text_normal; + } + + text.padding = nk_vec2(0,0); + nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); +} + +NK_INTERN int +nk_do_button_text_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, const char *str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect tri = {0,0,0,0}; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + tri.y = content.y + (content.h/2) - font->height/2; + tri.w = font->height; tri.h = font->height; + if (align & NK_TEXT_ALIGN_LEFT) { + tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); + tri.x = NK_MAX(tri.x, 0); + } else tri.x = content.x + 2 * style->padding.x; + + /* draw button */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw.button_text_symbol) + style->draw.button_text_symbol(out, &bounds, &content, &tri, + *state, style, str, len, symbol, font); + else nk_draw_button_text_symbol(out, &bounds, &content, &tri, + *state, style, str, len, symbol, font); + if (style->draw_end) + style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_text_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, + const char *str, int len, const struct nk_user_font *font, + const struct nk_image *img) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVERED) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVE) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); + nk_draw_image(out, *image, img); +} + +NK_INTERN int +nk_do_button_text_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, const char* str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect icon; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !font || !style || !str) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw.button_text_image) + style->draw.button_text_image(out, &bounds, &content, &icon, + *state, style, str, len, font, &img); + else nk_draw_button_text_image(out, &bounds, &content, &icon, + *state, style, str, len, font, &img); + if (style->draw_end) + style->draw_end(out, style->userdata); + return ret; +} + +/* =============================================================== + * + * TOGGLE + * + * ===============================================================*/ +enum nk_toggle_type { + NK_TOGGLE_CHECK, + NK_TOGGLE_OPTION +}; + +NK_INTERN int +nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, + nk_flags *state, int active) +{ + *state = NK_WIDGET_STATE_INACTIVE; + if (in && nk_input_is_mouse_hovering_rect(in, select)) + *state = NK_WIDGET_STATE_HOVERED; + if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, select)) { + *state = NK_WIDGET_STATE_ACTIVE; + active = !active; + } + if (*state == NK_WIDGET_STATE_HOVERED && !nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_LEFT; + return active; +} + +NK_INTERN void +nk_draw_checkbox(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *selector, &background->data.image); + else nk_fill_rect(out, *selector, 0, background->data.color); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image); + else nk_fill_rect(out, *cursors, 0, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} + +NK_INTERN void +nk_draw_option(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *selector, &background->data.image); + else nk_fill_circle(out, *selector, background->data.color); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image); + else nk_fill_circle(out, *cursors, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} + +NK_INTERN int +nk_do_toggle(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect r, + int *active, const char *str, int len, enum nk_toggle_type type, + const struct nk_style_toggle *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int was_active; + struct nk_rect bounds; + struct nk_rect select; + struct nk_rect cursor; + struct nk_rect label; + float cursor_pad; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font || !active) + return 0; + + r.w = NK_MAX(r.w, font->height + 2 * style->padding.x); + r.h = NK_MAX(r.h, font->height + 2 * style->padding.y); + + /* add additional touch padding for touch screen devices */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + + /* calculate the selector space */ + select.w = NK_MIN(r.h, font->height + style->padding.y); + select.h = select.w; + select.x = r.x + style->padding.x; + select.y = (r.y + style->padding.y + (select.w / 2)) - (font->height / 2); + cursor_pad = (type == NK_TOGGLE_OPTION) ? + (float)(int)(select.w / 4): + (float)(int)(select.h / 6); + + /* calculate the bounds of the cursor inside the selector */ + select.h = NK_MAX(select.w, cursor_pad * 2); + cursor.h = select.h - cursor_pad * 2; + cursor.w = cursor.h; + cursor.x = select.x + cursor_pad; + cursor.y = select.y + cursor_pad; + + /* label behind the selector */ + label.x = r.x + select.w + style->padding.x * 2; + label.y = select.y; + label.w = NK_MAX(r.x + r.w, label.x + style->padding.x); + label.w -= (label.x + style->padding.x); + label.h = select.w; + + /* update selector */ + was_active = *active; + *active = nk_toggle_behavior(in, bounds, state, *active); + + /* draw selector */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (type == NK_TOGGLE_CHECK) { + if (style->draw.checkbox) + style->draw.checkbox(out, *state, + style, *active, &label, &select, &cursor, str, len, font); + else nk_draw_checkbox(out, *state, style, *active, &label, + &select, &cursor, str, len, font); + } else { + if (style->draw.radio) + style->draw.radio(out, *state, style, + *active, &label, &select, &cursor, str, len, font); + else nk_draw_option(out, *state, style, *active, &label, + &select, &cursor, str, len, font); + } + if (style->draw_end) + style->draw_end(out, style->userdata); + return (was_active != *active); +} + +/* =============================================================== + * + * SELECTABLE + * + * ===============================================================*/ +NK_INTERN void +nk_draw_selectable(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_selectable *style, int active, + const struct nk_rect *bounds, const char *string, int len, + nk_flags align, const struct nk_user_font *font) +{ + const struct nk_style_item *background; + struct nk_text text; + text.padding = style->padding; + + /* select correct colors/images */ + if (!active) { + if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->pressed; + text.text = style->text_pressed; + } else if (state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + text.text = style->text_hover; + } else { + background = &style->normal; + text.text = style->text_normal; + } + } else { + if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->pressed_active; + text.text = style->text_pressed_active; + } else if (state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover_active; + text.text = style->text_hover_active; + } else { + background = &style->normal_active; + text.text = style->text_normal_active; + } + } + + /* draw selectable background and text */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image); + text.background = nk_rgba(0,0,0,0); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + text.background = background->data.color; + } + nk_widget_text(out, *bounds, string, len, &text, align, font); +} + +NK_INTERN int +nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + const struct nk_style_selectable *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* remove padding */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + + /* update button */ + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + /* draw selectable */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw) + style->draw(out, *state, style, *value, &bounds, + str, len, align, font); + else nk_draw_selectable(out, *state, style, *value, &bounds, + str, len, align, font); + if (style->draw_end) + style->draw_end(out, style->userdata); + return old_value != *value; +} + +/* =============================================================== + * + * SLIDER + * + * ===============================================================*/ +NK_INTERN float +nk_slider_behavior(nk_flags *state, struct nk_rect *cursor, + const struct nk_input *in, const struct nk_style_slider *style, + struct nk_rect bounds, float slider_min, float slider_max, float slider_value, + float slider_step, float slider_steps) +{ + int inslider = in && nk_input_is_mouse_hovering_rect(in, bounds); + int incursor = in && nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, + bounds, nk_true); + + *state = (inslider) ? NK_WIDGET_STATE_HOVERED: NK_WIDGET_STATE_INACTIVE; + if (in && inslider && incursor) + { + const float d = in->mouse.pos.x - (cursor->x + cursor->w / 2.0f); + const float pxstep = (bounds.w - (2 * style->padding.x)) / slider_steps; + + /* only update value if the next slider step is reached */ + *state = NK_WIDGET_STATE_ACTIVE; + if (NK_ABS(d) >= pxstep) { + float ratio = 0; + const float steps = (float)((int)(NK_ABS(d) / pxstep)); + slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); + slider_value = NK_CLAMP(slider_min, slider_value, slider_max); + ratio = (slider_value - slider_min)/slider_step; + cursor->x = bounds.x + (cursor->w * ratio); + } + } + + /* slider widget state */ + if (*state == NK_WIDGET_STATE_HOVERED && + !nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return slider_value; +} + +NK_INTERN void +nk_draw_slider(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_slider *style, const struct nk_rect *bounds, + const struct nk_rect *virtual_cursor, float min, float value, float max) +{ + struct nk_rect fill; + struct nk_rect bar; + struct nk_rect scursor; + const struct nk_style_item *background; + + /* select correct slider images/colors */ + struct nk_color bar_color; + const struct nk_style_item *cursor; + if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->active; + bar_color = style->bar_active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + bar_color = style->bar_hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + bar_color = style->bar_normal; + cursor = &style->cursor_normal; + } + + /* calculate slider background bar */ + bar.x = bounds->x; + bar.y = (bounds->y + virtual_cursor->h/2) - virtual_cursor->h/8; + bar.w = bounds->w; + bar.h = bounds->h/6; + + /* resize virtual cursor to given size */ + scursor.h = style->cursor_size.y; + scursor.w = style->cursor_size.x; + scursor.y = (bar.y + bar.h/2.0f) - scursor.h/2.0f; + scursor.x = (value <= min) ? virtual_cursor->x: (value >= max) ? + ((bar.x + bar.w) - virtual_cursor->w): + virtual_cursor->x - (virtual_cursor->w/2); + + /* filled background bar style */ + fill.w = (scursor.x + (scursor.w/2.0f)) - bar.x; + fill.x = bar.x; + fill.y = bar.y; + fill.h = bar.h; + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image); + } else { + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds, style->border), style->rounding, + background->data.color); + } + + /* draw slider bar */ + nk_fill_rect(out, bar, style->rounding, bar_color); + nk_fill_rect(out, fill, style->rounding, style->bar_filled); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, scursor, &cursor->data.image); + else nk_fill_circle(out, scursor, cursor->data.color); +} + +NK_INTERN float +nk_do_slider(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + float min, float val, float max, float step, + const struct nk_style_slider *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + float slider_range; + float slider_min; + float slider_max; + float slider_value; + float slider_steps; + float cursor_offset; + struct nk_rect cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) + return 0; + + /* remove padding from slider bounds */ + bounds.x = bounds.x + style->padding.x; + bounds.y = bounds.y + style->padding.y; + bounds.h = NK_MAX(bounds.h, 2 * style->padding.y); + bounds.w = NK_MAX(bounds.w, 1 + bounds.h + 2 * style->padding.x); + bounds.h -= 2 * style->padding.y; + bounds.w -= 2 * style->padding.y; + + /* optional buttons */ + if (style->show_buttons) { + nk_flags ws; + struct nk_rect button; + button.y = bounds.y; + button.w = bounds.h; + button.h = bounds.h; + + /* decrement button */ + button.x = bounds.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT, + &style->dec_button, in, font)) + val -= step; + + /* increment button */ + button.x = (bounds.x + bounds.w) - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT, + &style->inc_button, in, font)) + val += step; + + bounds.x = bounds.x + button.w + style->spacing.x; + bounds.w = bounds.w - (2 * button.w + 2 * style->spacing.x); + } + + /* make sure the provided values are correct */ + slider_max = NK_MAX(min, max); + slider_min = NK_MIN(min, max); + slider_value = NK_CLAMP(slider_min, val, slider_max); + slider_range = slider_max - slider_min; + slider_steps = slider_range / step; + + /* calculate slider virtual cursor bounds */ + cursor_offset = (slider_value - slider_min) / step; + cursor.h = bounds.h; + cursor.w = bounds.w / (slider_steps + 1); + cursor.x = bounds.x + (cursor.w * cursor_offset); + cursor.y = bounds.y; + slider_value = nk_slider_behavior(state, &cursor, in, style, bounds, + slider_min, slider_max, slider_value, step, slider_steps); + + /* draw slider */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw) + style->draw(out, *state, style, &bounds, &cursor, + slider_min, slider_value, slider_max); + else nk_draw_slider(out, *state, style, &bounds, &cursor, + slider_min, slider_value, slider_max); + if (style->draw_end) + style->draw_end(out, style->userdata); + return slider_value; +} + +/* =============================================================== + * + * PROGRESSBAR + * + * ===============================================================*/ +NK_INTERN nk_size +nk_progress_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect r, nk_size max, nk_size value, int modifiable) +{ + *state = NK_WIDGET_STATE_INACTIVE; + if (in && modifiable && nk_input_is_mouse_hovering_rect(in, r)) { + if (nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) { + float ratio = NK_MAX(0, (float)(in->mouse.pos.x - r.x)) / (float)r.w; + value = (nk_size)NK_MAX(0,((float)max * ratio)); + *state = NK_WIDGET_STATE_ACTIVE; + } else *state = NK_WIDGET_STATE_HOVERED; + } + + /* set progressbar widget state */ + if (*state == NK_WIDGET_STATE_HOVERED && !nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_LEFT; + + if (!max) return value; + value = NK_MIN(value, max); + return value; +} + +NK_INTERN void +nk_draw_progress(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_progress *style, const struct nk_rect *bounds, + const struct nk_rect *scursor, nk_size value, nk_size max) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + NK_UNUSED(max); + NK_UNUSED(value); + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVERED){ + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *bounds, &background->data.image); + else nk_fill_rect(out, *bounds, style->rounding, background->data.color); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *scursor, &cursor->data.image); + else nk_fill_rect(out, *scursor, style->rounding, cursor->data.color); +} + +NK_INTERN nk_size +nk_do_progress(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + nk_size value, nk_size max, int modifiable, + const struct nk_style_progress *style, const struct nk_input *in) +{ + float prog_scale; + nk_size prog_value; + struct nk_rect cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) return 0; + + /* calculate progressbar cursor */ + cursor.w = NK_MAX(bounds.w, 2 * style->padding.x); + cursor.h = NK_MAX(bounds.h, 2 * style->padding.y); + cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x, style->padding.y)); + prog_scale = (float)value / (float)max; + cursor.w = (bounds.w - 2) * prog_scale; + + /* update progressbar */ + prog_value = NK_MIN(value, max); + prog_value = nk_progress_behavior(state, in, bounds, max, prog_value, modifiable); + + /* draw progressbar */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw) + style->draw(out, *state, style, &bounds, &cursor, value, max); + else nk_draw_progress(out, *state, style, &bounds, &cursor, value, max); + if (style->draw_end) + style->draw_end(out, style->userdata); + return prog_value; +} + +/* =============================================================== + * + * SCROLLBAR + * + * ===============================================================*/ +NK_INTERN float +nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, + int has_scrolling, struct nk_rect scroll, + struct nk_rect cursor, float scroll_offset, + float target, float scroll_step, enum nk_orientation o) +{ + int left_mouse_down; + int left_mouse_click_in_cursor; + if (!in) return scroll_offset; + + *state = NK_WIDGET_STATE_INACTIVE; + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, cursor, nk_true); + if (nk_input_is_mouse_hovering_rect(in, scroll)) + *state = NK_WIDGET_STATE_HOVERED; + + if (left_mouse_down && left_mouse_click_in_cursor) { + /* update cursor by mouse dragging */ + float pixel, delta; + *state = NK_WIDGET_STATE_ACTIVE; + if (o == NK_VERTICAL) { + pixel = in->mouse.delta.y; + delta = (pixel / scroll.h) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll.h); + /* This is probably one of my most disgusting hacks I have ever done. + * This basically changes the mouse clicked position with the moving + * cursor. This allows for better scroll behavior but resulted into me + * having to remove const correctness for input. But in the end I believe + * it is worth it. */ + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y += in->mouse.delta.y; + } else { + pixel = in->mouse.delta.x; + delta = (pixel / scroll.w) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll.w); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x; + } + } else if (has_scrolling && ((in->mouse.scroll_delta<0) || + (in->mouse.scroll_delta>0))) { + /* update cursor by mouse scrolling */ + scroll_offset = scroll_offset + scroll_step * (-in->mouse.scroll_delta); + if (o == NK_VERTICAL) + scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll.h); + else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll.w); + } + if (*state == NK_WIDGET_STATE_HOVERED && !nk_input_is_mouse_prev_hovering_rect(in, scroll)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, scroll)) + *state |= NK_WIDGET_STATE_LEFT; + return scroll_offset; +} + +NK_INTERN void +nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_scrollbar *style, const struct nk_rect *bounds, + const struct nk_rect *scroll) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds,style->border), + style->rounding, background->data.color); + } else { + nk_draw_image(out, *bounds, &background->data.image); + } + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *scroll, &cursor->data.image); + else nk_fill_rect(out, *scroll, style->rounding, cursor->data.color); +} + +NK_INTERN float +nk_do_scrollbarv(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect cursor; + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + NK_ASSERT(state); + if (!out || !style) return 0; + + scroll.w = NK_MAX(scroll.w, 1); + scroll.h = NK_MAX(scroll.h, 2 * scroll.w); + if (target <= scroll.h) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_h; + struct nk_rect button; + button.x = scroll.x; + button.w = scroll.w; + button.h = scroll.w; + + scroll_h = scroll.h - 2 * button.h; + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.y = scroll.y; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.y = scroll.y + scroll.h - button.h; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.y = scroll.y + button.h; + scroll.h = scroll_h; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.h); + scroll_offset = NK_CLAMP(0, offset, target - scroll.h); + scroll_ratio = scroll.h / target; + scroll_off = scroll_offset / target; + + /* calculate scrollbar cursor bounds */ + cursor.h = (scroll_ratio * scroll.h - 2); + cursor.y = scroll.y + (scroll_off * scroll.h) + 1; + cursor.w = scroll.w - 2; + cursor.x = scroll.x + 1; + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, scroll, cursor, + scroll_offset, target, scroll_step, NK_VERTICAL); + scroll_off = scroll_offset / target; + cursor.y = scroll.y + (scroll_off * scroll.h); + + /* draw scrollbar */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw) + style->draw(out, *state, style, &scroll, &cursor); + else nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) + style->draw_end(out, style->userdata); + return scroll_offset; +} + +NK_INTERN float +nk_do_scrollbarh(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect cursor; + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + if (!out || !style) return 0; + + /* scrollbar background */ + scroll.h = NK_MAX(scroll.h, 1); + scroll.w = NK_MAX(scroll.w, 2 * scroll.h); + if (target <= scroll.w) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_w; + struct nk_rect button; + button.y = scroll.y; + button.w = scroll.h; + button.h = scroll.h; + + scroll_w = scroll.w - 2 * button.w; + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.x = scroll.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.x = scroll.x + scroll.w - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.x = scroll.x + button.w; + scroll.w = scroll_w; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.w); + scroll_offset = NK_CLAMP(0, offset, target - scroll.w); + scroll_ratio = scroll.w / target; + scroll_off = scroll_offset / target; + + /* calculate cursor bounds */ + cursor.w = scroll_ratio * scroll.w - 2; + cursor.x = scroll.x + (scroll_off * scroll.w) + 1; + cursor.h = scroll.h - 2; + cursor.y = scroll.y + 1; + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, scroll, cursor, + scroll_offset, target, scroll_step, NK_HORIZONTAL); + scroll_off = scroll_offset / target; + cursor.x = scroll.x + (scroll_off * scroll.w); + + /* draw scrollbar */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw) + style->draw(out, *state, style, &scroll, &cursor); + else nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) + style->draw_end(out, style->userdata); + return scroll_offset; +} + +/* =============================================================== + * + * FILTER + * + * ===============================================================*/ +NK_API int nk_filter_default(const struct nk_text_edit *box, nk_rune unicode) +{(void)unicode;NK_UNUSED(box);return nk_true;} + +NK_API int +nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode > 128) return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_float(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '-') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && + (unicode < 'a' || unicode > 'f') && + (unicode < 'A' || unicode > 'F')) + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode < '0' || unicode > '7') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode != '0' && unicode != '1') + return nk_false; + else return nk_true; +} + +/* =============================================================== + * + * EDIT + * + * ===============================================================*/ +NK_INTERN void +nk_edit_draw_text(struct nk_command_buffer *out, + const struct nk_style_edit *style, float pos_x, float pos_y, + float x_offset, const char *text, int byte_len, float row_height, + const struct nk_user_font *font, struct nk_color background, + struct nk_color foreground, int is_selected) +{ + NK_ASSERT(out); + NK_ASSERT(font); + NK_ASSERT(style); + if (!text || !byte_len || !out || !style) return; + + {int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + float line_width = 0; + float glyph_width; + const char *line = text; + float line_offset = 0; + int line_count = 0; + + struct nk_text txt; + txt.padding = nk_vec2(0,0); + txt.background = background; + txt.text = foreground; + + glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len); + if (!glyph_len) return; + while ((text_len < byte_len) && glyph_len) + { + if (unicode == '\n') { + /* new line sepeator so draw previous line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) /* selection needs to draw different background color */ + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_CENTERED, font); + + text_len++; + line_count++; + line_width = 0; + line = text + text_len; + line_offset += row_height; + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len)); + continue; + } + if (unicode == '\r') { + text_len++; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + line_width += (float)glyph_width; + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + if (line_width > 0) { + /* draw last line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_LEFT, font); + }} +} + +NK_INTERN nk_flags +nk_do_edit(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, nk_flags flags, nk_filter filter, + struct nk_text_edit *edit, const struct nk_style_edit *style, + struct nk_input *in, const struct nk_user_font *font) +{ + struct nk_rect area; + nk_flags ret = 0; + float row_height; + char prev_state = 0; + char is_hovered = 0; + char select_all = 0; + char cursor_follow = 0; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(style); + if (!state || !out || !style) + return ret; + + /* visible text area calculation */ + area.x = bounds.x + style->padding.x + style->border; + area.y = bounds.y + style->padding.y + style->border; + area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border); + area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border); + if (flags & NK_EDIT_MULTILINE) + area.h = area.h - style->scrollbar_size.y; + row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h; + + /* upate edit state */ + prev_state = (char)edit->active; + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds); + if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) { + edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, + bounds.x, bounds.y, bounds.w, bounds.h); + } + + /* (de)activate text editor */ + if (!prev_state && edit->active) { + const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE; + nk_textedit_clear_state(edit, type, filter); + if (flags & NK_EDIT_ALWAYS_INSERT_MODE) + edit->insert_mode = nk_true; + if (flags & NK_EDIT_AUTO_SELECT) + select_all = nk_true; + } else if (!edit->active) edit->insert_mode = 0; + + ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE; + if (prev_state != edit->active) + ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED; + + /* handle user input */ + if (edit->active && in && !(flags & NK_EDIT_READ_ONLY)) + { + int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down; + const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x; + const float mouse_y = (!(flags & NK_EDIT_MULTILINE)) ? + (in->mouse.pos.y - (area.y + area.h * 0.5f)) + edit->scrollbar.y: + (in->mouse.pos.y - area.y) + edit->scrollbar.y; + + /* mouse click handler */ + if (select_all) { + nk_textedit_select_all(edit); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) { + nk_textedit_click(edit, mouse_x, mouse_y, font, row_height); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) { + nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height); + cursor_follow = nk_true; + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked && + in->mouse.buttons[NK_BUTTON_RIGHT].down) { + nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height); + nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height); + cursor_follow = nk_true; + } + + {int i; /* keyboard input */ + for (i = 0; i < NK_KEY_MAX; ++i) { + if (nk_input_is_key_pressed(in, (enum nk_keys)i)) { + if (i == NK_KEY_ENTER) continue; /* special case */ + nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height); + cursor_follow = nk_true; + } + }} + + /* text input */ + edit->filter = filter; + if (in->keyboard.text_len) { + nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); + cursor_follow = nk_true; + } + + /* enter key handler */ + if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) { + if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod) { + nk_textedit_text(edit, "\n", 1); + } else if (flags & NK_EDIT_SIG_ENTER) { + ret = NK_EDIT_INACTIVE; + ret |= NK_EDIT_DEACTIVATED; + ret |= NK_EDIT_COMMITED; + edit->active = 0; + } else nk_textedit_text(edit, "\n", 1); + } + + /* cut & copy handler */ + {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY); + int cut = nk_input_is_key_pressed(in, NK_KEY_CUT); + if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD) && edit->clip.copy) + { + int glyph_len; + nk_rune unicode; + const char *text; + int begin = edit->select_start; + int end = edit->select_end; + + begin = NK_MIN(begin, end); + end = NK_MAX(begin, end); + text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len); + edit->clip.copy(edit->clip.userdata, text, end - begin); + if (cut){ + nk_textedit_cut(edit); + cursor_follow = nk_true; + } + }} + + /* paste handler */ + {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE); + if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) { + edit->clip.paste(edit->clip.userdata, edit); + cursor_follow = nk_true; + }} + + /* tab handler */ + {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB); + if (tab && (flags & NK_EDIT_ALLOW_TAB)) { + const char c = '\t'; + if (filter && filter(edit, (nk_rune)c)) { + nk_textedit_text(edit, &c, 1); + cursor_follow = nk_true; + } + }} + } + + /* set widget state */ + if (edit->active) + *state = NK_WIDGET_STATE_ACTIVE; + else *state = NK_WIDGET_STATE_INACTIVE; + if (is_hovered) + *state |= NK_WIDGET_STATE_HOVERED; + + /* DRAW EDIT */ + {struct nk_rect clip; + struct nk_rect old_clip = out->clip; + const char *text = nk_str_get_const(&edit->string); + int len = nk_str_len_char(&edit->string); + + {/* select background colors/images */ + const struct nk_style_item *background; + if (*state & NK_WIDGET_STATE_ACTIVE) + background = &style->active; + else if (*state & NK_WIDGET_STATE_HOVERED) + background = &style->hover; + else background = &style->normal; + + /* draw background frame */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(bounds,style->border), + style->rounding, background->data.color); + } else nk_draw_image(out, bounds, &background->data.image);} + + area.w -= style->cursor_size; + nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h); + nk_push_scissor(out, clip); + if (edit->active) + { + int total_lines = 1; + struct nk_vec2 text_size = nk_vec2(0,0); + + /* text pointer positions */ + const char *cursor_ptr = 0; + const char *select_begin_ptr = 0; + const char *select_end_ptr = 0; + + /* 2D pixel positions */ + struct nk_vec2 cursor_pos = nk_vec2(0,0); + struct nk_vec2 selection_offset_start = nk_vec2(0,0); + struct nk_vec2 selection_offset_end = nk_vec2(0,0); + + int selection_begin = NK_MIN(edit->select_start, edit->select_end); + int selection_end = NK_MAX(edit->select_start, edit->select_end); + + /* calculate total line count + total space + cursor/selection position */ + float line_width = 0.0f; + if (text && len) + { + /* utf8 encoding */ + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + int glyphs = 0; + int row_begin = 0; + + glyph_len = nk_utf_decode(text, &unicode, len); + glyph_width = font->width(font->userdata, font->height, text, glyph_len); + line_width = 0; + + /* iterate all lines */ + while ((text_len < len) && glyph_len) + { + /* set cursor 2D position and line */ + if (!cursor_ptr && glyphs == edit->cursor) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + cursor_pos.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + cursor_pos.x = row_size.x; + cursor_ptr = text + text_len; + } + + /* set start selection 2D position and line */ + if (!select_begin_ptr && edit->select_start != edit->select_end && + glyphs == selection_begin) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_start.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_start.x = row_size.x; + select_begin_ptr = text + text_len; + if (*select_begin_ptr == '\n') + select_begin_ptr++; + } + + /* set end selection 2D position and line */ + if (!select_end_ptr && edit->select_start != edit->select_end && + glyphs == selection_end) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_end.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_end.x = row_size.x; + select_end_ptr = text + text_len; + if (*select_end_ptr == '\n') + select_end_ptr++; + + } + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + total_lines++; + line_width = 0; + text_len++; + glyphs++; + row_begin = text_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + continue; + } + + glyphs++; + text_len += glyph_len; + line_width += (float)glyph_width; + + glyph_width = font->width(font->userdata, font->height, + text+text_len, glyph_len); + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + continue; + } + text_size.y = (float)total_lines * row_height; + + /* handle case if cursor is at end of text buffer */ + if (!cursor_ptr && edit->cursor == edit->string.len) { + cursor_pos.x = line_width; + cursor_pos.y = text_size.y - row_height; + } + } + { + /* scrollbar */ + if (cursor_follow) + { + /* update scrollbar to follow cursor */ + if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) { + /* horizontal scroll */ + const float scroll_increment = area.w * 0.25f; + if (cursor_pos.x < edit->scrollbar.x) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment); + if (cursor_pos.x >= edit->scrollbar.x + area.w) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x + scroll_increment); + } else edit->scrollbar.x = 0; + + if (flags & NK_EDIT_MULTILINE) { + /* vertical scroll */ + if (cursor_pos.y < edit->scrollbar.y) + edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height); + if (cursor_pos.y >= edit->scrollbar.y + area.h) + edit->scrollbar.y = edit->scrollbar.y + row_height; + } else edit->scrollbar.y = 0; + } + + /* scollbar widget */ + {nk_flags ws; + struct nk_rect scroll; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + + scroll.x = (bounds.x + bounds.w) - style->scrollbar_size.x; + scroll.y = bounds.y; + scroll.w = style->scrollbar_size.x; + scroll.h = bounds.h; + + scroll_offset = edit->scrollbar.y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = text_size.y; + edit->scrollbar.y = nk_do_scrollbarv(&ws, out, bounds, 0, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &style->scrollbar, in, font);} + } + + /* draw text */ + {struct nk_color background_color; + struct nk_color text_color; + struct nk_color sel_background_color; + struct nk_color sel_text_color; + struct nk_color cursor_color; + struct nk_color cursor_text_color; + const struct nk_style_item *background; + + /* select correct colors to draw */ + if (*state & NK_WIDGET_STATE_ACTIVE) { + background = &style->active; + text_color = style->text_active; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_color = style->cursor_hover; + cursor_text_color = style->cursor_text_hover; + } else if (*state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + text_color = style->text_hover; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_text_color = style->cursor_text_hover; + cursor_color = style->cursor_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + sel_text_color = style->selected_text_normal; + sel_background_color = style->selected_normal; + cursor_color = style->cursor_normal; + cursor_text_color = style->cursor_text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + + + if (edit->select_start == edit->select_end) { + /* no selection so just draw the complete text */ + const char *begin = nk_str_get_const(&edit->string); + int l = nk_str_len_char(&edit->string); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } else { + /* edit has selection so draw 1-3 text chunks */ + if (edit->select_start != edit->select_end && selection_begin > 0){ + /* draw unselected text before selection */ + const char *begin = nk_str_get_const(&edit->string); + NK_ASSERT(select_begin_ptr); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin), + row_height, font, background_color, text_color, nk_false); + } + if (edit->select_start != edit->select_end) { + /* draw selected text */ + NK_ASSERT(select_begin_ptr); + if (!select_end_ptr) { + const char *begin = nk_str_get_const(&edit->string); + select_end_ptr = begin + nk_str_len_char(&edit->string); + } + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_start.y - edit->scrollbar.y, + selection_offset_start.x, + select_begin_ptr, (int)(select_end_ptr - select_begin_ptr), + row_height, font, sel_background_color, sel_text_color, nk_true); + } + if ((edit->select_start != edit->select_end && + selection_end < edit->string.len)) + { + /* draw unselected text after selected text */ + const char *begin = select_end_ptr; + const char *end = nk_str_get_const(&edit->string) + + nk_str_len_char(&edit->string); + NK_ASSERT(select_end_ptr); + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_end.y - edit->scrollbar.y, + selection_offset_end.x, + begin, (int)(end - begin), row_height, font, + background_color, text_color, nk_true); + } + } + + /* cursor */ + if (edit->select_start == edit->select_end) + { + if (edit->cursor == nk_str_len(&edit->string) || (cursor_ptr && *cursor_ptr == '\n')) { + /* draw cursor at end of line */ + struct nk_rect cursor; + cursor.w = style->cursor_size; + cursor.h = font->height; + cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; + cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; + cursor.y -= edit->scrollbar.y; + nk_fill_rect(out, cursor, 0, cursor_color); + } else { + /* draw cursor at inside text */ + int glyph_len; + struct nk_rect label; + struct nk_text txt; + + nk_rune unicode; + NK_ASSERT(cursor_ptr); + glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4); + + label.x = area.x + cursor_pos.x - edit->scrollbar.x; + label.y = area.y + cursor_pos.y - edit->scrollbar.y; + label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len); + label.h = row_height; + + txt.padding = nk_vec2(0,0); + txt.background = cursor_color; + txt.text = cursor_text_color; + nk_fill_rect(out, label, 0, cursor_color); + nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); + } + }} + } else { + /* not active so just draw text */ + int l = nk_str_len(&edit->string); + const char *begin = nk_str_get_const(&edit->string); + + const struct nk_style_item *background; + struct nk_color background_color; + struct nk_color text_color; + if (*state & NK_WIDGET_STATE_ACTIVE) { + background = &style->active; + text_color = style->text_active; + } else if (*state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + text_color = style->text_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } + nk_push_scissor(out, old_clip);} + return ret; +} + +/* =============================================================== + * + * PROPERTY + * + * ===============================================================*/ +enum nk_property_status { + NK_PROPERTY_DEFAULT, + NK_PROPERTY_EDIT, + NK_PROPERTY_DRAG +}; + +enum nk_property_filter { + NK_FILTER_INT, + NK_FILTER_FLOAT +}; + +NK_INTERN float +nk_drag_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect drag, float min, float val, float max, float inc_per_pixel) +{ + int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_cursor = in && + nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true); + + *state = NK_WIDGET_STATE_INACTIVE; + if (nk_input_is_mouse_hovering_rect(in, drag)) + *state = NK_WIDGET_STATE_HOVERED; + + if (left_mouse_down && left_mouse_click_in_cursor) { + float delta, pixels; + pixels = in->mouse.delta.x; + delta = pixels * inc_per_pixel; + val += delta; + val = NK_CLAMP(min, val, max); + *state = NK_WIDGET_STATE_ACTIVE; + } + if (*state == NK_WIDGET_STATE_HOVERED && !nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_LEFT; + return val; +} + +NK_INTERN float +nk_property_behavior(nk_flags *ws, const struct nk_input *in, + struct nk_rect property, struct nk_rect label, struct nk_rect edit, + struct nk_rect empty, int *state, float min, float value, float max, + float step, float inc_per_pixel) +{ + NK_UNUSED(step); + if (in && *state == NK_PROPERTY_DEFAULT) { + if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT)) + *state = NK_PROPERTY_EDIT; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true)) + *state = NK_PROPERTY_DRAG; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true)) + *state = NK_PROPERTY_DRAG; + } + if (*state == NK_PROPERTY_DRAG) { + value = nk_drag_behavior(ws, in, property, min, value, max, inc_per_pixel); + if (!(*ws & NK_WIDGET_STATE_ACTIVE)) *state = NK_PROPERTY_DEFAULT; + } + return value; +} + +NK_INTERN void +nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, + const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, + const char *name, int len, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background and text color */ + if (state & NK_WIDGET_STATE_ACTIVE) { + background = &style->active; + text.text = style->label_active; + } else if (state & NK_WIDGET_STATE_HOVERED) { + background = &style->hover; + text.text = style->label_hover; + } else { + background = &style->normal; + text.text = style->label_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds,style->border), + style->rounding, background->data.color); + } + + /* draw label */ + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font); +} + +NK_INTERN float +nk_do_property(nk_flags *ws, + struct nk_command_buffer *out, struct nk_rect property, + const char *name, float min, float val, float max, + float step, float inc_per_pixel, char *buffer, int *len, + int *state, int *cursor, const struct nk_style_property *style, + enum nk_property_filter filter, struct nk_input *in, + const struct nk_user_font *font, struct nk_text_edit *text_edit) +{ + const nk_filter filters[] = { + nk_filter_decimal, + nk_filter_float + }; + int active, old; + int num_len, name_len; + char string[NK_MAX_NUMBER_BUFFER]; + float size; + + float property_min; + float property_max; + float property_value; + + char *dst = 0; + int *length; + + struct nk_rect left; + struct nk_rect right; + struct nk_rect label; + struct nk_rect edit; + struct nk_rect empty; + + /* make sure the provided values are correct */ + property_max = NK_MAX(min, max); + property_min = NK_MIN(min, max); + property_value = NK_CLAMP(property_min, val, property_max); + + /* left decrement button */ + left.h = font->height/2; + left.w = left.h; + left.x = property.x + style->border + style->padding.x; + left.y = property.y + style->border + property.h/2.0f - left.h/2; + + /* text label */ + name_len = nk_strlen(name); + size = font->width(font->userdata, font->height, name, name_len); + label.x = left.x + left.w + style->padding.x; + label.w = (float)size + 2 * style->padding.x; + label.y = property.y + style->border; + label.h = property.h - 2 * style->border; + + /* right increment button */ + right.y = left.y; + right.w = left.w; + right.h = left.h; + right.x = property.x + property.w - (right.w + style->padding.x); + + /* edit */ + if (*state == NK_PROPERTY_EDIT) { + size = font->width(font->userdata, font->height, buffer, *len); + length = len; + dst = buffer; + } else { + nk_ftos(string, property_value); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + size = font->width(font->userdata, font->height, string, num_len); + dst = string; + length = &num_len; + } + edit.w = (float)size + 2 * style->padding.x; + edit.x = right.x - (edit.w + style->padding.x); + edit.y = property.y + style->border + 1; + edit.h = property.h - (2 * style->border + 2); + + /* empty left space activator */ + empty.w = edit.x - (label.x + label.w); + empty.x = label.x + label.w; + empty.y = property.y; + empty.h = property.h; + + /* update property */ + old = (*state == NK_PROPERTY_EDIT); + property_value = nk_property_behavior(ws, in, property, label, edit, empty, + state, property_min, property_value, property_max, + step, inc_per_pixel); + + /* draw property */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (style->draw) + style->draw(out, style, &property, &label, *ws, name, name_len, font); + else nk_draw_property(out, style, &property, &label, *ws, name, name_len, font); + if (style->draw_end) + style->draw_end(out, style->userdata); + + /* execute right and left button */ + if (nk_do_button_symbol(ws, out, left, style->sym_left, NK_BUTTON_DEFAULT, + &style->dec_button, in, font)) + property_value = NK_CLAMP(min, property_value - step, max); + if (nk_do_button_symbol(ws, out, right, style->sym_right, NK_BUTTON_DEFAULT, + &style->inc_button, in, font)) + property_value = NK_CLAMP(min, property_value + step, max); + + active = (*state == NK_PROPERTY_EDIT); + if (old != NK_PROPERTY_EDIT && active) { + /* property has been activated so setup buffer */ + NK_MEMCPY(buffer, dst, (nk_size)*length); + *cursor = nk_utf_len(buffer, *length); + *len = *length; + length = len; + dst = buffer; + } + { + /* execute and run text edit field */ + nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]); + text_edit->active = (unsigned char)active; + text_edit->string.len = *length; + text_edit->cursor = NK_CLAMP(0, *cursor, *length); + text_edit->string.buffer.allocated = (nk_size)*length; + text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER; + text_edit->string.buffer.memory.ptr = dst; + nk_do_edit(ws, out, edit, NK_EDIT_ALWAYS_INSERT_MODE, filters[filter], + text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font); + + *length = text_edit->string.len; + active = text_edit->active; + *cursor = text_edit->cursor; + } + if (active && nk_input_is_key_pressed(in, NK_KEY_ENTER)) + active = !active; + + if (old && !active) { + /* property is now not active so convert edit text to value*/ + *state = NK_PROPERTY_DEFAULT; + buffer[*len] = '\0'; + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + nk_strtof(&property_value, buffer); + property_value = NK_CLAMP(min, property_value, max); + } + return property_value; +} +/* =============================================================== + * + * COLOR PICKER + * + * ===============================================================*/ +NK_INTERN int +nk_color_picker_behavior(nk_flags *state, + const struct nk_rect *bounds, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_color *color, const struct nk_input *in) +{ + float hsva[4]; + int value_changed = 0; + int hsv_changed = 0; + + NK_ASSERT(state); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + NK_ASSERT(color); + + /* color matrix */ + nk_color_hsva_fv(hsva, *color); + if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) { + hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); + hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); + value_changed = hsv_changed = 1; + } + + /* hue bar */ + if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) { + hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); + value_changed = hsv_changed = 1; + } + + /* alpha bar */ + if (alpha_bar) { + if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) { + hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); + value_changed = 1; + } + } + + *state = NK_WIDGET_STATE_INACTIVE; + if (hsv_changed) { + *color = nk_hsva_fv(hsva); + *state = NK_WIDGET_STATE_ACTIVE; + } + if (value_changed) { + color->a = (nk_byte)(hsva[3] * 255.0f); + *state = NK_WIDGET_STATE_ACTIVE; + } + + /* set color picker widget state */ + if (nk_input_is_mouse_hovering_rect(in, *bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state == NK_WIDGET_STATE_HOVERED && !nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return value_changed; +} + +NK_INTERN void +nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_color color) +{ + NK_STORAGE const struct nk_color black = {0,0,0,255}; + NK_STORAGE const struct nk_color white = {255, 255, 255, 255}; + NK_STORAGE const struct nk_color black_trans = {0,0,0,0}; + + const float crosshair_size = 7.0f; + struct nk_color temp; + float hsva[4]; + float line_y; + int i; + + NK_ASSERT(o); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + NK_ASSERT(alpha_bar); + + /* draw hue bar */ + nk_color_hsv_fv(hsva, color); + for (i = 0; i < 6; ++i) { + NK_GLOBAL const struct nk_color hue_colors[] = { + {255, 0, 0, 255}, {255,255,0,255}, {0,255,0,255}, {0, 255,255,255}, + {0,0,255,255}, {255, 0, 255, 255}, {255, 0, 0, 255}}; + nk_fill_rect_multi_color(o, + nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, + hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], + hue_colors[i+1], hue_colors[i+1]); + } + line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); + nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + + /* draw alpha bar */ + if (alpha_bar) { + float alpha = NK_SATURATE((float)color.a/255.0f); + line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); + + nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); + nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + } + + /* draw color matrix */ + temp = nk_hsv_f(hsva[0], 1.0f, 1.0f); + nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white); + nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); + + /* draw cross-hair */ + {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2]; + p.x = (float)(int)(matrix->x + S * matrix->w + 0.5f); + p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h + 0.5f); + nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); + nk_stroke_line(o, p.x + crosshair_size, p.y, p.x+2, p.y, 1.0f, white); + nk_stroke_line(o, p.x, p.y + crosshair_size, p.x, p.y+2, 1.0f, nk_rgb(255,255,255)); + nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, nk_rgb(255,255,255));} +} + +NK_INTERN int +nk_do_color_picker(nk_flags *state, + struct nk_command_buffer *out, struct nk_color *color, + enum nk_color_format fmt, struct nk_rect bounds, + struct nk_vec2 padding, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret = 0; + struct nk_rect matrix; + struct nk_rect hue_bar; + struct nk_rect alpha_bar; + float bar_w; + + NK_ASSERT(out); + NK_ASSERT(color); + NK_ASSERT(state); + NK_ASSERT(font); + if (!out || !color || !state || !font) + return ret; + + bar_w = font->height; + bounds.x += padding.x; + bounds.y += padding.x; + bounds.w -= 2 * padding.x; + bounds.h -= 2 * padding.y; + + matrix.x = bounds.x; + matrix.y = bounds.y; + matrix.h = bounds.h; + matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); + + hue_bar.w = bar_w; + hue_bar.y = bounds.y; + hue_bar.h = matrix.h; + hue_bar.x = matrix.x + matrix.w + padding.x; + + alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; + alpha_bar.y = bounds.y; + alpha_bar.w = bar_w; + alpha_bar.h = matrix.h; + + ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar, + (fmt == NK_RGBA) ? &alpha_bar:0, color, in); + nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *color); + return ret; +} + +/* ============================================================== + * + * STYLE + * + * ===============================================================*/ +NK_API void nk_style_default(struct nk_context *ctx){nk_style_from_table(ctx, 0);} +#define NK_COLOR_MAP(NK_COLOR)\ + NK_COLOR(NK_COLOR_TEXT, 175,175,175,255) \ + NK_COLOR(NK_COLOR_WINDOW, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_HEADER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BORDER, 65, 65, 65, 255) \ + NK_COLOR(NK_COLOR_BUTTON, 50, 50, 50, 255) \ + NK_COLOR(NK_COLOR_BUTTON_HOVER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BUTTON_ACTIVE, 35, 35, 35, 255) \ + NK_COLOR(NK_COLOR_TOGGLE, 100,100,100,255) \ + NK_COLOR(NK_COLOR_TOGGLE_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_TOGGLE_CURSOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT_ACTIVE, 35, 35, 35,255) \ + NK_COLOR(NK_COLOR_SLIDER, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ + NK_COLOR(NK_COLOR_PROPERTY, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT_CURSOR, 175,175,175,255) \ + NK_COLOR(NK_COLOR_COMBO, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART, 120,120,120,255) \ + NK_COLOR(NK_COLOR_CHART_COLOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT,255, 0, 0, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER,120,120,120,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE,150,150,150,255) \ + NK_COLOR(NK_COLOR_TAB_HEADER, 40, 40, 40,255) + +NK_GLOBAL const struct nk_color +nk_default_color_style[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) {b,c,d,e}, +NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; + +NK_GLOBAL const char *nk_color_names[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) #a, +NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; + +NK_API const char *nk_style_color_name(enum nk_style_colors c) +{return nk_color_names[c];} + +NK_API struct nk_style_item nk_style_item_image(struct nk_image img) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_IMAGE; i.data.image = img; return i;} + +NK_API struct nk_style_item nk_style_item_color(struct nk_color col) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = col; return i;} + +NK_API struct nk_style_item nk_style_item_hide(void) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = nk_rgba(0,0,0,0); return i;} + +NK_API void +nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) +{ + struct nk_style *style; + struct nk_style_text *text; + struct nk_style_button *button; + struct nk_style_toggle *toggle; + struct nk_style_selectable *select; + struct nk_style_slider *slider; + struct nk_style_progress *prog; + struct nk_style_scrollbar *scroll; + struct nk_style_edit *edit; + struct nk_style_property *property; + struct nk_style_combo *combo; + struct nk_style_chart *chart; + struct nk_style_tab *tab; + struct nk_style_window *win; + + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + table = (!table) ? nk_default_color_style: table; + + /* default text */ + text = &style->text; + text->color = table[NK_COLOR_TEXT]; + text->padding = nk_vec2(4,4); + + /* default button */ + button = &style->button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_BUTTON]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_BORDER]; + button->text_background = table[NK_COLOR_BUTTON]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(4.0f,4.0f); + button->image_padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f, 0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 4.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* contextual button */ + button = &style->contextual_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(4.0f,4.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* menu button */ + button = &style->menu_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(4.0f,4.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 1.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* checkbox toggle */ + toggle = &style->checkbox; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(4.0f, 4.0f); + toggle->touch_padding = nk_vec2(0,0); + + /* option toggle */ + toggle = &style->option; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(4.0f, 4.0f); + toggle->touch_padding = nk_vec2(0,0); + + /* selectable */ + select = &style->selectable; + nk_zero_struct(*select); + select->normal = nk_style_item_color(table[NK_COLOR_SELECT]); + select->hover = nk_style_item_color(table[NK_COLOR_SELECT]); + select->pressed = nk_style_item_color(table[NK_COLOR_SELECT]); + select->normal_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->hover_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->pressed_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->text_normal = table[NK_COLOR_TEXT]; + select->text_hover = table[NK_COLOR_TEXT]; + select->text_pressed = table[NK_COLOR_TEXT]; + select->text_normal_active = table[NK_COLOR_TEXT]; + select->text_hover_active = table[NK_COLOR_TEXT]; + select->text_pressed_active = table[NK_COLOR_TEXT]; + select->padding = nk_vec2(4.0f,4.0f); + select->touch_padding = nk_vec2(0,0); + select->userdata = nk_handle_ptr(0); + select->rounding = 0.0f; + select->draw_begin = 0; + select->draw = 0; + select->draw_end = 0; + + /* slider */ + slider = &style->slider; + nk_zero_struct(*slider); + slider->normal = nk_style_item_hide(); + slider->hover = nk_style_item_hide(); + slider->active = nk_style_item_hide(); + slider->bar_normal = table[NK_COLOR_SLIDER]; + slider->bar_hover = table[NK_COLOR_SLIDER]; + slider->bar_active = table[NK_COLOR_SLIDER]; + slider->bar_filled = table[NK_COLOR_SLIDER_CURSOR]; + slider->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + slider->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + slider->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + slider->inc_symbol = NK_SYMBOL_TRIANGLE_RIGHT; + slider->dec_symbol = NK_SYMBOL_TRIANGLE_LEFT; + slider->cursor_size = nk_vec2(16,16); + slider->padding = nk_vec2(4,4); + slider->spacing = nk_vec2(4,4); + slider->userdata = nk_handle_ptr(0); + slider->show_buttons = nk_false; + slider->bar_height = 8; + slider->rounding = 0; + slider->draw_begin = 0; + slider->draw = 0; + slider->draw_end = 0; + + /* slider buttons */ + button = &style->slider.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(8.0f,8.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->slider.dec_button = style->slider.inc_button; + + /* progressbar */ + prog = &style->progress; + nk_zero_struct(*prog); + prog->normal = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->hover = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->active = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + prog->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + prog->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + prog->userdata = nk_handle_ptr(0); + prog->padding = nk_vec2(4,4); + prog->rounding = 0; + prog->draw_begin = 0; + prog->draw = 0; + prog->draw_end = 0; + + /* scrollbars */ + scroll = &style->scrollh; + nk_zero_struct(*scroll); + scroll->normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->active = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->cursor_normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR]); + scroll->cursor_hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_HOVER]); + scroll->cursor_active = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE]); + scroll->dec_symbol = NK_SYMBOL_CIRCLE_FILLED; + scroll->inc_symbol = NK_SYMBOL_CIRCLE_FILLED; + scroll->userdata = nk_handle_ptr(0); + scroll->border_color = nk_rgb(65,65,65); + scroll->padding = nk_vec2(4,4); + scroll->show_buttons = nk_false; + scroll->border = 0; + scroll->rounding = 0; + scroll->draw_begin = 0; + scroll->draw = 0; + scroll->draw_end = 0; + style->scrollv = style->scrollh; + + /* scrollbars buttons */ + button = &style->scrollh.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(4.0f,4.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->scrollh.dec_button = style->scrollh.inc_button; + style->scrollv.inc_button = style->scrollh.inc_button; + style->scrollv.dec_button = style->scrollh.inc_button; + + /* edit */ + edit = &style->edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->hover = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->active = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->border_color = table[NK_COLOR_BORDER]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->row_padding = 2; + edit->padding = nk_vec2(4,4); + edit->cursor_size = 4; + edit->border = 1; + edit->rounding = 0; + + /* property */ + property = &style->property; + nk_zero_struct(*property); + property->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->border_color = table[NK_COLOR_BORDER]; + property->label_normal = table[NK_COLOR_TEXT]; + property->label_hover = table[NK_COLOR_TEXT]; + property->label_active = table[NK_COLOR_TEXT]; + property->sym_left = NK_SYMBOL_TRIANGLE_LEFT; + property->sym_right = NK_SYMBOL_TRIANGLE_RIGHT; + property->userdata = nk_handle_ptr(0); + property->padding = nk_vec2(4,4); + property->border = 1; + property->rounding = 10; + property->draw_begin = 0; + property->draw = 0; + property->draw_end = 0; + + /* property buttons */ + button = &style->property.dec_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_PROPERTY]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->property.inc_button = style->property.dec_button; + + /* property edit */ + edit = &style->property.edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->border_color = nk_rgba(0,0,0,0); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->padding = nk_vec2(0,0); + edit->cursor_size = 8; + edit->border = 0; + edit->rounding = 0; + + /* chart */ + chart = &style->line_chart; + nk_zero_struct(*chart); + chart->background = nk_style_item_color(table[NK_COLOR_CHART]); + chart->border_color = table[NK_COLOR_BORDER]; + chart->selected_color = table[NK_COLOR_CHART_COLOR_HIGHLIGHT]; + chart->color = table[NK_COLOR_CHART_COLOR]; + chart->border = 0; + chart->rounding = 0; + chart->padding = nk_vec2(4,4); + style->column_chart = *chart; + + /* combo */ + combo = &style->combo; + combo->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->active = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->border_color = table[NK_COLOR_BORDER]; + combo->label_normal = table[NK_COLOR_TEXT]; + combo->label_hover = table[NK_COLOR_TEXT]; + combo->label_active = table[NK_COLOR_TEXT]; + combo->sym_normal = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_hover = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_active =NK_SYMBOL_TRIANGLE_DOWN; + combo->content_padding = nk_vec2(4,4); + combo->button_padding = nk_vec2(0,4); + combo->spacing = nk_vec2(4,0); + combo->border = 1; + combo->rounding = 0; + + /* combo button */ + button = &style->combo.button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + button->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + button->active = nk_style_item_color(table[NK_COLOR_COMBO]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_COMBO]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* tab */ + tab = &style->tab; + tab->background = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + tab->border_color = table[NK_COLOR_BORDER]; + tab->text = table[NK_COLOR_TEXT]; + tab->sym_minimize = NK_SYMBOL_TRIANGLE_DOWN; + tab->sym_maximize = NK_SYMBOL_TRIANGLE_RIGHT; + tab->border = 1; + tab->rounding = 0; + tab->padding = nk_vec2(4,4); + tab->spacing = nk_vec2(4,4); + + /* tab button */ + button = &style->tab.tab_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* node button */ + button = &style->tab.node_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window header */ + win = &style->window; + win->header.align = NK_HEADER_RIGHT; + win->header.close_symbol = NK_SYMBOL_X; + win->header.minimize_symbol = NK_SYMBOL_MINUS; + win->header.maximize_symbol = NK_SYMBOL_PLUS; + win->header.normal = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.hover = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.active = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.label_normal = table[NK_COLOR_TEXT]; + win->header.label_hover = table[NK_COLOR_TEXT]; + win->header.label_active = table[NK_COLOR_TEXT]; + win->header.label_padding = nk_vec2(4,4); + win->header.padding = nk_vec2(4,4); + win->header.spacing = nk_vec2(0,0); + + /* window header close button */ + button = &style->window.header.close_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window header minimize button */ + button = &style->window.header.minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window */ + win->background = table[NK_COLOR_WINDOW]; + win->fixed_background = nk_style_item_color(table[NK_COLOR_WINDOW]); + win->border_color = table[NK_COLOR_BORDER]; + win->combo_border_color = table[NK_COLOR_BORDER]; + win->contextual_border_color = table[NK_COLOR_BORDER]; + win->menu_border_color = table[NK_COLOR_BORDER]; + win->group_border_color = table[NK_COLOR_BORDER]; + win->tooltip_border_color = table[NK_COLOR_BORDER]; + win->scaler = nk_style_item_color(table[NK_COLOR_TEXT]); + win->footer_padding = nk_vec2(4,4); + win->rounding = 0.0f; + win->scaler_size = nk_vec2(16,16); + win->padding = nk_vec2(8,8); + win->spacing = nk_vec2(4,4); + win->scrollbar_size = nk_vec2(10,10); + win->min_size = nk_vec2(64,64); + win->combo_border = 1.0f; + win->contextual_border = 1.0f; + win->menu_border = 1.0f; + win->group_border = 1.0f; + win->tooltip_border = 1.0f; + win->border = 2.0f; +} + +NK_API void +nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + style->font = *font; +} + +/* =============================================================== + * + * POOL + * + * ===============================================================*/ +struct nk_table { + unsigned int seq; + nk_hash keys[NK_VALUE_PAGE_CAPACITY]; + nk_uint values[NK_VALUE_PAGE_CAPACITY]; + struct nk_table *next, *prev; +}; + +union nk_page_data { + struct nk_table tbl; + struct nk_window win; +}; + +struct nk_window_page { + unsigned size; + struct nk_window_page *next; + union nk_page_data win[1]; +}; + +struct nk_pool { + struct nk_allocator alloc; + enum nk_allocation_type type; + unsigned int page_count; + struct nk_window_page *pages; + unsigned capacity; + nk_size size; + nk_size cap; +}; + +NK_INTERN void +nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, + unsigned int capacity) +{ + nk_zero(pool, sizeof(*pool)); + pool->alloc = *alloc; + pool->capacity = capacity; + pool->pages = 0; + pool->type = NK_BUFFER_DYNAMIC; +} + +NK_INTERN void +nk_pool_free(struct nk_pool *pool) +{ + struct nk_window_page *next; + struct nk_window_page *iter = pool->pages; + if (!pool) return; + if (pool->type == NK_BUFFER_FIXED) return; + while (iter) { + next = iter->next; + pool->alloc.free(pool->alloc.userdata, iter); + iter = next; + } + pool->alloc.free(pool->alloc.userdata, pool); +} + +NK_INTERN void +nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size) +{ + nk_zero(pool, sizeof(*pool)); + /* make sure pages have correct granularity to at least fit one page into memory */ + if (size < sizeof(struct nk_window_page) + NK_POOL_DEFAULT_CAPACITY * sizeof(struct nk_window)) + pool->capacity = (unsigned)(size - sizeof(struct nk_window_page)) / sizeof(struct nk_window); + else pool->capacity = NK_POOL_DEFAULT_CAPACITY; + pool->pages = (struct nk_window_page*)memory; + pool->type = NK_BUFFER_FIXED; + pool->size = size; +} + +NK_INTERN void* +nk_pool_alloc(struct nk_pool *pool) +{ + if (!pool->pages || pool->pages->size >= pool->capacity) { + /* allocate new page */ + struct nk_window_page *page; + if (pool->type == NK_BUFFER_FIXED) { + if (!pool->pages) { + NK_ASSERT(pool->pages); + return 0; + } + NK_ASSERT(pool->pages->size < pool->capacity); + return 0; + } else { + nk_size size = sizeof(struct nk_window_page); + size += NK_POOL_DEFAULT_CAPACITY * sizeof(union nk_page_data); + page = (struct nk_window_page*)pool->alloc.alloc(pool->alloc.userdata,0, size); + page->size = 0; + page->next = pool->pages; + pool->pages = page; + } + } + return &pool->pages->win[pool->pages->size++]; +} + +/* =============================================================== + * + * CONTEXT + * + * ===============================================================*/ +NK_INTERN void nk_remove_window(struct nk_context*, struct nk_window*); +NK_INTERN void* nk_create_window(struct nk_context *ctx); +NK_INTERN void nk_free_window(struct nk_context *ctx, struct nk_window *win); +NK_INTERN void nk_free_table(struct nk_context *ctx, struct nk_table *tbl); +NK_INTERN void nk_remove_table(struct nk_window *win, struct nk_table *tbl); + +NK_INTERN void +nk_setup(struct nk_context *ctx, const struct nk_user_font *font) +{ + NK_ASSERT(ctx); + if (!ctx) return; + + nk_zero_struct(*ctx); + nk_style_default(ctx); + if (font) ctx->style.font = *font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_draw_list_init(&ctx->draw_list); +#endif +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API int +nk_init_default(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + return nk_init(ctx, &alloc, font); +} +#endif + +NK_API int +nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, + const struct nk_user_font *font) +{ + NK_ASSERT(memory); + if (!memory) return 0; + nk_setup(ctx, font); + nk_buffer_init_fixed(&ctx->memory, memory, size); + ctx->pool = 0; + return 1; +} + +NK_API int +nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *pool, const struct nk_user_font *font) +{ + NK_ASSERT(cmds); + NK_ASSERT(pool); + if (!cmds || !pool) return 0; + nk_setup(ctx, font); + ctx->memory = *cmds; + if (pool->type == NK_BUFFER_FIXED) { + /* take memory from buffer and alloc fixed pool */ + void *memory = pool->memory.ptr; + nk_size size = pool->memory.size; + ctx->pool = memory; + NK_ASSERT(size > sizeof(struct nk_pool)); + size -= sizeof(struct nk_pool); + nk_pool_init_fixed((struct nk_pool*)ctx->pool, + (void*)((nk_byte*)ctx->pool+sizeof(struct nk_pool)), size); + } else { + /* create dynamic pool from buffer allocator */ + struct nk_allocator *alloc = &pool->pool; + ctx->pool = alloc->alloc(alloc->userdata,0, sizeof(struct nk_pool)); + nk_pool_init((struct nk_pool*)ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + } + return 1; +} + +NK_API int +nk_init(struct nk_context *ctx, struct nk_allocator *alloc, + const struct nk_user_font *font) +{ + NK_ASSERT(alloc); + if (!alloc) return 0; + nk_setup(ctx, font); + nk_buffer_init(&ctx->memory, alloc, NK_DEFAULT_COMMAND_BUFFER_SIZE); + ctx->pool = alloc->alloc(alloc->userdata,0, sizeof(struct nk_pool)); + nk_pool_init((struct nk_pool*)ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + return 1; +} + +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_set_user_data(struct nk_context *ctx, nk_handle handle) +{ + if (!ctx) return; + ctx->userdata = handle; + if (ctx->current) + ctx->current->buffer.userdata = handle; +} +#endif + +NK_API void +nk_free(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_buffer_free(&ctx->memory); + if (ctx->pool) nk_pool_free((struct nk_pool*)ctx->pool); + + nk_zero(&ctx->input, sizeof(ctx->input)); + nk_zero(&ctx->style, sizeof(ctx->style)); + nk_zero(&ctx->memory, sizeof(ctx->memory)); + + ctx->seq = 0; + ctx->pool = 0; + ctx->build = 0; + ctx->begin = 0; + ctx->end = 0; + ctx->active = 0; + ctx->current = 0; + ctx->freelist = 0; + ctx->count = 0; +} + +NK_API void +nk_clear(struct nk_context *ctx) +{ + struct nk_window *iter; + struct nk_window *next; + NK_ASSERT(ctx); + + if (!ctx) return; + if (ctx->pool) + nk_buffer_clear(&ctx->memory); + else nk_buffer_reset(&ctx->memory, NK_BUFFER_FRONT); + + ctx->build = 0; + ctx->memory.calls = 0; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_draw_list_clear(&ctx->draw_list); +#endif + + /* garbage collector */ + iter = ctx->begin; + while (iter) { + /* make sure minimized windows do not get removed */ + if (iter->flags & NK_WINDOW_MINIMIZED) { + iter = iter->next; + continue; + } + + /* free unused popup windows */ + if (iter->popup.win && iter->popup.win->seq != ctx->seq) { + nk_free_window(ctx, iter->popup.win); + iter->popup.win = 0; + } + + {struct nk_table *n, *it = iter->tables; + while (it) { + /* remove unused window state tables */ + n = it->next; + if (it->seq != ctx->seq) { + nk_remove_table(iter, it); + nk_zero(it, sizeof(union nk_page_data)); + nk_free_table(ctx, it); + if (it == iter->tables) + iter->tables = n; + } + it = n; + }} + + /* window itself is not used anymore so free */ + if (iter->seq != ctx->seq || iter->flags & NK_WINDOW_HIDDEN) { + next = iter->next; + nk_remove_window(ctx, iter); + nk_free_window(ctx, iter); + iter = next; + } else iter = iter->next; + } + ctx->seq++; +} + +/* ---------------------------------------------------------------- + * + * BUFFERING + * + * ---------------------------------------------------------------*/ +NK_INTERN void +nk_start(struct nk_context *ctx, struct nk_window *win) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + win->buffer.begin = ctx->memory.allocated; + win->buffer.end = win->buffer.begin; + win->buffer.last = win->buffer.begin; + win->buffer.clip = nk_null_rect; +} + +NK_INTERN void +nk_start_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_panel *iter; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + /* make sure to use the correct popup buffer*/ + iter = win->layout; + while (iter->parent) + iter = iter->parent; + + /* save buffer fill state for popup */ + buf = &iter->popup_buffer; + buf->begin = win->buffer.end; + buf->end = win->buffer.end; + buf->parent = win->buffer.last; + buf->last = buf->begin; + buf->active = nk_true; +} + +NK_INTERN void +nk_finish_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_panel *iter; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + /* make sure to use the correct popup buffer*/ + iter = win->layout; + while (iter->parent) + iter = iter->parent; + + buf = &iter->popup_buffer; + buf->last = win->buffer.last; + buf->end = win->buffer.end; +} + +NK_INTERN void +nk_finish(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_command *parent_last; + struct nk_command *sublast; + struct nk_command *last; + void *memory; + + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + win->buffer.end = ctx->memory.allocated; + if (!win->layout->popup_buffer.active) return; + + /* frome here this case is for popup windows */ + buf = &win->layout->popup_buffer; + memory = ctx->memory.memory.ptr; + + /* redirect the sub-window buffer to the end of the window command buffer */ + parent_last = nk_ptr_add(struct nk_command, memory, buf->parent); + sublast = nk_ptr_add(struct nk_command, memory, buf->last); + last = nk_ptr_add(struct nk_command, memory, win->buffer.last); + + parent_last->next = buf->end; + sublast->next = last->next; + last->next = buf->begin; + win->buffer.last = buf->last; + win->buffer.end = buf->end; + buf->active = nk_false; +} + +NK_INTERN void +nk_build(struct nk_context *ctx) +{ + struct nk_window *iter; + struct nk_window *next; + struct nk_command *cmd; + nk_byte *buffer; + + iter = ctx->begin; + buffer = (nk_byte*)ctx->memory.memory.ptr; + while (iter != 0) { + next = iter->next; + if (iter->buffer.last == iter->buffer.begin || (iter->flags & NK_WINDOW_HIDDEN)) { + iter = next; + continue; + } + cmd = nk_ptr_add(struct nk_command, buffer, iter->buffer.last); + while (next && ((next->buffer.last == next->buffer.begin) || + (next->flags & NK_WINDOW_HIDDEN))) + next = next->next; /* skip empty command buffers */ + + if (next) { + cmd->next = next->buffer.begin; + } else cmd->next = ctx->memory.allocated; + iter = next; + } +} + +NK_API const struct nk_command* +nk__begin(struct nk_context *ctx) +{ + struct nk_window *iter; + nk_byte *buffer; + NK_ASSERT(ctx); + if (!ctx) return 0; + if (!ctx->count) return 0; + + /* build one command list out of all windows */ + buffer = (nk_byte*)ctx->memory.memory.ptr; + if (!ctx->build) { + nk_build(ctx); + ctx->build = nk_true; + } + + iter = ctx->begin; + while (iter && ((iter->buffer.begin == iter->buffer.end) || (iter->flags & NK_WINDOW_HIDDEN))) + iter = iter->next; + if (!iter) return 0; + return nk_ptr_add_const(struct nk_command, buffer, iter->buffer.begin); +} + +NK_API const struct nk_command* +nk__next(struct nk_context *ctx, const struct nk_command *cmd) +{ + nk_byte *buffer; + const struct nk_command *next; + NK_ASSERT(ctx); + if (!ctx || !cmd || !ctx->count) return 0; + if (cmd->next >= ctx->memory.allocated) return 0; + buffer = (nk_byte*)ctx->memory.memory.ptr; + next = nk_ptr_add_const(struct nk_command, buffer, cmd->next); + return next; +} + +/* ---------------------------------------------------------------- + * + * TABLES + * + * ---------------------------------------------------------------*/ +NK_INTERN struct nk_table* +nk_create_table(struct nk_context *ctx) +{ + void *tbl = (void*)nk_create_window(ctx); + nk_zero(tbl, sizeof(struct nk_table)); + return (struct nk_table*)tbl; +} + +NK_INTERN void +nk_free_table(struct nk_context *ctx, struct nk_table *tbl) +{nk_free_window(ctx, (struct nk_window*)tbl);} + +NK_INTERN void +nk_push_table(struct nk_window *win, struct nk_table *tbl) +{ + if (!win->tables) { + win->tables = tbl; + tbl->next = 0; + tbl->prev = 0; + win->table_count = 1; + win->table_size = 1; + return; + } + win->tables->prev = tbl; + tbl->next = win->tables; + tbl->prev = 0; + win->tables = tbl; + win->table_count++; + win->table_size = 0; +} + +NK_INTERN void +nk_remove_table(struct nk_window *win, struct nk_table *tbl) +{ + if (win->tables == tbl) + win->tables = tbl->next; + if (tbl->next) + tbl->next->prev = tbl->prev; + if (tbl->prev) + tbl->prev->next = tbl->next; + tbl->next = 0; + tbl->prev = 0; +} + +NK_INTERN nk_uint* +nk_add_value(struct nk_context *ctx, struct nk_window *win, + nk_hash name, nk_uint value) +{ + if (!win->tables || win->table_size == NK_VALUE_PAGE_CAPACITY) { + struct nk_table *tbl = nk_create_table(ctx); + nk_push_table(win, tbl); + } + win->tables->seq = win->seq; + win->tables->keys[win->table_size] = name; + win->tables->values[win->table_size] = value; + return &win->tables->values[win->table_size++]; +} + +NK_INTERN nk_uint* +nk_find_value(struct nk_window *win, nk_hash name) +{ + unsigned short size = win->table_size; + struct nk_table *iter = win->tables; + while (iter) { + unsigned short i = 0; + for (i = 0; i < size; ++i) { + if (iter->keys[i] == name) { + iter->seq = win->seq; + return &iter->values[i]; + } + } + size = NK_VALUE_PAGE_CAPACITY; + iter = iter->next; + } + return 0; +} + +/* ---------------------------------------------------------------- + * + * WINDOW + * + * ---------------------------------------------------------------*/ +NK_INTERN int nk_panel_begin(struct nk_context *ctx, const char *title); +NK_INTERN void nk_panel_end(struct nk_context *ctx); + +NK_INTERN void* +nk_create_window(struct nk_context *ctx) +{ + struct nk_window *win = 0; + if (ctx->freelist) { + /* unlink window from free list */ + win = ctx->freelist; + ctx->freelist = win->next; + } else if (ctx->pool) { + /* allocate window from memory pool */ + win = (struct nk_window*) nk_pool_alloc((struct nk_pool*)ctx->pool); + NK_ASSERT(win); + if (!win) return 0; + } else { + /* allocate new window from the back of the fixed size memory buffer */ + NK_STORAGE const nk_size size = sizeof(union nk_page_data); + NK_STORAGE const nk_size align = NK_ALIGNOF(union nk_page_data); + win = (struct nk_window*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align); + NK_ASSERT(win); + if (!win) return 0; + } + nk_zero(win, sizeof(*win)); + win->next = 0; + win->prev = 0; + win->seq = ctx->seq; + return win; +} + +NK_INTERN void +nk_free_window(struct nk_context *ctx, struct nk_window *win) +{ + /* unlink windows from list */ + struct nk_table *n, *it = win->tables; + if (win->popup.win) { + nk_free_window(ctx, win->popup.win); + win->popup.win = 0; + } + + win->next = 0; + win->prev = 0; + + while (it) { + /*free window state tables */ + n = it->next; + if (it->seq != ctx->seq) { + nk_remove_table(win, it); + nk_free_table(ctx, it); + if (it == win->tables) + win->tables = n; + } + it = n; + } + + /* link windows into freelist */ + if (!ctx->freelist) { + ctx->freelist = win; + } else { + win->next = ctx->freelist; + ctx->freelist = win; + } +} + +NK_INTERN struct nk_window* +nk_find_window(struct nk_context *ctx, nk_hash hash) +{ + struct nk_window *iter; + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + if (iter->name == hash) + return iter; + iter = iter->next; + } + return 0; +} + +NK_INTERN void +nk_insert_window(struct nk_context *ctx, struct nk_window *win) +{ + const struct nk_window *iter; + struct nk_window *end; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return; + + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + NK_ASSERT(iter != win); + if (iter == win) return; + iter = iter->next; + } + + if (!ctx->begin) { + win->next = 0; + win->prev = 0; + ctx->begin = win; + ctx->end = win; + ctx->count = 1; + return; + } + + end = ctx->end; + end->flags |= NK_WINDOW_ROM; + end->next = win; + win->prev = ctx->end; + win->next = 0; + ctx->end = win; + ctx->count++; + + ctx->active = ctx->end; + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; +} + +NK_INTERN void +nk_remove_window(struct nk_context *ctx, struct nk_window *win) +{ + if (win == ctx->begin || win == ctx->end) { + if (win == ctx->begin) { + ctx->begin = win->next; + if (win->next) + win->next->prev = 0; + } + if (win == ctx->end) { + ctx->end = win->prev; + if (win->prev) + win->prev->next = 0; + } + } else { + if (win->next) + win->next->prev = win->prev; + if (win->prev) + win->prev->next = win->next; + } + if (win == ctx->active || !ctx->active) { + ctx->active = ctx->end; + if (ctx->end) + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; + } + win->next = 0; + win->prev = 0; + ctx->count--; +} + +NK_API int +nk_begin(struct nk_context *ctx, struct nk_panel *layout, const char *title, + struct nk_rect bounds, nk_flags flags) +{ + struct nk_window *win; + struct nk_style *style; + nk_hash title_hash; + int title_len; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(!ctx->current && "if this triggers you missed a `nk_end` call"); + if (!ctx || ctx->current || !title) + return 0; + + /* find or create window */ + style = &ctx->style; + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash); + if (!win) { + win = (struct nk_window*)nk_create_window(ctx); + nk_insert_window(ctx, win); + nk_command_buffer_init(&win->buffer, &ctx->memory, NK_CLIPPING_ON); + NK_ASSERT(win); + if (!win) return 0; + + win->flags = flags; + win->bounds = bounds; + win->name = title_hash; + win->popup.win = 0; + if (!ctx->active) + ctx->active = win; + } else { + /* update public window flags */ + win->flags &= ~(nk_flags)(NK_WINDOW_PRIVATE-1); + win->flags |= flags; + win->seq++; + if (!ctx->active) + ctx->active = win; + } + if (win->flags & NK_WINDOW_HIDDEN) { + ctx->current = win; + return 0; + } + + /* overlapping window */ + if (!(win->flags & NK_WINDOW_SUB) && !(win->flags & NK_WINDOW_HIDDEN)) + { + int inpanel, ishovered; + const struct nk_window *iter = win; + + /* This is so terrible but neccessary for minimized windows. The difference + * lies in the size of the window. But it is not possible to get the size + * without cheating because you do not have the information at this point. + * Even worse this is wrong since windows could have different window heights. + * I leave it in for now since I otherwise loose my mind. */ + float h = ctx->style.font.height + 2 * style->window.header.padding.y; + + /* activate window if hovered and no other window is overlapping this window */ + nk_start(ctx, win); + inpanel = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_LEFT, win->bounds); + ishovered = nk_input_is_mouse_hovering_rect(&ctx->input, win->bounds); + if ((win != ctx->active) && ishovered) { + iter = win->next; + while (iter) { + if (!(iter->flags & NK_WINDOW_MINIMIZED)) { + if (NK_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, + iter->bounds.x, iter->bounds.y, iter->bounds.w, iter->bounds.h) && + !(iter->flags & NK_WINDOW_HIDDEN)) + break; + } else { + if (NK_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, + iter->bounds.x, iter->bounds.y, iter->bounds.w, h) && + !(iter->flags & NK_WINDOW_HIDDEN)) + break; + } + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + + /* activate window if clicked */ + if (iter && inpanel && (win != ctx->end)) { + iter = win->next; + while (iter) { + /* try to find a panel with higher priorty in the same position */ + if (!(iter->flags & NK_WINDOW_MINIMIZED)) { + if (NK_INBOX(ctx->input.mouse.prev.x, ctx->input.mouse.prev.y, iter->bounds.x, + iter->bounds.y, iter->bounds.w, iter->bounds.h) && + !(iter->flags & NK_WINDOW_HIDDEN)) + break; + } else { + if (NK_INBOX(ctx->input.mouse.prev.x, ctx->input.mouse.prev.y, iter->bounds.x, + iter->bounds.y, iter->bounds.w, h) && + !(iter->flags & NK_WINDOW_HIDDEN)) + break; + } + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + + if (!iter && ctx->end != win) { + /* current window is active in that position so transfer to top + * at the highest priority in stack */ + nk_remove_window(ctx, win); + nk_insert_window(ctx, win); + + win->flags &= ~(nk_flags)NK_WINDOW_ROM; + ctx->active = win; + } + if (ctx->end != win) + win->flags |= NK_WINDOW_ROM; + } + + win->layout = layout; + ctx->current = win; + ret = nk_panel_begin(ctx, title); + layout->offset = &win->scrollbar; + return ret; +} + +NK_API void +nk_end(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return; + if (ctx->current->flags & NK_WINDOW_HIDDEN) { + ctx->current = 0; + return; + } + nk_panel_end(ctx); + ctx->current = 0; + ctx->last_widget_state = 0; +} + +NK_API struct nk_rect +nk_window_get_bounds(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->bounds; +} + +NK_API struct nk_vec2 +nk_window_get_position(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y); +} + +NK_API struct nk_vec2 +nk_window_get_size(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h); +} + +NK_API float +nk_window_get_width(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.w; +} + +NK_API float +nk_window_get_height(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.h; +} + +NK_API struct nk_rect +nk_window_get_content_region(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->layout->clip; +} + +NK_API struct nk_vec2 +nk_window_get_content_region_min(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y); +} + +NK_API struct nk_vec2 +nk_window_get_content_region_max(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w, + ctx->current->layout->clip.y + ctx->current->layout->clip.h); +} + +NK_API struct nk_vec2 +nk_window_get_content_region_size(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h); +} + +NK_API struct nk_command_buffer* +nk_window_get_canvas(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return &ctx->current->buffer; +} + +NK_API struct nk_panel* +nk_window_get_panel(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->layout; +} + +NK_API int +nk_window_has_focus(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return ctx->current == ctx->active; +} + +NK_API int +nk_window_is_hovered(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return nk_input_is_mouse_hovering_rect(&ctx->input, ctx->current->bounds); +} + +NK_API int +nk_window_is_any_hovered(struct nk_context *ctx) +{ + struct nk_window *iter; + NK_ASSERT(ctx); + if (!ctx) return 0; + iter = ctx->begin; + while (iter) { + if (nk_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) + return 1; + iter = iter->next; + } + return 0; +} + +NK_API int +nk_window_is_collapsed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash); + if (!win) return 0; + return win->flags & NK_WINDOW_MINIMIZED; +} + +NK_API int +nk_window_is_closed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 1; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash); + if (!win) return 1; + return (win->flags & NK_WINDOW_HIDDEN); +} + +NK_API int +nk_window_is_active(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash); + if (!win) return 0; + return win == ctx->active; +} + +NK_API struct nk_window* +nk_window_find(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + return nk_find_window(ctx, title_hash); +} + +NK_API void +nk_window_close(struct nk_context *ctx, const char *name) +{ + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + win = nk_window_find(ctx, name); + if (!win) return; + NK_ASSERT(ctx->current != win && "You cannot close a currently active window"); + if (ctx->current == win) return; + win->flags |= NK_WINDOW_HIDDEN; +} + +NK_API void +nk_window_set_bounds(struct nk_context *ctx, struct nk_rect bounds) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds = bounds; +} + +NK_API void +nk_window_set_position(struct nk_context *ctx, struct nk_vec2 pos) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds.x = pos.x; + ctx->current->bounds.y = pos.y; +} + +NK_API void +nk_window_set_size(struct nk_context *ctx, struct nk_vec2 size) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds.w = size.x; + ctx->current->bounds.h = size.y; +} + +NK_API void +nk_window_collapse(struct nk_context *ctx, const char *name, + enum nk_collapse_states c) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash); + if (!win) return; + if (c == NK_MINIMIZED) + win->flags |= NK_WINDOW_MINIMIZED; + else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED; +} + +NK_API void +nk_window_collapse_if(struct nk_context *ctx, const char *name, + enum nk_collapse_states c, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_collapse(ctx, name, c); +} + +NK_API void +nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash); + if (!win) return; + if (s == NK_HIDDEN) + win->flags |= NK_WINDOW_HIDDEN; + else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; +} + +NK_API void +nk_window_show_if(struct nk_context *ctx, const char *name, + enum nk_show_states s, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_show(ctx, name, s); +} + +NK_API void +nk_window_set_focus(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash); + if (win && ctx->end != win) { + nk_remove_window(ctx, win); + nk_insert_window(ctx, win); + } + ctx->active = win; +} + +/*---------------------------------------------------------------- + * + * PANEL + * + * --------------------------------------------------------------*/ +NK_INTERN int +nk_panel_begin(struct nk_context *ctx, const char *title) +{ + struct nk_input *in; + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + const struct nk_style *style; + const struct nk_user_font *font; + + int header_active = 0; + struct nk_vec2 scrollbar_size; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + struct nk_vec2 scaler_size; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + style = &ctx->style; + font = &style->font; + in = &ctx->input; + win = ctx->current; + layout = win->layout; + + /* cache style data */ + scrollbar_size = style->window.scrollbar_size; + window_padding = style->window.padding; + item_spacing = style->window.spacing; + scaler_size = style->window.scaler_size; + + /* check arguments */ + nk_zero(layout, sizeof(*layout)); + if (win->flags & NK_WINDOW_HIDDEN) + return 0; + + /* move panel position if requested */ + layout->header_h = font->height + 2 * style->window.header.padding.y; + layout->header_h += 2 * style->window.header.label_padding.y; + if ((win->flags & NK_WINDOW_MOVABLE) && !(win->flags & NK_WINDOW_ROM)) { + int incursor; + struct nk_rect move; + move.x = win->bounds.x; + move.y = win->bounds.y; + move.w = win->bounds.w; + move.h = layout->header_h; + incursor = nk_input_is_mouse_prev_hovering_rect(in, move); + if (nk_input_is_mouse_down(in, NK_BUTTON_LEFT) && incursor) { + win->bounds.x = win->bounds.x + in->mouse.delta.x; + win->bounds.y = win->bounds.y + in->mouse.delta.y; + } + } + +#ifdef NK_INCLUDE_COMMAND_USERDATA + win->buffer.userdata = ctx->userdata; +#endif + + /* panel space with border */ + if (win->flags & NK_WINDOW_BORDER) { + if (!(win->flags & NK_WINDOW_SUB)) + layout->bounds = nk_shrink_rect(win->bounds, style->window.border); + else if (win->flags & NK_WINDOW_COMBO) + layout->bounds = nk_shrink_rect(win->bounds, style->window.combo_border); + else if (win->flags & NK_WINDOW_CONTEXTUAL) + layout->bounds = nk_shrink_rect(win->bounds, style->window.contextual_border); + else if (win->flags & NK_WINDOW_MENU) + layout->bounds = nk_shrink_rect(win->bounds, style->window.menu_border); + else if (win->flags & NK_WINDOW_GROUP) + layout->bounds = nk_shrink_rect(win->bounds, style->window.group_border); + else if (win->flags & NK_WINDOW_TOOLTIP) + layout->bounds = nk_shrink_rect(win->bounds, style->window.tooltip_border); + else layout->bounds = nk_shrink_rect(win->bounds, style->window.border); + } else layout->bounds = win->bounds; + + /* setup panel */ + layout->border = layout->bounds.x - win->bounds.x; + layout->at_x = layout->bounds.x; + layout->at_y = layout->bounds.y; + layout->width = layout->bounds.w; + layout->height = layout->bounds.h; + layout->max_x = 0; + layout->row.index = 0; + layout->row.columns = 0; + layout->row.height = 0; + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.tree_depth = 0; + layout->flags = win->flags; + out = &win->buffer; + + /* calculate window header */ + if (win->flags & NK_WINDOW_MINIMIZED) { + layout->header_h = 0; + layout->row.height = 0; + } else { + layout->header_h = 0; + layout->row.height = item_spacing.y; + } + + /* calculate window footer height */ + if (!(win->flags & NK_WINDOW_NONBLOCK) && + (!(win->flags & NK_WINDOW_NO_SCROLLBAR) || (win->flags & NK_WINDOW_SCALABLE))) + layout->footer_h = scaler_size.y + style->window.footer_padding.y; + else layout->footer_h = 0; + + /* calculate the window size */ + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR)) + layout->width = layout->bounds.w - scrollbar_size.x; + layout->height = layout->bounds.h - (layout->header_h + 2 * item_spacing.y); + layout->height -= layout->footer_h; + + /* window header state */ + header_active = (win->flags & (NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE)); + header_active = header_active || (win->flags & NK_WINDOW_TITLE); + header_active = header_active && !(win->flags & NK_WINDOW_HIDDEN) && title; + + /* window header */ + if (header_active) + { + struct nk_rect header; + struct nk_rect button; + struct nk_text text; + const struct nk_style_item *background; + + /* calculate header bounds */ + header.x = layout->bounds.x; + header.y = layout->bounds.y; + header.w = layout->bounds.w; + + /* calculate correct header height */ + layout->header_h = font->height + 2.0f * style->window.header.padding.y; + layout->header_h += 2.0f * style->window.header.label_padding.y; + layout->row.height += layout->header_h + style->window.padding.y; + header.h = layout->header_h + 0.5f; + + /* update window height */ + layout->height = layout->bounds.h - (header.h + 2 * item_spacing.y); + layout->height -= layout->footer_h; + + /* select correct header background and text color */ + if (ctx->active == win) { + background = &style->window.header.active; + text.text = style->window.header.label_active; + } else if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) { + background = &style->window.header.hover; + text.text = style->window.header.label_hover; + } else { + background = &style->window.header.normal; + text.text = style->window.header.label_normal; + } + + /* draw header background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image); + } else { + text.background = background->data.color; + nk_fill_rect(out, nk_rect(layout->bounds.x, layout->bounds.y, + layout->bounds.w, layout->header_h), 0, background->data.color); + } + + /* window close button */ + button.y = header.y + style->window.header.padding.y; + button.h = layout->header_h - 2 * style->window.header.padding.y; + button.w = button.h; + if (win->flags & NK_WINDOW_CLOSABLE) { + nk_flags ws; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - (button.w + style->window.header.padding.x); + header.w -= button.w + style->window.header.spacing.x + style->window.header.padding.x; + } else { + button.x = header.x + style->window.header.padding.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + if (nk_do_button_symbol(&ws, &win->buffer, button, + style->window.header.close_symbol, NK_BUTTON_DEFAULT, + &style->window.header.close_button, in, &style->font)) + layout->flags |= NK_WINDOW_HIDDEN; + } + + /* window minimize button */ + if (win->flags & NK_WINDOW_MINIMIZABLE) { + nk_flags ws; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - button.w; + if (!(win->flags & NK_WINDOW_CLOSABLE)) { + button.x -= style->window.header.padding.x; + header.w -= style->window.header.padding.x; + } + header.w -= button.w + style->window.header.spacing.x; + } else { + button.x = header.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + if (nk_do_button_symbol(&ws, &win->buffer, button, + (layout->flags & NK_WINDOW_MINIMIZED)? + style->window.header.maximize_symbol: + style->window.header.minimize_symbol, + NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, &style->font)) + layout->flags = (layout->flags & NK_WINDOW_MINIMIZED) ? + layout->flags & (nk_flags)~NK_WINDOW_MINIMIZED: + layout->flags | NK_WINDOW_MINIMIZED; + } + { + /* window header title */ + int text_len = nk_strlen(title); + struct nk_rect label = {0,0,0,0}; + float t = font->width(font->userdata, font->height, title, text_len); + + label.x = header.x + style->window.header.padding.x; + label.x += style->window.header.label_padding.x; + label.y = header.y + style->window.header.label_padding.y; + label.h = font->height + 2 * style->window.header.label_padding.y; + label.w = t + 2 * style->window.header.spacing.x; + text.padding = nk_vec2(0,0); + nk_widget_text(out, label,(const char*)title, text_len, &text, + NK_TEXT_LEFT, font); + } + } + + /* fix header height for transistion between minimized and maximized window state */ + if (win->flags & NK_WINDOW_MINIMIZED && !(layout->flags & NK_WINDOW_MINIMIZED)) + layout->row.height += 2 * item_spacing.y + style->window.border; + + if (layout->flags & NK_WINDOW_MINIMIZED) { + /* draw window background if minimized */ + layout->row.height = 0; + nk_fill_rect(out, nk_rect(layout->bounds.x, layout->bounds.y, + layout->bounds.w, layout->row.height), 0, style->window.background); + } else if (!(layout->flags & NK_WINDOW_DYNAMIC)) { + /* draw fixed window body */ + struct nk_rect body = layout->bounds; + if (header_active) { + body.y += layout->header_h - 0.5f; + body.h -= layout->header_h; + } + if (style->window.fixed_background.type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, body, &style->window.fixed_background.data.image); + else nk_fill_rect(out, body, 0, style->window.fixed_background.data.color); + } else { + /* draw dynamic window body */ + nk_fill_rect(out, nk_rect(layout->bounds.x, layout->bounds.y, + layout->bounds.w, layout->row.height + window_padding.y), 0, + style->window.background); + } + + /* draw top window border line */ + if (layout->flags & NK_WINDOW_BORDER) { + struct nk_color border; + + /* select correct border color */ + if (!(win->flags & NK_WINDOW_SUB)) + border = style->window.border_color; + else if (win->flags & NK_WINDOW_COMBO) + border = style->window.combo_border_color; + else if (win->flags & NK_WINDOW_CONTEXTUAL) + border = style->window.contextual_border_color; + else if (win->flags & NK_WINDOW_MENU) + border = style->window.menu_border_color; + else if (win->flags & NK_WINDOW_GROUP) + border = style->window.group_border_color; + else if (win->flags & NK_WINDOW_TOOLTIP) + border = style->window.tooltip_border_color; + else border = style->window.border_color; + + /* draw border */ + nk_stroke_line(out, + win->bounds.x + layout->border/2.0f, + win->bounds.y + layout->border/2.0f, + win->bounds.x + win->bounds.w - layout->border, + win->bounds.y + layout->border/2.0f, + style->window.border, border); + } + { + /* calculate and set the window clipping rectangle*/ + struct nk_rect clip; + if (!(win->flags & NK_WINDOW_DYNAMIC)) { + layout->clip.x = layout->bounds.x + window_padding.x; + layout->clip.w = layout->width - 2 * window_padding.x; + } else { + layout->clip.x = layout->bounds.x; + layout->clip.w = layout->width; + } + + layout->clip.h = layout->bounds.h - (layout->footer_h + layout->header_h); + layout->clip.h -= (2.0f * window_padding.y); + layout->clip.y = layout->bounds.y; + + /* combo box and menu do not have header space */ + if (!(win->flags & NK_WINDOW_COMBO) && !(win->flags & NK_WINDOW_MENU)) + layout->clip.y += layout->header_h; + + nk_unify(&clip, &win->buffer.clip, layout->clip.x, layout->clip.y, + layout->clip.x + layout->clip.w, layout->clip.y + layout->clip.h); + nk_push_scissor(out, clip); + layout->clip = clip; + + win->buffer.clip.x = layout->bounds.x; + win->buffer.clip.w = layout->width; + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR)) + win->buffer.clip.w += scrollbar_size.x; + } + return !(layout->flags & NK_WINDOW_HIDDEN) && !(layout->flags & NK_WINDOW_MINIMIZED); +} + +NK_INTERN void +nk_panel_end(struct nk_context *ctx) +{ + struct nk_input *in; + struct nk_window *window; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 scrollbar_size; + struct nk_vec2 scaler_size; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + struct nk_rect footer = {0,0,0,0}; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + window = ctx->current; + layout = window->layout; + style = &ctx->style; + out = &window->buffer; + in = (layout->flags & NK_WINDOW_ROM) ? 0 :&ctx->input; + if (!(layout->flags & NK_WINDOW_SUB)) + nk_push_scissor(out, nk_null_rect); + + /* cache configuration data */ + item_spacing = style->window.spacing; + window_padding = style->window.padding; + scrollbar_size = style->window.scrollbar_size; + scaler_size = style->window.scaler_size; + + /* update the current cursor Y-position to point over the last added widget */ + layout->at_y += layout->row.height; + + /* draw footer and fill empty spaces inside a dynamically growing panel */ + if (layout->flags & NK_WINDOW_DYNAMIC && !(layout->flags & NK_WINDOW_MINIMIZED)) { + layout->height = layout->at_y - layout->bounds.y; + layout->height = NK_MIN(layout->height, layout->bounds.h); + + if ((layout->offset->x == 0) || (layout->flags & NK_WINDOW_NO_SCROLLBAR)) { + /* special case for dynamic windows without horizontal scrollbar + * or hidden scrollbars */ + footer.x = window->bounds.x; + footer.y = window->bounds.y + layout->height + item_spacing.y; + footer.w = window->bounds.w + scrollbar_size.x; + layout->footer_h = 0; + footer.h = 0; + + if ((layout->offset->x == 0) && !(layout->flags & NK_WINDOW_NO_SCROLLBAR)) { + /* special case for windows like combobox, menu require draw call + * to fill the empty scrollbar background */ + struct nk_rect bounds; + bounds.x = layout->bounds.x + layout->width; + bounds.y = layout->clip.y; + bounds.w = scrollbar_size.x; + bounds.h = layout->height; + nk_fill_rect(out, bounds, 0, style->window.background); + } + } else { + /* dynamic window with visible scrollbars and therefore bigger footer */ + footer.x = window->bounds.x; + footer.w = window->bounds.w + scrollbar_size.x; + footer.h = layout->footer_h; + if ((layout->flags & NK_WINDOW_COMBO) || (layout->flags & NK_WINDOW_MENU) || + (layout->flags & NK_WINDOW_CONTEXTUAL)) + footer.y = window->bounds.y + layout->height; + else footer.y = window->bounds.y + layout->height + layout->footer_h; + nk_fill_rect(out, footer, 0, style->window.background); + + if (!(layout->flags & NK_WINDOW_COMBO) && !(layout->flags & NK_WINDOW_MENU)) { + /* fill empty scrollbar space */ + struct nk_rect bounds; + bounds.x = layout->bounds.x; + bounds.y = window->bounds.y + layout->height; + bounds.w = layout->bounds.w; + bounds.h = layout->row.height; + nk_fill_rect(out, bounds, 0, style->window.background); + } + } + } + + /* scrollbars */ + if (!(layout->flags & NK_WINDOW_NO_SCROLLBAR) && !(layout->flags & NK_WINDOW_MINIMIZED)) + { + struct nk_rect bounds; + int scroll_has_scrolling; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + { + /* vertical scollbar */ + nk_flags state; + bounds.x = layout->bounds.x + layout->width; + bounds.y = layout->clip.y; + bounds.w = scrollbar_size.y; + bounds.h = layout->clip.h; + if (layout->flags & NK_WINDOW_BORDER) bounds.h -= 1; + + scroll_offset = layout->offset->y; + scroll_step = layout->clip.h * 0.10f; + scroll_inc = layout->clip.h * 0.01f; + scroll_target = (float)(int)(layout->at_y - layout->clip.y); + scroll_has_scrolling = (window == ctx->active); + scroll_offset = nk_do_scrollbarv(&state, out, bounds, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollv, in, &style->font); + layout->offset->y = (unsigned short)scroll_offset; + } + { + /* horizontal scrollbar */ + nk_flags state; + bounds.x = layout->bounds.x + window_padding.x; + if (layout->flags & NK_WINDOW_SUB) { + bounds.h = scrollbar_size.x; + bounds.y = (layout->flags & NK_WINDOW_BORDER) ? + layout->bounds.y + 1 : layout->bounds.y; + bounds.y += layout->header_h + layout->menu.h + layout->height; + bounds.w = layout->clip.w; + } else if (layout->flags & NK_WINDOW_DYNAMIC) { + bounds.h = NK_MIN(scrollbar_size.x, layout->footer_h); + bounds.w = layout->bounds.w; + bounds.y = footer.y; + } else { + bounds.h = NK_MIN(scrollbar_size.x, layout->footer_h); + bounds.y = layout->bounds.y + window->bounds.h; + bounds.y -= NK_MAX(layout->footer_h, scrollbar_size.x); + bounds.w = layout->width - 2 * window_padding.x; + } + scroll_offset = layout->offset->x; + scroll_target = (float)(int)(layout->max_x - bounds.x); + scroll_step = layout->max_x * 0.05f; + scroll_inc = layout->max_x * 0.005f; + scroll_has_scrolling = nk_false; + scroll_offset = nk_do_scrollbarh(&state, out, bounds, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollh, in, &style->font); + layout->offset->x = (unsigned short)scroll_offset; + } + } + + /* scaler */ + if ((layout->flags & NK_WINDOW_SCALABLE) && in && !(layout->flags & NK_WINDOW_MINIMIZED)) { + /* calculate scaler bounds */ + const struct nk_style_item *scaler; + float scaler_w = NK_MAX(0, scaler_size.x - window_padding.x); + float scaler_h = NK_MAX(0, scaler_size.y - window_padding.y); + float scaler_x = (layout->bounds.x + layout->bounds.w) - (window_padding.x + scaler_w); + float scaler_y; + + if (layout->flags & NK_WINDOW_DYNAMIC) + scaler_y = footer.y + layout->footer_h - scaler_size.y; + else scaler_y = layout->bounds.y + layout->bounds.h - scaler_size.y; + + /* draw scaler */ + scaler = &style->window.scaler; + if (scaler->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, nk_rect(scaler_x, scaler_y, scaler_w, scaler_h), + &scaler->data.image); + } else { + nk_fill_triangle(out, scaler_x + scaler_w, scaler_y, scaler_x + scaler_w, + scaler_y + scaler_h, scaler_x, scaler_y + scaler_h, + scaler->data.color); + } + + /* do window scaling logic */ + if (!(window->flags & NK_WINDOW_ROM)) { + float prev_x = in->mouse.prev.x; + float prev_y = in->mouse.prev.y; + struct nk_vec2 window_size = style->window.min_size; + int incursor = NK_INBOX(prev_x,prev_y,scaler_x,scaler_y,scaler_w,scaler_h); + + if (nk_input_is_mouse_down(in, NK_BUTTON_LEFT) && incursor) { + window->bounds.w = NK_MAX(window_size.x, window->bounds.w + in->mouse.delta.x); + /* draging in y-direction is only possible if static window */ + if (!(layout->flags & NK_WINDOW_DYNAMIC)) + window->bounds.h = NK_MAX(window_size.y, window->bounds.h + in->mouse.delta.y); + } + } + } + + /* window border */ + if (layout->flags & NK_WINDOW_BORDER) + { + const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) ? + 2.0f*style->window.border + window->bounds.y + layout->header_h: + (layout->flags & NK_WINDOW_DYNAMIC)? + layout->footer_h + footer.y: + layout->bounds.y + layout->bounds.h; + + /* select correct border color */ + struct nk_color border; + if (!(layout->flags & NK_WINDOW_SUB)) + border = style->window.border_color; + else if (layout->flags & NK_WINDOW_COMBO) + border = style->window.combo_border_color; + else if (layout->flags & NK_WINDOW_CONTEXTUAL) + border = style->window.contextual_border_color; + else if (layout->flags & NK_WINDOW_MENU) + border = style->window.menu_border_color; + else if (layout->flags & NK_WINDOW_GROUP) + border = style->window.group_border_color; + else if (layout->flags & NK_WINDOW_TOOLTIP) + border = style->window.tooltip_border_color; + else border = style->window.border_color; + + /* draw border between header and window body */ + if (window->flags & NK_WINDOW_BORDER_HEADER) + nk_stroke_line(out, window->bounds.x + layout->border/2.0f, + window->bounds.y + layout->header_h - layout->border, + window->bounds.x + window->bounds.w - layout->border, + window->bounds.y + layout->header_h - layout->border, + layout->border, border); + + /* draw bottom border */ + nk_stroke_line(out, window->bounds.x + layout->border/2.0f, + padding_y - layout->border, + window->bounds.x + window->bounds.w - layout->border, + padding_y - layout->border, + layout->border, border); + + /* draw left border */ + nk_stroke_line(out, window->bounds.x + layout->border/2.0f, + window->bounds.y + layout->border/2.0f, window->bounds.x + layout->border/2.0f, + padding_y - layout->border, layout->border, border); + + /* draw right border */ + nk_stroke_line(out, window->bounds.x + window->bounds.w - layout->border, + window->bounds.y + layout->border/2.0f, + window->bounds.x + window->bounds.w - layout->border, + padding_y - layout->border, layout->border, border); + } + + if (!(window->flags & NK_WINDOW_SUB)) { + /* window is hidden so clear command buffer */ + if (layout->flags & NK_WINDOW_HIDDEN) + nk_command_buffer_reset(&window->buffer); + /* window is visible and not tab */ + else nk_finish(ctx, window); + } + + /* NK_WINDOW_REMOVE_ROM flag was set so remove NK_WINDOW_ROM */ + if (layout->flags & NK_WINDOW_REMOVE_ROM) { + layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + layout->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + } + window->flags = layout->flags; + + /* property garbage collector */ + if (window->property.active && window->property.old != window->property.seq && + window->property.active == window->property.prev) { + nk_zero(&window->property, sizeof(window->property)); + } else { + window->property.old = window->property.seq; + window->property.prev = window->property.active; + window->property.seq = 0; + } + + /* edit garbage collector */ + if (window->edit.active && window->edit.old != window->edit.seq && + window->edit.active == window->edit.prev) { + nk_zero(&window->edit, sizeof(window->edit)); + } else { + window->edit.old = window->edit.seq; + window->edit.prev = window->edit.active; + window->edit.seq = 0; + } + + /* contextual gargabe collector */ + if (window->popup.active_con && window->popup.con_old != window->popup.con_count) { + window->popup.con_count = 0; + window->popup.con_old = 0; + window->popup.active_con = 0; + } else { + window->popup.con_old = window->popup.con_count; + window->popup.con_count = 0; + } + window->popup.combo_count = 0; + /* helper to make sure you have a 'nk_tree_push' + * for every 'nk_tree_pop' */ + NK_ASSERT(!layout->row.tree_depth); +} + +NK_API void +nk_menubar_begin(struct nk_context *ctx) +{ + struct nk_panel *layout; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + layout = ctx->current->layout; + if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + layout->menu.x = layout->at_x; + layout->menu.y = layout->bounds.y + layout->header_h; + layout->menu.w = layout->width; + layout->menu.offset = *layout->offset; + layout->offset->y = 0; +} + +NK_API void +nk_menubar_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + if (!ctx || layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + out = &win->buffer; + layout->menu.h = layout->at_y - layout->menu.y; + layout->clip.y = layout->bounds.y + layout->header_h + layout->menu.h + layout->row.height; + layout->height -= layout->menu.h; + *layout->offset = layout->menu.offset; + layout->clip.h -= layout->menu.h + layout->row.height; + layout->at_y = layout->menu.y + layout->menu.h; + nk_push_scissor(out, layout->clip); +} +/* ------------------------------------------------------------- + * + * LAYOUT + * + * --------------------------------------------------------------*/ +#define NK_LAYOUT_DYNAMIC_FIXED 0 +#define NK_LAYOUT_DYNAMIC_ROW 1 +#define NK_LAYOUT_DYNAMIC_FREE 2 +#define NK_LAYOUT_DYNAMIC 3 +#define NK_LAYOUT_STATIC_FIXED 4 +#define NK_LAYOUT_STATIC_ROW 5 +#define NK_LAYOUT_STATIC_FREE 6 +#define NK_LAYOUT_STATIC 7 + +NK_INTERN void +nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, + float height, int cols) +{ + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 item_spacing; + struct nk_vec2 panel_padding; + struct nk_color color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* prefetch some configuration data */ + layout = win->layout; + style = &ctx->style; + out = &win->buffer; + color = style->window.background; + item_spacing = style->window.spacing; + panel_padding = style->window.padding; + + /* update the current row and set the current row layout */ + layout->row.index = 0; + layout->at_y += layout->row.height; + layout->row.columns = cols; + layout->row.height = height + item_spacing.y; + layout->row.item_offset = 0; + if (layout->flags & NK_WINDOW_DYNAMIC) + nk_fill_rect(out, nk_rect(layout->bounds.x, layout->at_y, + layout->bounds.w, height + panel_padding.y), 0, color); +} + +NK_INTERN void +nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, int width) +{ + /* update the current row and set the current row layout */ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) + win->layout->row.type = NK_LAYOUT_DYNAMIC_FIXED; + else win->layout->row.type = NK_LAYOUT_STATIC_FIXED; + + win->layout->row.item_width = (float)width; + win->layout->row.ratio = 0; + win->layout->row.item_offset = 0; + win->layout->row.filled = 0; +} + +NK_API void +nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols) +{ + nk_row_layout(ctx, NK_DYNAMIC, height, cols, 0); +} + +NK_API void +nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols) +{ + nk_row_layout(ctx, NK_STATIC, height, cols, item_width); +} + +NK_API void +nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float row_height, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + + nk_panel_layout(ctx, win, row_height, cols); + if (fmt == NK_DYNAMIC) + layout->row.type = NK_LAYOUT_DYNAMIC_ROW; + else layout->row.type = NK_LAYOUT_STATIC_ROW; + + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; + layout->row.filled = 0; + layout->row.columns = cols; +} + +NK_API void +nk_layout_row_push(struct nk_context *ctx, float ratio_or_width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + + if (layout->row.type == NK_LAYOUT_DYNAMIC_ROW) { + float ratio = ratio_or_width; + if ((ratio + layout->row.filled) > 1.0f) return; + if (ratio > 0.0f) + layout->row.item_width = NK_SATURATE(ratio); + else layout->row.item_width = 1.0f - layout->row.filled; + } else layout->row.item_width = ratio_or_width; +} + +NK_API void +nk_layout_row_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item_width = 0; + layout->row.item_offset = 0; +} + +NK_API void +nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, const float *ratio) +{ + int i; + int n_undef = 0; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) { + /* calculate width of undefined widget ratios */ + float r = 0; + layout->row.ratio = ratio; + for (i = 0; i < cols; ++i) { + if (ratio[i] < 0.0f) + n_undef++; + else r += ratio[i]; + } + r = NK_SATURATE(1.0f - r); + layout->row.type = NK_LAYOUT_DYNAMIC; + layout->row.item_width = (r > 0 && n_undef > 0) ? (r / (float)n_undef):0; + } else { + layout->row.ratio = ratio; + layout->row.type = NK_LAYOUT_STATIC; + layout->row.item_width = 0; + layout->row.item_offset = 0; + } + layout->row.item_offset = 0; + layout->row.filled = 0; +} + +NK_API void +nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int widget_count) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, widget_count); + if (fmt == NK_STATIC) + layout->row.type = NK_LAYOUT_STATIC_FREE; + else layout->row.type = NK_LAYOUT_DYNAMIC_FREE; + + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; + layout->row.filled = 0; +} + +NK_API void +nk_layout_space_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item_width = 0; + layout->row.item_height = 0; + layout->row.item_offset = 0; + nk_zero(&layout->row.item, sizeof(layout->row.item)); +} + +NK_API void +nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item = rect; +} + +NK_API struct nk_rect +nk_layout_space_bounds(struct nk_context *ctx) +{ + struct nk_rect ret; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x = layout->clip.x; + ret.y = layout->clip.y; + ret.w = layout->clip.w; + ret.h = layout->row.height; + return ret; +} + +NK_API struct nk_vec2 +nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - layout->offset->x; + ret.y += layout->at_y - layout->offset->y; + return ret; +} + +NK_API struct nk_vec2 +nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + layout->offset->x; + ret.y += -layout->at_y + layout->offset->y; + return ret; +} + +NK_API struct nk_rect +nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - layout->offset->x; + ret.y += layout->at_y - layout->offset->y; + return ret; +} + +NK_API struct nk_rect +nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + layout->offset->x; + ret.y += -layout->at_y + layout->offset->y; + return ret; +} + +NK_INTERN void +nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win) +{ + struct nk_panel *layout = win->layout; + struct nk_vec2 spacing = ctx->style.window.spacing; + const float row_height = layout->row.height - spacing.y; + nk_panel_layout(ctx, win, row_height, layout->row.columns); +} + +NK_INTERN void +nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, + struct nk_window *win, int modify) +{ + struct nk_panel *layout; + float item_offset = 0; + float item_width = 0; + float item_spacing = 0; + + float panel_padding; + float panel_spacing; + float panel_space; + + struct nk_vec2 spacing; + struct nk_vec2 padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(bounds); + + /* cache some configuration data */ + spacing = ctx->style.window.spacing; + padding = ctx->style.window.padding; + + /* calculate the useable panel space */ + panel_padding = 2 * padding.x; + panel_spacing = (float)(layout->row.columns - 1) * spacing.x; + panel_space = layout->width - panel_padding - panel_spacing; + + /* calculate the width of one item inside the current layout space */ + switch (layout->row.type) { + case NK_LAYOUT_DYNAMIC_FIXED: { + /* scaling fixed size widgets item width */ + item_width = panel_space / (float)layout->row.columns; + item_offset = (float)layout->row.index * item_width; + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_DYNAMIC_ROW: { + /* scaling single ratio widget width */ + item_width = layout->row.item_width * panel_space; + item_offset = layout->row.item_offset; + item_spacing = (float)layout->row.index * spacing.x; + + if (modify) { + layout->row.item_offset += item_width + spacing.x; + layout->row.filled += layout->row.item_width; + layout->row.index = 0; + } + } break; + case NK_LAYOUT_DYNAMIC_FREE: { + /* panel width depended free widget placing */ + bounds->x = layout->at_x + (layout->width * layout->row.item.x); + bounds->x -= layout->offset->x; + bounds->y = layout->at_y + (layout->row.height * layout->row.item.y); + bounds->y -= layout->offset->y; + bounds->w = layout->width * layout->row.item.w; + bounds->h = layout->row.height * layout->row.item.h; + return; + }; + case NK_LAYOUT_DYNAMIC: { + /* scaling arrays of panel width ratios for every widget */ + float ratio; + NK_ASSERT(layout->row.ratio); + ratio = (layout->row.ratio[layout->row.index] < 0) ? + layout->row.item_width : layout->row.ratio[layout->row.index]; + + item_spacing = (float)layout->row.index * spacing.x; + if (layout->row.index < layout->row.columns-1) + item_width = (ratio * panel_space) - spacing.x; + else item_width = (ratio * panel_space); + + item_offset = layout->row.item_offset; + if (modify) { + layout->row.item_offset += item_width + spacing.x; + layout->row.filled += ratio; + } + } break; + case NK_LAYOUT_STATIC_FIXED: { + /* non-scaling fixed widgets item width */ + item_width = layout->row.item_width; + item_offset = (float)layout->row.index * item_width; + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_STATIC_ROW: { + /* scaling single ratio widget width */ + item_width = layout->row.item_width; + item_offset = layout->row.item_offset; + item_spacing = (float)layout->row.index * spacing.x; + if (modify) { + layout->row.item_offset += item_width + spacing.x; + layout->row.index = 0; + } + } break; + case NK_LAYOUT_STATIC_FREE: { + /* free widget placing */ + bounds->x = layout->at_x + layout->row.item.x; + bounds->w = layout->row.item.w; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = (bounds->x + bounds->w); + bounds->x -= layout->offset->x; + bounds->y = layout->at_y + layout->row.item.y; + bounds->y -= layout->offset->y; + bounds->h = layout->row.item.h; + return; + }; + case NK_LAYOUT_STATIC: { + /* non-scaling array of panel pixel width for every widget */ + item_spacing = (float)layout->row.index * spacing.x; + item_width = layout->row.ratio[layout->row.index]; + item_offset = layout->row.item_offset; + if (modify) layout->row.item_offset += item_width + spacing.x; + } break; + default: NK_ASSERT(0); break; + }; + + /* set the bounds of the newly allocated widget */ + bounds->w = item_width; + bounds->h = layout->row.height - spacing.y; + bounds->y = layout->at_y - layout->offset->y; + bounds->x = layout->at_x + item_offset + item_spacing + padding.x; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = bounds->x + bounds->w; + bounds->x -= layout->offset->x; +} + +NK_INTERN void +nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* check if the end of the row has been hit and begin new row if so */ + win = ctx->current; + layout = win->layout; + if (layout->row.index >= layout->row.columns) + nk_panel_alloc_row(ctx, win); + + /* calculate widget position and size */ + nk_layout_widget_space(bounds, ctx, win, nk_true); + layout->row.index++; +} + +NK_INTERN void +nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx) +{ + float y; + int index; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + y = layout->at_y; + index = layout->row.index; + if (layout->row.index >= layout->row.columns) { + layout->at_y += layout->row.height; + layout->row.index = 0; + } + nk_layout_widget_space(bounds, ctx, win, nk_false); + layout->at_y = y; + layout->row.index = index; +} + +NK_API int +nk__tree_push(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states initial_state, + const char *file, int line) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + const struct nk_input *in; + + struct nk_vec2 item_spacing; + struct nk_vec2 panel_padding; + struct nk_rect header = {0,0,0,0}; + struct nk_rect sym = {0,0,0,0}; + struct nk_text text; + + nk_flags ws; + int title_len; + nk_hash title_hash; + nk_uint *state = 0; + enum nk_widget_layout_states widget_state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* cache some data */ + win = ctx->current; + layout = win->layout; + out = &win->buffer; + style = &ctx->style; + + item_spacing = style->window.spacing; + panel_padding = style->window.padding; + + /* calculate header bounds and draw background */ + nk_layout_row_dynamic(ctx, style->font.height + 2 * style->tab.padding.y, 1); + widget_state = nk_widget(&header, ctx); + if (type == NK_TREE_TAB) { + const struct nk_style_item *background = &style->tab.background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, header, &background->data.image); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, style->tab.border_color); + nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), + style->tab.rounding, background->data.color); + } + } else text.background = style->window.background; + + /* find or create tab persistent state (open/closed) */ + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); + if (file) title_hash += nk_murmur_hash(file, (int)nk_strlen(file), (nk_hash)line); + state = nk_find_value(win, title_hash); + if (!state) { + state = nk_add_value(ctx, win, title_hash, 0); + *state = initial_state; + } + + /* update node state */ + in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; + in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; + if (nk_button_behavior(&ws, header, in, NK_BUTTON_DEFAULT)) + *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED; + + { + /* calculate the triangle bounds */ + sym.w = sym.h = style->font.height; + sym.y = header.y + style->tab.padding.y; + sym.x = header.x + panel_padding.x + style->tab.padding.x; + + /* calculate the triangle points and draw triangle */ + nk_do_button_symbol(&ws, &win->buffer, sym, + (*state == NK_MAXIMIZED)? style->tab.sym_minimize: style->tab.sym_maximize, + NK_BUTTON_DEFAULT, (type == NK_TREE_TAB)? + &style->tab.tab_button: &style->tab.node_button, + in, &style->font); + + /* calculate the space the icon occupied */ + sym.w = style->font.height + 2 * style->tab.spacing.x; + } + { + /* draw node label */ + struct nk_rect label; + header.w = NK_MAX(header.w, sym.w + item_spacing.y + panel_padding.x); + label.x = sym.x + sym.w + item_spacing.x; + label.y = sym.y; + label.w = header.w - (sym.w + item_spacing.y + panel_padding.x); + label.h = style->font.height; + + text.text = style->tab.text; + text.padding = nk_vec2(0,0); + nk_widget_text(out, label, title, nk_strlen(title), &text, + NK_TEXT_LEFT, &style->font); + } + + /* increase x-axis cursor widget position pointer */ + if (*state == NK_MAXIMIZED) { + layout->at_x = header.x + layout->offset->x; + layout->width = NK_MAX(layout->width, 2 * panel_padding.x); + layout->width -= 2 * panel_padding.x; + layout->row.tree_depth++; + return nk_true; + } else return nk_false; +} + +NK_API void +nk_tree_pop(struct nk_context *ctx) +{ + struct nk_vec2 panel_padding; + struct nk_window *win = 0; + struct nk_panel *layout = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + panel_padding = ctx->style.window.padding; + layout->at_x -= panel_padding.x; + layout->width += 2 * panel_padding.x; + NK_ASSERT(layout->row.tree_depth); + layout->row.tree_depth--; +} +/*---------------------------------------------------------------- + * + * WIDGETS + * + * --------------------------------------------------------------*/ +NK_API struct nk_rect +nk_widget_bounds(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_rect(0,0,0,0); + nk_layout_peek(&bounds, ctx); + return bounds; +} + +NK_API struct nk_vec2 +nk_widget_position(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.x, bounds.y); +} + +NK_API struct nk_vec2 +nk_widget_size(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.w, bounds.h); +} + +NK_API int +nk_widget_is_hovered(struct nk_context *ctx) +{ + int ret; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + ret = (ctx->active == ctx->current); + ret = ret && nk_input_is_mouse_hovering_rect(&ctx->input, bounds); + return ret; +} + +NK_API int +nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn) +{ + int ret; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + ret = (ctx->active == ctx->current); + ret = ret && nk_input_mouse_clicked(&ctx->input, btn, bounds); + return ret; +} + +NK_API int +nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, int down) +{ + int ret; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + ret = (ctx->active == ctx->current); + ret = ret && nk_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down); + return ret; +} + +NK_API enum nk_widget_layout_states +nk_widget(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_rect *c = 0; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + /* allocate space and check if the widget needs to be updated and drawn */ + nk_panel_alloc_space(bounds, ctx); + c = &ctx->current->layout->clip; + if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds->x, bounds->y, bounds->w, bounds->h)) + return NK_WIDGET_INVALID; + if (!NK_CONTAINS(bounds->x, bounds->y, bounds->w, bounds->h, c->x, c->y, c->w, c->h)) + return NK_WIDGET_ROM; + return NK_WIDGET_VALID; +} + +NK_API enum nk_widget_layout_states +nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx, + struct nk_vec2 item_padding) +{ + /* update the bounds to stand without padding */ + struct nk_window *win; + struct nk_style *style; + struct nk_panel *layout; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(bounds, ctx); + if (layout->row.index == 1) { + bounds->w += style->window.padding.x; + bounds->x -= style->window.padding.x; + } else bounds->x -= item_padding.x; + + if (layout->row.index == layout->row.columns) + bounds->w += style->window.padding.x; + else bounds->w += item_padding.x; + return state; +} + +/*---------------------------------------------------------------- + * + * MISC + * + * --------------------------------------------------------------*/ +NK_API void +nk_spacing(struct nk_context *ctx, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_rect nil; + int i, index, rows; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* spacing over row boundries */ + win = ctx->current; + layout = win->layout; + index = (layout->row.index + cols) % layout->row.columns; + rows = (layout->row.index + cols) / layout->row.columns; + if (rows) { + for (i = 0; i < rows; ++i) + nk_panel_alloc_row(ctx, win); + cols = index; + } + + /* non table layout need to allocate space */ + if (layout->row.type != NK_LAYOUT_DYNAMIC_FIXED && + layout->row.type != NK_LAYOUT_STATIC_FIXED) { + for (i = 0; i < cols; ++i) + nk_panel_alloc_space(&nil, ctx); + } + layout->row.index = index; +} + +/*---------------------------------------------------------------- + * + * TEXT + * + * --------------------------------------------------------------*/ +NK_API void +nk_text_colored(struct nk_context *ctx, const char *str, int len, + nk_flags alignment, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, &style->font); +} + +NK_API void +nk_text_wrap_colored(struct nk_context *ctx, const char *str, + int len, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, &style->font); + ctx->last_widget_state = 0; +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_API void +nk_labelf_colored(struct nk_context *ctx, nk_flags flags, + struct nk_color color, const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmtv(buf, NK_LEN(buf), fmt, args); + nk_label_colored(ctx, buf, flags, color); + va_end(args); +} + +NK_API void +nk_labelf_colored_wrap(struct nk_context *ctx, struct nk_color color, + const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmtv(buf, NK_LEN(buf), fmt, args); + nk_label_colored_wrap(ctx, buf, color); + va_end(args); +} + +NK_API void +nk_labelf(struct nk_context *ctx, nk_flags flags, const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmtv(buf, NK_LEN(buf), fmt, args); + nk_label(ctx, buf, flags); + va_end(args); +} + +NK_API void +nk_labelf_wrap(struct nk_context *ctx, const char *fmt,...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmtv(buf, NK_LEN(buf), fmt, args); + nk_label_wrap(ctx, buf); + va_end(args); +} + +NK_API void +nk_value_bool(struct nk_context *ctx, const char *prefix, int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false"));} + +NK_API void +nk_value_int(struct nk_context *ctx, const char *prefix, int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value);} + +NK_API void +nk_value_uint(struct nk_context *ctx, const char *prefix, unsigned int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value);} + +NK_API void +nk_value_float(struct nk_context *ctx, const char *prefix, float value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %.3f", prefix, value);} + +NK_API void +nk_value_color_byte(struct nk_context *ctx, const char *p, struct nk_color c) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%c, %c, %c, %c)", p, c.r, c.g, c.b, c.a);} + +NK_API void +nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color color) +{ + float c[4]; nk_color_fv(c, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)", + p, c[0], c[1], c[2], c[3]); +} + +NK_API void +nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color color) +{ + char hex[16]; + nk_color_hex_rgba(hex, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, hex); +} +#endif + +NK_API void +nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_colored(ctx, str, len, alignment, ctx->style.text.color); +} + +NK_API void +nk_text_wrap(struct nk_context *ctx, const char *str, int len) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_wrap_colored(ctx, str, len, ctx->style.text.color); +} + +NK_API void +nk_label(struct nk_context *ctx, const char *str, nk_flags alignment) +{nk_text(ctx, str, nk_strlen(str), alignment);} + +NK_API void +nk_label_colored(struct nk_context *ctx, const char *str, nk_flags align, + struct nk_color color) +{nk_text_colored(ctx, str, nk_strlen(str), align, color);} + +NK_API void +nk_label_wrap(struct nk_context *ctx, const char *str) +{nk_text_wrap(ctx, str, nk_strlen(str));} + +NK_API void +nk_label_colored_wrap(struct nk_context *ctx, const char *str, struct nk_color color) +{nk_text_wrap_colored(ctx, str, nk_strlen(str), color);} + +NK_API void +nk_image(struct nk_context *ctx, struct nk_image img) +{ + struct nk_window *win; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + if (!nk_widget(&bounds, ctx)) return; + nk_draw_image(&win->buffer, bounds, &img); + ctx->last_widget_state = 0; +} + +/*---------------------------------------------------------------- + * + * BUTTON + * + * --------------------------------------------------------------*/ +NK_API int +nk_button_text(struct nk_context *ctx, const char *title, int len, + enum nk_button_behavior behavior) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + title, len, style->button.text_alignment, behavior, + &style->button, in, &style->font); +} + +NK_API int nk_button_label(struct nk_context *ctx, const char *title, + enum nk_button_behavior behavior) +{return nk_button_text(ctx, title, nk_strlen(title), behavior);} + +NK_API int +nk_button_color(struct nk_context *ctx, struct nk_color color, + enum nk_button_behavior behavior) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + struct nk_style_button button; + + int ret = 0; + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + button = ctx->style.button; + button.normal = nk_style_item_color(color); + button.hover = nk_style_item_color(color); + button.active = nk_style_item_color(color); + button.padding = nk_vec2(0,0); + ret = nk_do_button(&ctx->last_widget_state, &win->buffer, bounds, + &button, in, behavior, &bounds); + nk_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button); + return ret; +} + +NK_API int +nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, + enum nk_button_behavior behavior) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, behavior, &style->button, in, &style->font); +} + +NK_API int +nk_button_image(struct nk_context *ctx, struct nk_image img, + enum nk_button_behavior behavior) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds, + img, behavior, &style->button, in); +} + +NK_API int +nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char* text, int len, nk_flags align, enum nk_button_behavior behavior) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, behavior, &style->button, &style->font, in); +} + +NK_API int nk_button_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *label, nk_flags align, enum nk_button_behavior behavior) +{return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align, behavior);} + +NK_API int +nk_button_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align, enum nk_button_behavior behavior) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + bounds, img, text, len, align, behavior, &style->button, &style->font, in); +} + +NK_API int nk_button_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align, enum nk_button_behavior behavior) +{return nk_button_image_text(ctx, img, label, nk_strlen(label), align, behavior);} + +/*---------------------------------------------------------------- + * + * SELECTABLE + * + * --------------------------------------------------------------*/ +NK_API int +nk_selectable_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &style->selectable, in, &style->font); +} + +NK_API int nk_select_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int value) +{nk_selectable_text(ctx, str, len, align, &value);return value;} + +NK_API int nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, int *value) +{return nk_selectable_text(ctx, str, nk_strlen(str), align, value);} + +NK_API int nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, int value) +{nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value;} + +/*---------------------------------------------------------------- + * + * CHECKBOX + * + * --------------------------------------------------------------*/ +NK_API int +nk_check_text(struct nk_context *ctx, const char *text, int len, int active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return active; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active, + text, len, NK_TOGGLE_CHECK, &style->checkbox, in, &style->font); + return active; +} + +NK_API unsigned int +nk_check_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int flags, unsigned int value) +{ + int old_active, active; + NK_ASSERT(ctx); + NK_ASSERT(text); + if (!ctx || !text) return flags; + old_active = active = (int)((flags & value) & value); + if (nk_check_text(ctx, text, len, old_active)) + flags |= value; + else flags &= ~value; + return flags; +} + +NK_API int +nk_checkbox_text(struct nk_context *ctx, const char *text, int len, int *active) +{ + int old_val; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_val = *active; + *active = nk_check_text(ctx, text, len, *active); + return old_val != *active; +} + +NK_API int +nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int *flags, unsigned int value) +{ + int active; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(flags); + if (!ctx || !text || !flags) return 0; + active = (int)((*flags & value) & value); + if (nk_checkbox_text(ctx, text, len, &active)) { + if (active) *flags |= value; + else *flags &= ~value; + return 1; + } + return 0; +} + +NK_API int nk_check_label(struct nk_context *ctx, const char *label, int active) +{return nk_check_text(ctx, label, nk_strlen(label), active);} + +NK_API unsigned int nk_check_flags_label(struct nk_context *ctx, const char *label, + unsigned int flags, unsigned int value) +{return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value);} + +NK_API int nk_checkbox_label(struct nk_context *ctx, const char *label, int *active) +{return nk_checkbox_text(ctx, label, nk_strlen(label), active);} + +NK_API int nk_checkbox_flags_label(struct nk_context *ctx, const char *label, + unsigned int *flags, unsigned int value) +{return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value);} + +/*---------------------------------------------------------------- + * + * OPTION + * + * --------------------------------------------------------------*/ +NK_API int +nk_option_text(struct nk_context *ctx, const char *text, int len, int is_active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return is_active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return state; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active, + text, len, NK_TOGGLE_OPTION, &style->option, in, &style->font); + return is_active; +} + +NK_API int +nk_radio_text(struct nk_context *ctx, const char *text, int len, int *active) +{ + int old_value; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_value = *active; + *active = nk_option_text(ctx, text, len, old_value); + return old_value != *active; +} + +NK_API int +nk_option_label(struct nk_context *ctx, const char *label, int active) +{return nk_option_text(ctx, label, nk_strlen(label), active);} + +NK_API int +nk_radio_label(struct nk_context *ctx, const char *label, int *active) +{return nk_radio_text(ctx, label, nk_strlen(label), active);} + +/*---------------------------------------------------------------- + * + * SLIDER + * + * --------------------------------------------------------------*/ +NK_API int +nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max_value, + float value_step) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + int ret = 0; + float old_value; + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(value); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return ret; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return ret; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + old_value = *value; + *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value, + old_value, max_value, value_step, &style->slider, in, &style->font); + return (old_value > *value || old_value < *value); +} + +NK_API float +nk_slide_float(struct nk_context *ctx, float min, float val, float max, float step) +{ + nk_slider_float(ctx, min, &val, max, step); return val; +} + +NK_API int +nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step) +{ + float value = (float)val; + nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + return (int)value; +} + +NK_API int +nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step) +{ + int ret; + float value = (float)*val; + ret = nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + *val = (int)value; + return ret; +} + +/*---------------------------------------------------------------- + * + * PROGRESSBAR + * + * --------------------------------------------------------------*/ +NK_API int +nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, int is_modifyable) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + nk_size old_value; + + NK_ASSERT(ctx); + NK_ASSERT(cur); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !cur) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + old_value = *cur; + *cur = nk_do_progress(&ctx->last_widget_state, &win->buffer, bounds, + *cur, max, is_modifyable, &style->progress, in); + return (*cur != old_value); +} + +NK_API nk_size nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, int modifyable) +{nk_progress(ctx, &cur, max, modifyable);return cur;} + +/*---------------------------------------------------------------- + * + * EDIT + * + * --------------------------------------------------------------*/ +NK_API nk_flags +nk_edit_string(struct nk_context *ctx, nk_flags flags, + char *memory, int *len, int max, nk_filter filter) +{ + nk_hash hash; + nk_flags state; + struct nk_text_edit *edit; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(memory); + NK_ASSERT(len); + if (!ctx || !memory || !len) + return 0; + + filter = (!filter) ? nk_filter_default: filter; + win = ctx->current; + hash = win->edit.seq; + edit = &ctx->text_edit; + nk_textedit_clear_state(&ctx->text_edit, (flags & NK_EDIT_MULTILINE)? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE, filter); + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = nk_utf_len(memory, *len); + else edit->cursor = win->edit.cursor; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = win->edit.cursor; + edit->select_end = win->edit.cursor; + } else { + edit->select_start = win->edit.sel_start; + edit->select_end = win->edit.sel_end; + } + edit->insert_mode = win->edit.insert_mode; + edit->scrollbar.x = (float)win->edit.scrollbar.x; + edit->scrollbar.y = (float)win->edit.scrollbar.y; + edit->active = nk_true; + } else edit->active = nk_false; + + max = NK_MAX(1, max); + *len = NK_MIN(*len, max-1); + nk_str_init_fixed(&edit->string, memory, (nk_size)max); + edit->string.buffer.allocated = (nk_size)*len; + edit->string.len = nk_utf_len(memory, *len); + state = nk_edit_buffer(ctx, flags, edit, filter); + *len = (int)edit->string.buffer.allocated; + + if (edit->active) { + win->edit.cursor = edit->cursor; + win->edit.sel_start = edit->select_start; + win->edit.sel_end = edit->select_end; + win->edit.insert_mode = edit->insert_mode; + win->edit.scrollbar.x = (unsigned short)edit->scrollbar.x; + win->edit.scrollbar.y = (unsigned short)edit->scrollbar.y; + } + return state; +} + +NK_API nk_flags +nk_edit_buffer(struct nk_context *ctx, nk_flags flags, + struct nk_text_edit *edit, nk_filter filter) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + nk_flags ret_flags = 0; + unsigned char prev_state; + nk_hash hash; + + /* make sure correct values */ + NK_ASSERT(ctx); + NK_ASSERT(edit); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget(&bounds, ctx); + if (!state) return state; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + /* check if edit is currently hot item */ + hash = win->edit.seq++; + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = edit->string.len; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = edit->cursor; + edit->select_end = edit->cursor; + } + } + + filter = (!filter) ? nk_filter_default: filter; + prev_state = (unsigned char)edit->active; + in = (flags & NK_EDIT_READ_ONLY) ? 0: in; + ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags, + filter, edit, &style->edit, in, &style->font); + + if (edit->active && prev_state != edit->active) { + /* current edit is now hot */ + win->edit.active = nk_true; + win->edit.name = hash; + } else if (prev_state && !edit->active) { + /* current edit is now cold */ + win->edit.active = nk_false; + } + return ret_flags; +} + +/*---------------------------------------------------------------- + * + * PROPERTY + * + * --------------------------------------------------------------*/ +NK_INTERN float +nk_property(struct nk_context *ctx, const char *name, float min, float val, + float max, float step, float inc_per_pixel, const enum nk_property_filter filter) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states s; + + int *state = 0; + nk_hash hash = 0; + char *buffer = 0; + int *len = 0; + int *cursor = 0; + int old_state; + + char dummy_buffer[NK_MAX_NUMBER_BUFFER]; + int dummy_state = NK_PROPERTY_DEFAULT; + int dummy_length = 0; + int dummy_cursor = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return val; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + s = nk_widget(&bounds, ctx); + if (!s) return val; + in = (s == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + /* calculate hash from name */ + if (name[0] == '#') { + hash = nk_murmur_hash(name, (int)nk_strlen(name), win->property.seq++); + name++; /* special number hash */ + } else hash = nk_murmur_hash(name, (int)nk_strlen(name), 42); + + /* check if property is currently hot item */ + if (win->property.active && hash == win->property.name) { + buffer = win->property.buffer; + len = &win->property.length; + cursor = &win->property.cursor; + state = &win->property.state; + } else { + buffer = dummy_buffer; + len = &dummy_length; + cursor = &dummy_cursor; + state = &dummy_state; + } + + /* execute property widget */ + old_state = *state; + val = nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name, + min, val, max, step, inc_per_pixel, buffer, len, state, cursor, + &style->property, filter, in, &style->font, &ctx->text_edit); + + if (in && *state != NK_PROPERTY_DEFAULT && !win->property.active) { + /* current property is now hot */ + win->property.active = 1; + NK_MEMCPY(win->property.buffer, buffer, (nk_size)*len); + win->property.length = *len; + win->property.cursor = *cursor; + win->property.state = *state; + win->property.name = hash; + } + + /* check if previously active property is now unactive */ + if (*state == NK_PROPERTY_DEFAULT && old_state != NK_PROPERTY_DEFAULT) + win->property.active = 0; + return val; +} + +NK_API void +nk_property_float(struct nk_context *ctx, const char *name, + float min, float *val, float max, float step, float inc_per_pixel) +{ + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + if (!ctx || !ctx->current || !name || !val) return; + *val = nk_property(ctx, name, min, *val, max, step, inc_per_pixel, NK_FILTER_FLOAT); +} + +NK_API void +nk_property_int(struct nk_context *ctx, const char *name, + int min, int *val, int max, int step, int inc_per_pixel) +{ + float value; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + if (!ctx || !ctx->current || !name || !val) return; + value = nk_property(ctx, name, (float)min, (float)*val, (float)max, (float)step, + (float)inc_per_pixel, NK_FILTER_FLOAT); + *val = (int)value; +} + +NK_API float +nk_propertyf(struct nk_context *ctx, const char *name, float min, + float val, float max, float step, float inc_per_pixel) +{ + NK_ASSERT(ctx); + NK_ASSERT(name); + if (!ctx || !ctx->current || !name) return val; + return nk_property(ctx, name, min, val, max, step, inc_per_pixel, NK_FILTER_FLOAT); +} + +NK_API int +nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, + int max, int step, int inc_per_pixel) +{ + float value; + NK_ASSERT(ctx); + NK_ASSERT(name); + if (!ctx || !ctx->current || !name) return val; + value = nk_property(ctx, name, (float)min, (float)val, (float)max, (float)step, + (float)inc_per_pixel, NK_FILTER_FLOAT); + return (int)value; +} + +/*---------------------------------------------------------------- + * + * COLOR PICKER + * + * --------------------------------------------------------------*/ +NK_API int +nk_color_pick(struct nk_context * ctx, struct nk_color *color, + enum nk_color_format fmt) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *config; + const struct nk_input *in; + + nk_flags ws; + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(color); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !color) + return 0; + + win = ctx->current; + config = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_color_picker(&ws, &win->buffer, color, fmt, bounds, + nk_vec2(0,0), in, &config->font); +} + +NK_API struct nk_color +nk_color_picker(struct nk_context *ctx, struct nk_color color, + enum nk_color_format fmt) +{ + nk_color_pick(ctx, &color, fmt); + return color; +} + +/* ------------------------------------------------------------- + * + * CHART + * + * --------------------------------------------------------------*/ +NK_API int +nk_chart_begin(struct nk_context *ctx, const enum nk_chart_type type, + int count, float min_value, float max_value) +{ + struct nk_window *win; + struct nk_chart *chart; + const struct nk_style *config; + + const struct nk_style_item *background; + struct nk_rect bounds = {0, 0, 0, 0}; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + if (!nk_widget(&bounds, ctx)) { + chart = &ctx->current->layout->chart; + chart->style = 0; + nk_zero(chart, sizeof(*chart)); + return 0; + } + + win = ctx->current; + config = &ctx->style; + chart = &win->layout->chart; + + /* setup basic generic chart */ + nk_zero(chart, sizeof(*chart)); + chart->type = type; + chart->style = (type == NK_CHART_LINES) ? &config->line_chart: &config->column_chart; + chart->index = 0; + chart->count = count; + chart->min = NK_MIN(min_value, max_value); + chart->max = NK_MAX(min_value, max_value); + chart->range = chart->max - chart->min; + chart->x = bounds.x + chart->style->padding.x; + chart->y = bounds.y + chart->style->padding.y; + chart->w = bounds.w - 2 * chart->style->padding.x; + chart->h = bounds.h - 2 * chart->style->padding.y; + chart->w = NK_MAX(chart->w, 2 * chart->style->padding.x); + chart->h = NK_MAX(chart->h, 2 * chart->style->padding.y); + chart->last.x = 0; chart->last.y = 0; + + /* draw chart background */ + background = &chart->style->background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, bounds, &background->data.image); + } else { + nk_fill_rect(&win->buffer, bounds, chart->style->rounding, chart->style->border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(bounds, chart->style->border), + chart->style->rounding, chart->style->border_color); + } + return 1; +} + +NK_INTERN nk_flags +nk_chart_push_line(struct nk_context *ctx, struct nk_window *win, + struct nk_chart *g, float value) +{ + struct nk_panel *layout = win->layout; + const struct nk_input *i = &ctx->input; + struct nk_command_buffer *out = &win->buffer; + + nk_flags ret = 0; + struct nk_vec2 cur; + struct nk_rect bounds; + struct nk_color color; + float step; + float range; + float ratio; + + step = g->w / (float)g->count; + range = g->max - g->min; + ratio = (value - g->min) / range; + + if (g->index == 0) { + /* first data point does not have a connection */ + g->last.x = g->x; + g->last.y = (g->y + g->h) - ratio * (float)g->h; + + bounds.x = g->last.x - 2; + bounds.y = g->last.y - 2; + bounds.w = 4; + bounds.h = 4; + + color = g->style->color; + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->last.x-3, g->last.y-3, 6, 6)){ + ret = nk_input_is_mouse_hovering_rect(i, bounds) ? NK_CHART_HOVERING : 0; + ret |= (i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->style->selected_color; + } + nk_fill_rect(out, bounds, 0, color); + g->index++; + return ret; + } + + /* draw a line between the last data point and the new one */ + cur.x = g->x + (float)(step * (float)g->index); + cur.y = (g->y + g->h) - (ratio * (float)g->h); + nk_stroke_line(out, g->last.x, g->last.y, cur.x, cur.y, 1.0f, g->style->color); + + bounds.x = cur.x - 3; + bounds.y = cur.y - 3; + bounds.w = 6; + bounds.h = 6; + + /* user selection of current data point */ + color = g->style->color; + if (!(layout->flags & NK_WINDOW_ROM)) { + if (nk_input_is_mouse_hovering_rect(i, bounds)) { + ret = NK_CHART_HOVERING; + ret |= (!i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->style->selected_color; + } + } + nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color); + + /* save current data point position */ + g->last.x = cur.x; + g->last.y = cur.y; + g->index++; + return ret; +} + +NK_INTERN nk_flags +nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win, + struct nk_chart *chart, float value) +{ + struct nk_command_buffer *out = &win->buffer; + const struct nk_input *in = &ctx->input; + struct nk_panel *layout = win->layout; + + float ratio; + nk_flags ret = 0; + struct nk_color color; + struct nk_rect item = {0,0,0,0}; + + if (chart->index >= chart->count) + return nk_false; + if (chart->count) { + float padding = (float)(chart->count-1); + item.w = (chart->w - padding) / (float)(chart->count); + } + + /* calculate bounds of the current bar chart entry */ + color = chart->style->color; + item.h = chart->h * NK_ABS((value/chart->range)); + if (value >= 0) { + ratio = (value + NK_ABS(chart->min)) / NK_ABS(chart->range); + item.y = (chart->y + chart->h) - chart->h * ratio; + } else { + ratio = (value - chart->max) / chart->range; + item.y = chart->y + (chart->h * NK_ABS(ratio)) - item.h; + } + item.x = chart->x + ((float)chart->index * item.w); + item.x = item.x + ((float)chart->index); + + /* user chart bar selection */ + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) { + ret = NK_CHART_HOVERING; + ret |= (!in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = chart->style->selected_color; + } + nk_fill_rect(out, item, 0, color); + chart->index++; + return ret; +} + +NK_API nk_flags +nk_chart_push(struct nk_context *ctx, float value) +{ + nk_flags flags; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current || !ctx->current->layout->chart.style) + return nk_false; + + win = ctx->current; + switch (win->layout->chart.type) { + case NK_CHART_LINES: + flags = nk_chart_push_line(ctx, win, &win->layout->chart, value); break; + case NK_CHART_COLUMN: + flags = nk_chart_push_column(ctx, win, &win->layout->chart, value); break; + default: + case NK_CHART_MAX: + flags = 0; + } + return flags; +} + +NK_API void +nk_chart_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_chart *chart; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + win = ctx->current; + chart = &win->layout->chart; + chart->type = NK_CHART_MAX; + chart->index = 0; + chart->count = 0; + chart->min = 0; + chart->max = 0; + chart->x = 0; + chart->y = 0; + chart->w = 0; + chart->h = 0; + return; +} + +/* ------------------------------------------------------------- + * + * GROUP + * + * --------------------------------------------------------------*/ +NK_API int +nk_group_begin(struct nk_context *ctx, struct nk_panel *layout, const char *title, + nk_flags flags) +{ + struct nk_window *win; + const struct nk_rect *c; + union {struct nk_scroll *s; nk_uint *i;} value; + struct nk_window panel; + struct nk_rect bounds; + nk_hash title_hash; + int title_len; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !title) + return 0; + + /* allocate space for the group panel inside the panel */ + win = ctx->current; + c = &win->layout->clip; + nk_panel_alloc_space(&bounds, ctx); + nk_zero(layout, sizeof(*layout)); + + /* find group persistent scrollbar value */ + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_WINDOW_SUB); + value.i = nk_find_value(win, title_hash); + if (!value.i) { + value.i = nk_add_value(ctx, win, title_hash, 0); + *value.i = 0; + } + if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && + !(flags & NK_WINDOW_MOVABLE)) { + return 0; + } + + flags |= NK_WINDOW_SUB; + if (win->flags & NK_WINDOW_ROM) + flags |= NK_WINDOW_ROM; + + /* initialize a fake window to create the layout from */ + nk_zero(&panel, sizeof(panel)); + panel.bounds = bounds; + panel.flags = flags; + panel.scrollbar.x = (unsigned short)value.s->x; + panel.scrollbar.y = (unsigned short)value.s->y; + panel.buffer = win->buffer; + panel.layout = layout; + ctx->current = &panel; + nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0); + + win->buffer = panel.buffer; + win->buffer.clip = layout->clip; + layout->offset = value.s; + layout->parent = win->layout; + win->layout = layout; + ctx->current = win; + return 1; +} + +NK_API void +nk_group_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *parent; + struct nk_panel *g; + + struct nk_rect clip; + struct nk_window pan; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + /* make sure nk_group_begin was called correctly */ + NK_ASSERT(ctx->current); + win = ctx->current; + NK_ASSERT(win->layout); + g = win->layout; + NK_ASSERT(g->parent); + parent = g->parent; + + /* dummy window */ + nk_zero(&pan, sizeof(pan)); + pan.bounds = g->bounds; + pan.scrollbar.x = (unsigned short)g->offset->x; + pan.scrollbar.y = (unsigned short)g->offset->y; + pan.flags = g->flags|NK_WINDOW_SUB; + pan.buffer = win->buffer; + pan.layout = g; + ctx->current = &pan; + + /* make sure group has correct clipping rectangle */ + nk_unify(&clip, &parent->clip, + g->bounds.x, g->clip.y - g->header_h, + g->bounds.x + g->bounds.w+1, + g->bounds.y + g->bounds.h + 1); + nk_push_scissor(&pan.buffer, clip); + nk_end(ctx); + + win->buffer = pan.buffer; + nk_push_scissor(&win->buffer, parent->clip); + ctx->current = win; + win->layout = parent; + win->bounds = parent->bounds; + if (win->flags & NK_WINDOW_BORDER) + win->bounds = nk_shrink_rect(win->bounds, -win->layout->border); + return; +} + +/* -------------------------------------------------------------- + * + * POPUP + * + * --------------------------------------------------------------*/ +NK_API int +nk_popup_begin(struct nk_context *ctx, struct nk_panel *layout, + enum nk_popup_type type, const char *title, nk_flags flags, struct nk_rect rect) +{ + struct nk_window *popup; + struct nk_window *win; + + int title_len; + nk_hash title_hash; + nk_size allocated; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + NK_ASSERT(!(win->flags & NK_WINDOW_POPUP)); + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_WINDOW_POPUP); + + popup = win->popup.win; + if (!popup) { + popup = (struct nk_window*)nk_create_window(ctx); + win->popup.win = popup; + win->popup.active = 0; + } + + /* make sure we have to correct popup */ + if (win->popup.name != title_hash) { + if (!win->popup.active) { + nk_zero(popup, sizeof(*popup)); + win->popup.name = title_hash; + win->popup.active = 1; + } else return 0; + } + + /* popup position is local to window */ + ctx->current = popup; + rect.x += win->layout->clip.x; + rect.y += win->layout->clip.y; + + /* setup popup data */ + popup->parent = win; + popup->bounds = rect; + popup->seq = ctx->seq; + popup->layout = layout; + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER|NK_WINDOW_SUB|NK_WINDOW_POPUP; + if (type == NK_POPUP_DYNAMIC) + popup->flags |= NK_WINDOW_DYNAMIC; + + popup->buffer = win->buffer; + nk_start_popup(ctx, win); + allocated = ctx->memory.allocated; + nk_push_scissor(&popup->buffer, nk_null_rect); + + if (nk_panel_begin(ctx, title)) { + /* popup is running therefore invalidate parent window */ + win->layout->flags |= NK_WINDOW_ROM; + win->layout->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + win->popup.active = 1; + layout->offset = &popup->scrollbar; + return 1; + } else { + /* popup was closed/is invalid so cleanup */ + win->layout->flags |= NK_WINDOW_REMOVE_ROM; + win->layout->popup_buffer.active = 0; + win->popup.active = 0; + ctx->memory.allocated = allocated; + ctx->current = win; + return 0; + } +} + +NK_INTERN int +nk_nonblock_begin(struct nk_panel *layout, struct nk_context *ctx, + nk_flags flags, struct nk_rect body, struct nk_rect header) +{ + struct nk_window *popup; + struct nk_window *win; + int is_active = nk_true; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* popups cannot have popups */ + win = ctx->current; + NK_ASSERT(!(win->flags & NK_WINDOW_POPUP)); + popup = win->popup.win; + if (!popup) { + /* create window for nonblocking popup */ + popup = (struct nk_window*)nk_create_window(ctx); + win->popup.win = popup; + nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON); + } else { + /* check if user clicked outside the popup and close if so */ + int in_panel, in_body, in_header; + in_panel = nk_input_is_mouse_click_in_rect(&ctx->input, NK_BUTTON_LEFT, win->layout->bounds); + in_body = nk_input_is_mouse_click_in_rect(&ctx->input, NK_BUTTON_LEFT, body); + in_header = nk_input_is_mouse_click_in_rect(&ctx->input, NK_BUTTON_LEFT, header); + if (!in_body && in_panel && !in_header) + is_active = nk_false; + } + + if (!is_active) { + win->layout->flags |= NK_WINDOW_REMOVE_ROM; + return is_active; + } + + popup->bounds = body; + popup->parent = win; + popup->layout = layout; + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER|NK_WINDOW_POPUP; + popup->flags |= NK_WINDOW_DYNAMIC|NK_WINDOW_SUB; + popup->flags |= NK_WINDOW_NONBLOCK; + popup->seq = ctx->seq; + win->popup.active = 1; + + nk_start_popup(ctx, win); + popup->buffer = win->buffer; + nk_push_scissor(&popup->buffer, nk_null_rect); + ctx->current = popup; + + nk_panel_begin(ctx, 0); + win->buffer = popup->buffer; + win->layout->flags |= NK_WINDOW_ROM; + layout->offset = &popup->scrollbar; + return is_active; +} + +NK_API void +nk_popup_close(struct nk_context *ctx) +{ + struct nk_window *popup; + NK_ASSERT(ctx); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + NK_ASSERT(popup->parent); + NK_ASSERT(popup->flags & NK_WINDOW_POPUP); + popup->flags |= NK_WINDOW_HIDDEN; +} + +NK_API void +nk_popup_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_window *popup; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + popup = ctx->current; + NK_ASSERT(popup->parent); + win = popup->parent; + if (popup->flags & NK_WINDOW_HIDDEN) { + win->layout->flags |= NK_WINDOW_REMOVE_ROM; + win->popup.active = 0; + } + nk_push_scissor(&popup->buffer, nk_null_rect); + nk_end(ctx); + + win->buffer = popup->buffer; + nk_finish_popup(ctx, win); + ctx->current = win; + nk_push_scissor(&win->buffer, win->layout->clip); +} +/* ------------------------------------------------------------- + * + * TOOLTIP + * + * -------------------------------------------------------------- */ +NK_API int +nk_tooltip_begin(struct nk_context *ctx, struct nk_panel *layout, float width) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect bounds; + int ret; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* make sure that no nonblocking popup is currently active */ + win = ctx->current; + in = &ctx->input; + if (win->popup.win && (win->popup.win->flags & NK_WINDOW_NONBLOCK)) + return 0; + + bounds.w = width; + bounds.h = nk_null_rect.h; + bounds.x = (in->mouse.pos.x + 1) - win->layout->clip.x; + bounds.y = (in->mouse.pos.y + 1) - win->layout->clip.y; + + ret = nk_popup_begin(ctx, layout, NK_POPUP_DYNAMIC, + "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_TOOLTIP, bounds); + if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + return ret; +} + +NK_API void +nk_tooltip_end(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + nk_popup_close(ctx); + nk_popup_end(ctx); +} + +NK_API void +nk_tooltip(struct nk_context *ctx, const char *text) +{ + const struct nk_style *style; + struct nk_vec2 padding; + struct nk_panel layout; + + int text_len; + float text_width; + float text_height; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(text); + if (!ctx || !ctx->current || !ctx->current->layout || !text) + return; + + /* fetch configuration data */ + style = &ctx->style; + padding = style->window.padding; + + /* calculate size of the text and tooltip */ + text_len = nk_strlen(text); + text_width = style->font.width(style->font.userdata, + style->font.height, text, text_len); + text_width += (4 * padding.x); + text_height = (style->font.height + 2 * padding.y); + + /* execute tooltip and fill with text */ + if (nk_tooltip_begin(ctx, &layout, (float)text_width)) { + nk_layout_row_dynamic(ctx, (float)text_height, 1); + nk_text(ctx, text, text_len, NK_TEXT_LEFT); + nk_tooltip_end(ctx); + } +} +/* ------------------------------------------------------------- + * + * CONTEXTUAL + * + * -------------------------------------------------------------- */ +NK_API int +nk_contextual_begin(struct nk_context *ctx, struct nk_panel *layout, + nk_flags flags, struct nk_vec2 size, struct nk_rect trigger_bounds) +{ + struct nk_window *win; + struct nk_window *popup; + struct nk_rect body; + + NK_STORAGE const struct nk_rect null_rect = {0,0,0,0}; + int is_clicked = 0; + int is_active = 0; + int is_open = 0; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + ++win->popup.con_count; + + /* check if currently active contextual is active */ + popup = win->popup.win; + is_open = (popup && (popup->flags & NK_WINDOW_CONTEXTUAL) && win->popup.type == NK_WINDOW_CONTEXTUAL); + is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds); + if (win->popup.active_con && win->popup.con_count != win->popup.active_con) + return 0; + if ((is_clicked && is_open && !is_active) || (!is_open && !is_active && !is_clicked)) + return 0; + + /* calculate contextual position on click */ + win->popup.active_con = win->popup.con_count; + if (is_clicked) { + body.x = ctx->input.mouse.pos.x; + body.y = ctx->input.mouse.pos.y; + } else { + body.x = popup->bounds.x; + body.y = popup->bounds.y; + } + body.w = size.x; + body.h = size.y; + + /* start nonblocking contextual popup */ + ret = nk_nonblock_begin(layout, ctx, + flags|NK_WINDOW_CONTEXTUAL|NK_WINDOW_NO_SCROLLBAR, body, null_rect); + if (ret) win->popup.type = NK_WINDOW_CONTEXTUAL; + else { + win->popup.active_con = 0; + win->popup.win->flags = 0; + } + return ret; +} + +NK_API int +nk_contextual_item_text(struct nk_context *ctx, const char *text, int len, + nk_flags alignment) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, &style->font)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_text(ctx, label, nk_strlen(label), align);} + +NK_API int +nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, + img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, &style->font, in)){ + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align);} + +NK_API int +nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, &style->font, in)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, nk_flags align) +{return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align);} + +NK_API void +nk_contextual_close(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + if (!ctx->current) + return; + nk_popup_close(ctx); +} + +NK_API void +nk_contextual_end(struct nk_context *ctx) +{ + struct nk_window *popup; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + popup = ctx->current; + NK_ASSERT(popup->parent); + if (popup->flags & NK_WINDOW_HIDDEN) + popup->seq = 0; + nk_popup_end(ctx); + return; +} +/* ------------------------------------------------------------- + * + * COMBO + * + * --------------------------------------------------------------*/ +NK_INTERN int +nk_combo_begin(struct nk_panel *layout, struct nk_context *ctx, struct nk_window *win, + int height, int is_clicked, struct nk_rect header) +{ + struct nk_window *popup; + int is_open = 0; + int is_active = 0; + struct nk_rect body; + nk_hash hash; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + popup = win->popup.win; + body.x = header.x; + body.w = header.w; + body.y = header.y + header.h-1; + body.h = (float)height; + + hash = win->popup.combo_count++; + is_open = (popup && (popup->flags & NK_WINDOW_COMBO)); + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_WINDOW_COMBO); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(layout, ctx, NK_WINDOW_COMBO, + body, nk_rect(0,0,0,0))) return 0; + + win->popup.type = NK_WINDOW_COMBO; + win->popup.name = hash; + return 1; +} + +NK_API int +nk_combo_begin_text(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, int len, int height) +{ + const struct nk_input *in; + struct nk_window *win; + struct nk_style *style; + + enum nk_widget_layout_states s; + int is_active = nk_false; + struct nk_rect header; + + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(selected); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !selected) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_active = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), style->combo.rounding, + background->data.color); + } + { + /* print currently selected text item */ + struct nk_rect label; + struct nk_rect button; + struct nk_rect content; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + sym = style->combo.sym_hover; + else if (is_active) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw selected label */ + text.padding = nk_vec2(0,0); + label.x = header.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = button.x - (style->combo.content_padding.x + style->combo.spacing.x) - label.x;; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, + NK_TEXT_LEFT, &ctx->style.font); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, &style->font); + } + return nk_combo_begin(layout, ctx, win, height, is_active, header); +} + +NK_API int nk_combo_begin_label(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, int max_height) +{return nk_combo_begin_text(ctx, layout, selected, nk_strlen(selected), max_height);} + +NK_API int +nk_combo_begin_color(struct nk_context *ctx, struct nk_panel *layout, + struct nk_color color, int height) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_active = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_active = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image); + } else { + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect bounds; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + sym = style->combo.sym_hover; + else if (is_active) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw color */ + bounds.h = header.h - 4 * style->combo.content_padding.y; + bounds.y = header.y + 2 * style->combo.content_padding.y; + bounds.x = header.x + 2 * style->combo.content_padding.x; + bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x; + nk_fill_rect(&win->buffer, bounds, 0, color); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, &style->font); + } + return nk_combo_begin(layout, ctx, win, height, is_active, header); +} + +NK_API int +nk_combo_begin_symbol(struct nk_context *ctx, struct nk_panel *layout, + enum nk_symbol_type symbol, int height) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_active = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color sym_background; + struct nk_color symbol_color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_active = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_hover; + } + + if (background->type == NK_STYLE_ITEM_IMAGE) { + sym_background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image); + } else { + sym_background = background->data.color; + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + sym = style->combo.sym_hover; + else if (is_active) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw symbol */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + nk_draw_symbol(&win->buffer, symbol, bounds, sym_background, symbol_color, + 1.0f, &style->font); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, &style->font); + } + return nk_combo_begin(layout, ctx, win, height, is_active, header); +} + +NK_API int +nk_combo_begin_symbol_text(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, int len, enum nk_symbol_type symbol, int height) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_active = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color symbol_color; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_active = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + sym = style->combo.sym_hover; + else if (is_active) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, &style->font); + + /* draw symbol */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_symbol(&win->buffer, symbol, image, text.background, symbol_color, + 1.0f, &style->font); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = (button.x - style->combo.content_padding.x) - label.x; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, &style->font); + } + return nk_combo_begin(layout, ctx, win, height, is_active, header); +} + +NK_API int +nk_combo_begin_image(struct nk_context *ctx, struct nk_panel *layout, + struct nk_image img, int height) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_active = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_active = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image); + } else { + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + sym = style->combo.sym_hover; + else if (is_active) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw image */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + nk_draw_image(&win->buffer, bounds, &img); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, &style->font); + } + return nk_combo_begin(layout, ctx, win, height, is_active, header); +} + +NK_API int +nk_combo_begin_image_text(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, int len, struct nk_image img, int height) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_active = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_active = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVERED) + sym = style->combo.sym_hover; + else if (is_active) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, &style->font); + + /* draw image */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_image(&win->buffer, image, &img); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = (button.x - style->combo.content_padding.x) - label.x; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, &style->font); + } + return nk_combo_begin(layout, ctx, win, height, is_active, header); +} + +NK_API int nk_combo_begin_symbol_label(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, enum nk_symbol_type type, int height) +{return nk_combo_begin_symbol_text(ctx, layout, selected, nk_strlen(selected), type, height);} + +NK_API int nk_combo_begin_image_label(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, struct nk_image img, int height) +{return nk_combo_begin_image_text(ctx, layout, selected, nk_strlen(selected), img, height);} + +NK_API int nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align) +{return nk_contextual_item_text(ctx, text, len, align);} + +NK_API int nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_label(ctx, label, align);} + +NK_API int nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, + int len, nk_flags alignment) +{return nk_contextual_item_image_text(ctx, img, text, len, alignment);} + +NK_API int nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *text, nk_flags alignment) +{return nk_contextual_item_image_label(ctx, img, text, alignment);} + +NK_API int nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags alignment) +{return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment);} + +NK_API int nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags alignment) +{return nk_contextual_item_symbol_label(ctx, sym, label, alignment);} + +NK_API void nk_combo_end(struct nk_context *ctx) +{nk_contextual_end(ctx);} + +NK_API void nk_combo_close(struct nk_context *ctx) +{nk_contextual_close(ctx);} + +NK_API int +nk_combo(struct nk_context *ctx, const char **items, int count, + int selected, int item_height) +{ + int i = 0; + int max_height; + struct nk_panel combo; + float item_padding; + float window_padding; + + NK_ASSERT(ctx); + NK_ASSERT(items); + if (!ctx || !items ||!count) + return selected; + + item_padding = ctx->style.combo.button_padding.y; + window_padding = ctx->style.window.padding.y; + max_height = (count+1) * item_height + (int)item_padding * 3 + (int)window_padding * 2; + if (nk_combo_begin_label(ctx, &combo, items[selected], max_height)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API int +nk_combo_seperator(struct nk_context *ctx, const char *items_seperated_by_seperator, + int seperator, int selected, int count, int item_height) +{ + int i; + int max_height; + struct nk_panel combo; + float item_padding; + float window_padding; + const char *current_item; + const char *iter; + int length = 0; + + NK_ASSERT(ctx); + NK_ASSERT(items_seperated_by_seperator); + if (!ctx || !items_seperated_by_seperator) + return selected; + + /* calculate popup window */ + item_padding = ctx->style.combo.content_padding.y; + window_padding = ctx->style.window.padding.y; + max_height = (count+1) * item_height + (int)item_padding * 3 + (int)window_padding * 2; + + /* find selected item */ + current_item = items_seperated_by_seperator; + for (i = 0; i < selected; ++i) { + iter = current_item; + while (*iter != seperator) iter++; + length = (int)(iter - current_item); + current_item = iter + 1; + } + + if (nk_combo_begin_text(ctx, &combo, current_item, length, max_height)) { + current_item = items_seperated_by_seperator; + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + iter = current_item; + while (*iter != seperator) iter++; + length = (int)(iter - current_item); + if (nk_combo_item_text(ctx, current_item, length, NK_TEXT_LEFT)) + selected = i; + current_item = current_item + length + 1; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API int +nk_combo_string(struct nk_context *ctx, const char *items_seperated_by_zeros, + int selected, int count, int item_height) +{return nk_combo_seperator(ctx, items_seperated_by_zeros, '\0', selected, count, item_height);} + +NK_API int +nk_combo_callback(struct nk_context *ctx, void(item_getter)(void*, int, const char**), + void *userdata, int selected, int count, int item_height) +{ + int i; + int max_height; + struct nk_panel combo; + float item_padding; + float window_padding; + const char *item; + + NK_ASSERT(ctx); + NK_ASSERT(item_getter); + if (!ctx || !item_getter) + return selected; + + /* calculate popup window */ + item_padding = ctx->style.combo.content_padding.y; + window_padding = ctx->style.window.padding.y; + max_height = (count+1) * item_height + (int)item_padding * 3 + (int)window_padding * 2; + + item_getter(userdata, selected, &item); + if (nk_combo_begin_label(ctx, &combo, item, max_height)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + item_getter(userdata, i, &item); + if (nk_combo_item_label(ctx, item, NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API void nk_combobox(struct nk_context *ctx, const char **items, int count, + int *selected, int item_height) +{*selected = nk_combo(ctx, items, count, *selected, item_height);} + +NK_API void nk_combobox_string(struct nk_context *ctx, const char *items_seperated_by_zeros, + int *selected, int count, int item_height) +{*selected = nk_combo_string(ctx, items_seperated_by_zeros, *selected, count, item_height);} + +NK_API void nk_combobox_seperator(struct nk_context *ctx, const char *items_seperated_by_seperator, + int seperator,int *selected, int count, int item_height) +{*selected = nk_combo_seperator(ctx, items_seperated_by_seperator, seperator, + *selected, count, item_height);} + +NK_API void nk_combobox_callback(struct nk_context *ctx, + void(item_getter)(void* data, int id, const char **out_text), + void *userdata, int *selected, int count, int item_height) +{*selected = nk_combo_callback(ctx, item_getter, userdata, *selected, count, item_height);} + +/* + * ------------------------------------------------------------- + * + * MENU + * + * -------------------------------------------------------------- + */ +NK_INTERN int +nk_menu_begin(struct nk_panel *layout, struct nk_context *ctx, struct nk_window *win, + const char *id, int is_clicked, struct nk_rect header, float width) +{ + int is_open = 0; + int is_active = 0; + struct nk_rect body; + struct nk_window *popup; + nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_WINDOW_MENU); + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + body.x = header.x; + body.w = width; + body.y = header.y + header.h; + body.h = (win->layout->bounds.y + win->layout->bounds.h) - body.y; + + popup = win->popup.win; + is_open = (popup && (popup->flags & NK_WINDOW_MENU)); + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_WINDOW_MENU); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(layout, ctx, NK_WINDOW_MENU|NK_WINDOW_NO_SCROLLBAR, body, header)) + return 0; + win->popup.type = NK_WINDOW_MENU; + win->popup.name = hash; + return 1; +} + +NK_API int +nk_menu_begin_text(struct nk_context *ctx, struct nk_panel *layout, + const char *title, int len, nk_flags align, float width) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header, + title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, &ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, title, is_clicked, header, width); +} + +NK_API int nk_menu_begin_label(struct nk_context *ctx, struct nk_panel *layout, + const char *text, nk_flags align, float width) +{return nk_menu_begin_text(ctx, layout, text, nk_strlen(text), align, width);} + +NK_API int +nk_menu_begin_image(struct nk_context *ctx, struct nk_panel *layout, + const char *id, struct nk_image img, float width) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header, + img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, id, is_clicked, header, width); +} + +NK_API int +nk_menu_begin_symbol(struct nk_context *ctx, struct nk_panel *layout, + const char *id, enum nk_symbol_type sym, float width) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, + sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, &ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, id, is_clicked, header, width); +} + +NK_API int +nk_menu_begin_image_text(struct nk_context *ctx, struct nk_panel *layout, + const char *title, int len, nk_flags align, struct nk_image img, float width) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + &ctx->style.font, in)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, title, is_clicked, header, width); +} + +NK_API int nk_menu_begin_image_label(struct nk_context *ctx, struct nk_panel *layout, + const char *title, nk_flags align, struct nk_image img, float width) +{return nk_menu_begin_image_text(ctx, layout, title, nk_strlen(title), align, img, width);} + +NK_API int +nk_menu_begin_symbol_text(struct nk_context *ctx, struct nk_panel *layout, + const char *title, int size, nk_flags align, enum nk_symbol_type sym, float width) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, + header, sym, title, size, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + &ctx->style.font, in)) is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, title, is_clicked, header, width); +} + +NK_API int nk_menu_begin_symbol_label(struct nk_context *ctx, struct nk_panel *layout, + const char *title, nk_flags align, enum nk_symbol_type sym, float width) +{return nk_menu_begin_symbol_text(ctx, layout, title, nk_strlen(title), align,sym, width);} + +NK_API int nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align) +{return nk_contextual_item_text(ctx, title, len, align);} + +NK_API int nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_label(ctx, label, align);} + +NK_API int nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_contextual_item_image_label(ctx, img, label, align);} + +NK_API int nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{return nk_contextual_item_image_text(ctx, img, text, len, align);} + +NK_API int nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags align) +{return nk_contextual_item_symbol_text(ctx, sym, text, len, align);} + +NK_API int nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags align) +{return nk_contextual_item_symbol_label(ctx, sym, label, align);} + +NK_API void nk_menu_close(struct nk_context *ctx) +{nk_contextual_close(ctx);} + +NK_API void +nk_menu_end(struct nk_context *ctx) +{nk_contextual_end(ctx);} + +#endif diff --git a/stb_rect_pack.h b/stb_rect_pack.h deleted file mode 100644 index a9225d4..0000000 --- a/stb_rect_pack.h +++ /dev/null @@ -1,545 +0,0 @@ -/* stb_rect_pack.h - v0.05 - public domain - rectangle packing */ -/* Sean Barrett 2014 */ -/* */ -/* Useful for e.g. packing rectangular textures into an atlas. */ -/* Does not do rotation. */ -/* */ -/* Not necessarily the awesomest packing method, but better than */ -/* the totally naive one in stb_truetype (which is primarily what */ -/* this is meant to replace). */ -/* */ -/* Has only had a few tests run, may have issues. */ -/* */ -/* More docs to come. */ -/* */ -/* No memory allocations; uses qsort() and assert() from stdlib. */ -/* */ -/* This library currently uses the Skyline Bottom-Left algorithm. */ -/* */ -/* Please note: better rectangle packers are welcome! Please */ -/* implement them to the same API, but with a different init */ -/* function. */ -/* */ -/* Version history: */ -/* */ -/* 0.05: added STBRP_ASSERT to allow replacing assert */ -/* 0.04: fixed minor bug in STBRP_LARGE_RECTS support */ -/* 0.01: initial release */ - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* INCLUDE SECTION */ -#ifndef STB_INCLUDE_STB_RECT_PACK_H -#define STB_INCLUDE_STB_RECT_PACK_H - -#define STB_RECT_PACK_VERSION 1 - -#ifdef STBRP_STATIC -#define STBRP_DEF static -#else -#define STBRP_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct stbrp_context stbrp_context; -typedef struct stbrp_node stbrp_node; -typedef struct stbrp_rect stbrp_rect; - -#ifdef STBRP_LARGE_RECTS -typedef int stbrp_coord; -#else -typedef unsigned short stbrp_coord; -#endif - -STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); -/* Assign packed locations to rectangles. The rectangles are of type */ -/* 'stbrp_rect' defined below, stored in the array 'rects', and there */ -/* are 'num_rects' many of them. */ -/* */ -/* Rectangles which are successfully packed have the 'was_packed' flag */ -/* set to a non-zero value and 'x' and 'y' store the minimum location */ -/* on each axis (i.e. bottom-left in cartesian coordinates, top-left */ -/* if you imagine y increasing downwards). Rectangles which do not fit */ -/* have the 'was_packed' flag set to 0. */ -/* */ -/* You should not try to access the 'rects' array from another thread */ -/* while this function is running, as the function temporarily reorders */ -/* the array while it executes. */ -/* */ -/* To pack into another rectangle, you need to call stbrp_init_target */ -/* again. To continue packing into the same rectangle, you can call */ -/* this function again. Calling this multiple times with multiple rect */ -/* arrays will probably produce worse packing results than calling it */ -/* a single time with the full rectangle array, but the option is */ -/* available. */ - -struct stbrp_rect -{ - /* reserved for your use: */ - int id; - - /* input: */ - stbrp_coord w, h; - - /* output: */ - stbrp_coord x, y; - int was_packed; /* non-zero if valid packing */ - -}; /* 16 bytes, nominally */ - - -STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); -/* Initialize a rectangle packer to: */ -/* pack a rectangle that is 'width' by 'height' in dimensions */ -/* using temporary storage provided by the array 'nodes', which is 'num_nodes' long */ -/* */ -/* You must call this function every time you start packing into a new target. */ -/* */ -/* There is no "shutdown" function. The 'nodes' memory must stay valid for */ -/* the following stbrp_pack_rects() call (or calls), but can be freed after */ -/* the call (or calls) finish. */ -/* */ -/* Note: to guarantee best results, either: */ -/* 1. make sure 'num_nodes' >= 'width' */ -/* or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' */ -/* */ -/* If you don't do either of the above things, widths will be quantized to multiples */ -/* of small integers to guarantee the algorithm doesn't run out of temporary storage. */ -/* */ -/* If you do #2, then the non-quantized algorithm will be used, but the algorithm */ -/* may run out of temporary storage and be unable to pack some rectangles. */ - -STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); -/* Optionally call this function after init but before doing any packing to */ -/* change the handling of the out-of-temp-memory scenario, described above. */ -/* If you call init again, this will be reset to the default (false). */ - - -STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); -/* Optionally select which packing heuristic the library should use. Different */ -/* heuristics will produce better/worse results for different data sets. */ -/* If you call init again, this will be reset to the default. */ - -enum -{ - STBRP_HEURISTIC_Skyline_default=0, - STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, - STBRP_HEURISTIC_Skyline_BF_sortHeight -}; - - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* the details of the following structures don't matter to you, but they must */ -/* be visible so you can handle the memory allocations for them */ - -struct stbrp_node -{ - stbrp_coord x,y; - stbrp_node *next; -}; - -struct stbrp_context -{ - int width; - int height; - int align; - int init_mode; - int heuristic; - int num_nodes; - stbrp_node *active_head; - stbrp_node *free_head; - stbrp_node extra[2]; /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */ -}; - -#ifdef __cplusplus -} -#endif - -#endif - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* IMPLEMENTATION SECTION */ -/* */ - -#ifdef STB_RECT_PACK_IMPLEMENTATION -#include - -#ifndef STBRP_ASSERT -#include -#define STBRP_ASSERT assert -#endif - -enum -{ - STBRP__INIT_skyline = 1 -}; - -STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) -{ - switch (context->init_mode) { - case STBRP__INIT_skyline: - STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); - context->heuristic = heuristic; - break; - default: - STBRP_ASSERT(0); - } -} - -STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) -{ - if (allow_out_of_mem) - /* if it's ok to run out of memory, then don't bother aligning them; */ - /* this gives better packing, but may fail due to OOM (even though */ - /* the rectangles easily fit). @TODO a smarter approach would be to only */ - /* quantize once we've hit OOM, then we could get rid of this parameter. */ - context->align = 1; - else { - /* if it's not ok to run out of memory, then quantize the widths */ - /* so that num_nodes is always enough nodes. */ - /* */ - /* I.e. num_nodes * align >= width */ - /* align >= width / num_nodes */ - /* align = ceil(width/num_nodes) */ - - context->align = (context->width + context->num_nodes-1) / context->num_nodes; - } -} - -STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) -{ - int i; -#ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(width <= 0xffff && height <= 0xffff); -#endif - - for (i=0; i < num_nodes-1; ++i) - nodes[i].next = &nodes[i+1]; - nodes[i].next = NULL; - context->init_mode = STBRP__INIT_skyline; - context->heuristic = STBRP_HEURISTIC_Skyline_default; - context->free_head = &nodes[0]; - context->active_head = &context->extra[0]; - context->width = width; - context->height = height; - context->num_nodes = num_nodes; - stbrp_setup_allow_out_of_mem(context, 0); - - /* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */ - context->extra[0].x = 0; - context->extra[0].y = 0; - context->extra[0].next = &context->extra[1]; - context->extra[1].x = (stbrp_coord) width; -#ifdef STBRP_LARGE_RECTS - context->extra[1].y = (1<<30); -#else - context->extra[1].y = 65535; -#endif - context->extra[1].next = NULL; -} - -/* find minimum y position if it starts at x1 */ -static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) -{ - stbrp_node *node = first; - int x1 = x0 + width; - int min_y, visited_width, waste_area; - STBRP_ASSERT(first->x <= x0); - (void)c; - - #if 0 - /* skip in case we're past the node */ - while (node->next->x <= x0) - ++node; - #else - STBRP_ASSERT(node->next->x > x0); /* we ended up handling this in the caller for efficiency */ - #endif - - STBRP_ASSERT(node->x <= x0); - - min_y = 0; - waste_area = 0; - visited_width = 0; - while (node->x < x1) { - if (node->y > min_y) { - /* raise min_y higher. */ - /* we've accounted for all waste up to min_y, */ - /* but we'll now add more waste for everything we've visted */ - waste_area += visited_width * (node->y - min_y); - min_y = node->y; - /* the first time through, visited_width might be reduced */ - if (node->x < x0) - visited_width += node->next->x - x0; - else - visited_width += node->next->x - node->x; - } else { - /* add waste area */ - int under_width = node->next->x - node->x; - if (under_width + visited_width > width) - under_width = width - visited_width; - waste_area += under_width * (min_y - node->y); - visited_width += under_width; - } - node = node->next; - } - - *pwaste = waste_area; - return min_y; -} - -typedef struct -{ - int x,y; - stbrp_node **prev_link; -} stbrp__findresult; - -static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) -{ - int best_waste = (1<<30), best_x, best_y = (1 << 30); - stbrp__findresult fr; - stbrp_node **prev, *node, *tail, **best = NULL; - - /* align to multiple of c->align */ - width = (width + c->align - 1); - width -= width % c->align; - STBRP_ASSERT(width % c->align == 0); - - node = c->active_head; - prev = &c->active_head; - while (node->x + width <= c->width) { - int y,waste; - y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); - if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { /* actually just want to test BL */ - /* bottom left */ - if (y < best_y) { - best_y = y; - best = prev; - } - } else { - /* best-fit */ - if (y + height <= c->height) { - /* can only use it if it first vertically */ - if (y < best_y || (y == best_y && waste < best_waste)) { - best_y = y; - best_waste = waste; - best = prev; - } - } - } - prev = &node->next; - node = node->next; - } - - best_x = (best == NULL) ? 0 : (*best)->x; - - /* if doing best-fit (BF), we also have to try aligning right edge to each node position */ - /* */ - /* e.g, if fitting */ - /* */ - /* ____________________ */ - /* |____________________| */ - /* */ - /* into */ - /* */ - /* | | */ - /* | ____________| */ - /* |____________| */ - /* */ - /* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */ - /* */ - /* This makes BF take about 2x the time */ - - if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { - tail = c->active_head; - node = c->active_head; - prev = &c->active_head; - /* find first node that's admissible */ - while (tail->x < width) - tail = tail->next; - while (tail) { - int xpos = tail->x - width; - int y,waste; - STBRP_ASSERT(xpos >= 0); - /* find the left position that matches this */ - while (node->next->x <= xpos) { - prev = &node->next; - node = node->next; - } - STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); - y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); - if (y + height < c->height) { - if (y <= best_y) { - if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { - best_x = xpos; - STBRP_ASSERT(y <= best_y); - best_y = y; - best_waste = waste; - best = prev; - } - } - } - tail = tail->next; - } - } - - fr.prev_link = best; - fr.x = best_x; - fr.y = best_y; - return fr; -} - -static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) -{ - /* find best position according to heuristic */ - stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); - stbrp_node *node, *cur; - - /* bail if: */ - /* 1. it failed */ - /* 2. the best node doesn't fit (we don't always check this) */ - /* 3. we're out of memory */ - if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { - res.prev_link = NULL; - return res; - } - - /* on success, create new node */ - node = context->free_head; - node->x = (stbrp_coord) res.x; - node->y = (stbrp_coord) (res.y + height); - - context->free_head = node->next; - - /* insert the new node into the right starting point, and */ - /* let 'cur' point to the remaining nodes needing to be */ - /* stiched back in */ - - cur = *res.prev_link; - if (cur->x < res.x) { - /* preserve the existing one, so start testing with the next one */ - stbrp_node *next = cur->next; - cur->next = node; - cur = next; - } else { - *res.prev_link = node; - } - - /* from here, traverse cur and free the nodes, until we get to one */ - /* that shouldn't be freed */ - while (cur->next && cur->next->x <= res.x + width) { - stbrp_node *next = cur->next; - /* move the current node to the free list */ - cur->next = context->free_head; - context->free_head = cur; - cur = next; - } - - /* stitch the list back in */ - node->next = cur; - - if (cur->x < res.x + width) - cur->x = (stbrp_coord) (res.x + width); - -#ifdef _DEBUG - cur = context->active_head; - while (cur->x < context->width) { - STBRP_ASSERT(cur->x < cur->next->x); - cur = cur->next; - } - STBRP_ASSERT(cur->next == NULL); - - { - stbrp_node *L1 = NULL, *L2 = NULL; - int count=0; - cur = context->active_head; - while (cur) { - L1 = cur; - cur = cur->next; - ++count; - } - cur = context->free_head; - while (cur) { - L2 = cur; - cur = cur->next; - ++count; - } - STBRP_ASSERT(count == context->num_nodes+2); - } -#endif - - return res; -} - -static int rect_height_compare(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - if (p->h > q->h) - return -1; - if (p->h < q->h) - return 1; - return (p->w > q->w) ? -1 : (p->w < q->w); -} - -static int rect_width_compare(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - if (p->w > q->w) - return -1; - if (p->w < q->w) - return 1; - return (p->h > q->h) ? -1 : (p->h < q->h); -} - -static int rect_original_order(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); -} - -#ifdef STBRP_LARGE_RECTS -#define STBRP__MAXVAL 0xffffffff -#else -#define STBRP__MAXVAL 0xffff -#endif - -STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) -{ - int i; - - /* we use the 'was_packed' field internally to allow sorting/unsorting */ - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = i; - #ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); - #endif - } - - /* sort according to heuristic */ - qsort(rects, (size_t)num_rects, sizeof(rects[0]), rect_height_compare); - - for (i=0; i < num_rects; ++i) { - stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); - if (fr.prev_link) { - rects[i].x = (stbrp_coord) fr.x; - rects[i].y = (stbrp_coord) fr.y; - } else { - rects[i].x = rects[i].y = STBRP__MAXVAL; - } - } - - /* unsort */ - qsort(rects, (size_t)num_rects, sizeof(rects[0]), rect_original_order); - - /* set was_packed flags */ - for (i=0; i < num_rects; ++i) - rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); -} -#endif diff --git a/stb_truetype.h b/stb_truetype.h deleted file mode 100644 index a3cb2c5..0000000 --- a/stb_truetype.h +++ /dev/null @@ -1,3218 +0,0 @@ -/* stb_truetype.h - v1.07 - public domain */ -/* authored from 2009-2015 by Sean Barrett / RAD Game Tools */ -/* */ -/* This library processes TrueType files: */ -/* parse files */ -/* extract glyph metrics */ -/* extract glyph shapes */ -/* render glyphs to one-channel bitmaps with antialiasing (box filter) */ -/* */ -/* Todo: */ -/* non-MS cmaps */ -/* crashproof on bad data */ -/* hinting? (no longer patented) */ -/* cleartype-style AA? */ -/* optimize: use simple memory allocator for intermediates */ -/* optimize: build edge-list directly from curves */ -/* optimize: rasterize directly from curves? */ -/* */ -/* ADDITIONAL CONTRIBUTORS */ -/* */ -/* Mikko Mononen: compound shape support, more cmap formats */ -/* Tor Andersson: kerning, subpixel rendering */ -/* */ -/* Bug/warning reports/fixes: */ -/* "Zer" on mollyrocket (with fix) */ -/* Cass Everitt */ -/* stoiko (Haemimont Games) */ -/* Brian Hook */ -/* Walter van Niftrik */ -/* David Gow */ -/* David Given */ -/* Ivan-Assen Ivanov */ -/* Anthony Pesch */ -/* Johan Duparc */ -/* Hou Qiming */ -/* Fabian "ryg" Giesen */ -/* Martins Mozeiko */ -/* Cap Petschulat */ -/* Omar Cornut */ -/* github:aloucks */ -/* Peter LaValle */ -/* Giumo X. Clanjor */ -/* */ -/* Misc other: */ -/* Ryan Gordon */ -/* */ -/* VERSION HISTORY */ -/* */ -/* 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; */ -/* variant PackFontRanges to pack and render in separate phases; */ -/* fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); */ -/* fixed an assert() bug in the new rasterizer */ -/* replace assert() with STBTT_assert() in new rasterizer */ -/* 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) */ -/* also more precise AA rasterizer, except if shapes overlap */ -/* remove need for STBTT_sort */ -/* 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC */ -/* 1.04 (2015-04-15) typo in example */ -/* 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes */ -/* */ -/* Full history can be found at the end of this file. */ -/* */ -/* LICENSE */ -/* */ -/* This software is in the public domain. Where that dedication is not */ -/* recognized, you are granted a perpetual, irrevocable license to copy, */ -/* distribute, and modify this file as you see fit. */ -/* */ -/* USAGE */ -/* */ -/* Include this file in whatever places neeed to refer to it. In ONE C/C++ */ -/* file, write: */ -/* #define STB_TRUETYPE_IMPLEMENTATION */ -/* before the #include of this file. This expands out the actual */ -/* implementation into that C/C++ file. */ -/* */ -/* To make the implementation private to the file that generates the implementation, */ -/* #define STBTT_STATIC */ -/* */ -/* Simple 3D API (don't ship this, but it's fine for tools and quick start) */ -/* stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture */ -/* stbtt_GetBakedQuad() -- compute quad to draw for a given char */ -/* */ -/* Improved 3D API (more shippable): */ -/* #include "stb_rect_pack.h" -- optional, but you really want it */ -/* stbtt_PackBegin() */ -/* stbtt_PackSetOversample() -- for improved quality on small fonts */ -/* stbtt_PackFontRanges() -- pack and renders */ -/* stbtt_PackEnd() */ -/* stbtt_GetPackedQuad() */ -/* */ -/* "Load" a font file from a memory buffer (you have to keep the buffer loaded) */ -/* stbtt_InitFont() */ -/* stbtt_GetFontOffsetForIndex() -- use for TTC font collections */ -/* */ -/* Render a unicode codepoint to a bitmap */ -/* stbtt_GetCodepointBitmap() -- allocates and returns a bitmap */ -/* stbtt_MakeCodepointBitmap() -- renders into bitmap you provide */ -/* stbtt_GetCodepointBitmapBox() -- how big the bitmap must be */ -/* */ -/* Character advance/positioning */ -/* stbtt_GetCodepointHMetrics() */ -/* stbtt_GetFontVMetrics() */ -/* stbtt_GetCodepointKernAdvance() */ -/* */ -/* Starting with version 1.06, the rasterizer was replaced with a new, */ -/* faster and generally-more-precise rasterizer. The new rasterizer more */ -/* accurately measures pixel coverage for anti-aliasing, except in the case */ -/* where multiple shapes overlap, in which case it overestimates the AA pixel */ -/* coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If */ -/* this turns out to be a problem, you can re-enable the old rasterizer with */ -/* #define STBTT_RASTERIZER_VERSION 1 */ -/* which will incur about a 15% speed hit. */ -/* */ -/* ADDITIONAL DOCUMENTATION */ -/* */ -/* Immediately after this block comment are a series of sample programs. */ -/* */ -/* After the sample programs is the "header file" section. This section */ -/* includes documentation for each API function. */ -/* */ -/* Some important concepts to understand to use this library: */ -/* */ -/* Codepoint */ -/* Characters are defined by unicode codepoints, e.g. 65 is */ -/* uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is */ -/* the hiragana for "ma". */ -/* */ -/* Glyph */ -/* A visual character shape (every codepoint is rendered as */ -/* some glyph) */ -/* */ -/* Glyph index */ -/* A font-specific integer ID representing a glyph */ -/* */ -/* Baseline */ -/* Glyph shapes are defined relative to a baseline, which is the */ -/* bottom of uppercase characters. Characters extend both above */ -/* and below the baseline. */ -/* */ -/* Current Point */ -/* As you draw text to the screen, you keep track of a "current point" */ -/* which is the origin of each character. The current point's vertical */ -/* position is the baseline. Even "baked fonts" use this model. */ -/* */ -/* Vertical Font Metrics */ -/* The vertical qualities of the font, used to vertically position */ -/* and space the characters. See docs for stbtt_GetFontVMetrics. */ -/* */ -/* Font Size in Pixels or Points */ -/* The preferred interface for specifying font sizes in stb_truetype */ -/* is to specify how tall the font's vertical extent should be in pixels. */ -/* If that sounds good enough, skip the next paragraph. */ -/* */ -/* Most font APIs instead use "points", which are a common typographic */ -/* measurement for describing font size, defined as 72 points per inch. */ -/* stb_truetype provides a point API for compatibility. However, true */ -/* "per inch" conventions don't make much sense on computer displays */ -/* since they different monitors have different number of pixels per */ -/* inch. For example, Windows traditionally uses a convention that */ -/* there are 96 pixels per inch, thus making 'inch' measurements have */ -/* nothing to do with inches, and thus effectively defining a point to */ -/* be 1.333 pixels. Additionally, the TrueType font data provides */ -/* an explicit scale factor to scale a given font's glyphs to points, */ -/* but the author has observed that this scale factor is often wrong */ -/* for non-commercial fonts, thus making fonts scaled in points */ -/* according to the TrueType spec incoherently sized in practice. */ -/* */ -/* ADVANCED USAGE */ -/* */ -/* Quality: */ -/* */ -/* - Use the functions with Subpixel at the end to allow your characters */ -/* to have subpixel positioning. Since the font is anti-aliased, not */ -/* hinted, this is very import for quality. (This is not possible with */ -/* baked fonts.) */ -/* */ -/* - Kerning is now supported, and if you're supporting subpixel rendering */ -/* then kerning is worth using to give your text a polished look. */ -/* */ -/* Performance: */ -/* */ -/* - Convert Unicode codepoints to glyph indexes and operate on the glyphs; */ -/* if you don't do this, stb_truetype is forced to do the conversion on */ -/* every call. */ -/* */ -/* - There are a lot of memory allocations. We should modify it to take */ -/* a temp buffer and allocate from the temp buffer (without freeing), */ -/* should help performance a lot. */ -/* */ -/* NOTES */ -/* */ -/* The system uses the raw data found in the .ttf file without changing it */ -/* and without building auxiliary data structures. This is a bit inefficient */ -/* on little-endian systems (the data is big-endian), but assuming you're */ -/* caching the bitmaps or glyph shapes this shouldn't be a big deal. */ -/* */ -/* It appears to be very hard to programmatically determine what font a */ -/* given file is in a general way. I provide an API for this, but I don't */ -/* recommend it. */ -/* */ -/* */ -/* SOURCE STATISTICS (based on v0.6c, 2050 LOC) */ -/* */ -/* Documentation & header file 520 LOC \___ 660 LOC documentation */ -/* Sample code 140 LOC / */ -/* Truetype parsing 620 LOC ---- 620 LOC TrueType */ -/* Software rasterization 240 LOC \ . */ -/* Curve tesselation 120 LOC \__ 550 LOC Bitmap creation */ -/* Bitmap management 100 LOC / */ -/* Baked bitmap interface 70 LOC / */ -/* Font name matching & access 150 LOC ---- 150 */ -/* C runtime library abstraction 60 LOC ---- 60 */ -/* */ -/* */ -/* PERFORMANCE MEASUREMENTS FOR 1.06: */ -/* */ -/* 32-bit 64-bit */ -/* Previous release: 8.83 s 7.68 s */ -/* Pool allocations: 7.72 s 6.34 s */ -/* Inline sort : 6.54 s 5.65 s */ -/* New rasterizer : 5.63 s 5.00 s */ - -/*//////////////////////////////////////////////////////////////////////////// */ -/*//////////////////////////////////////////////////////////////////////////// */ -/*// */ -/*// SAMPLE PROGRAMS */ -/*// */ -/* */ -/* Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless */ -/* */ -#if 0 -#define STB_TRUETYPE_IMPLEMENTATION /* force following include to generate implementation */ -#include "stb_truetype.h" - -unsigned char ttf_buffer[1<<20]; -unsigned char temp_bitmap[512*512]; - -stbtt_bakedchar cdata[96]; /* ASCII 32..126 is 95 glyphs */ -GLuint ftex; - -void my_stbtt_initfont(void) -{ - fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); - stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); /* no guarantee this fits! */ - /* can free ttf_buffer at this point */ - glGenTextures(1, &ftex); - glBindTexture(GL_TEXTURE_2D, ftex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); - /* can free temp_bitmap at this point */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -} - -void my_stbtt_print(float x, float y, char *text) -{ - /* assume orthographic projection with units = screen pixels, origin at top left */ - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, ftex); - glBegin(GL_QUADS); - while (*text) { - if (*text >= 32 && *text < 128) { - stbtt_aligned_quad q; - stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);/*1=opengl & d3d10+,0=d3d9 */ - glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); - glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); - glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); - glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); - } - ++text; - } - glEnd(); -} -#endif -/* */ -/* */ -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* Complete program (this compiles): get a single bitmap, print as ASCII art */ -/* */ -#if 0 -#include -#define STB_TRUETYPE_IMPLEMENTATION /* force following include to generate implementation */ -#include "stb_truetype.h" - -char ttf_buffer[1<<25]; - -int main(int argc, char **argv) -{ - stbtt_fontinfo font; - unsigned char *bitmap; - int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); - - fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); - - stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); - bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); - - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) - putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); - putchar('\n'); - } - return 0; -} -#endif -/* */ -/* Output: */ -/* */ -/* .ii. */ -/* @@@@@@. */ -/* V@Mio@@o */ -/* :i. V@V */ -/* :oM@@M */ -/* :@@@MM@M */ -/* @@o o@M */ -/* :@@. M@M */ -/* @@@o@@@@ */ -/* :M@@V:@@. */ -/* */ -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* Complete program: print "Hello World!" banner, with bugs */ -/* */ -#if 0 -char buffer[24<<20]; -unsigned char screen[20][79]; - -int main(int arg, char **argv) -{ - stbtt_fontinfo font; - int i,j,ascent,baseline,ch=0; - float scale, xpos=2; /* leave a little padding in case the character extends left */ - char *text = "Heljo World!"; /* intentionally misspelled to show 'lj' brokenness */ - - fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); - stbtt_InitFont(&font, buffer, 0); - - scale = stbtt_ScaleForPixelHeight(&font, 15); - stbtt_GetFontVMetrics(&font, &ascent,0,0); - baseline = (int) (ascent*scale); - - while (text[ch]) { - int advance,lsb,x0,y0,x1,y1; - float x_shift = xpos - (float) floor(xpos); - stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); - stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); - stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); - /* note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong */ - /* because this API is really for baking character bitmaps into textures. if you want to render */ - /* a sequence of characters, you really need to render each bitmap to a temp buffer, then */ - /* "alpha blend" that into the working buffer */ - xpos += (advance * scale); - if (text[ch+1]) - xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); - ++ch; - } - - for (j=0; j < 20; ++j) { - for (i=0; i < 78; ++i) - putchar(" .:ioVM@"[screen[j][i]>>5]); - putchar('\n'); - } - - return 0; -} -#endif - -/*//////////////////////////////////////////////////////////////////////////// */ -/*//////////////////////////////////////////////////////////////////////////// */ -/*// */ -/*// INTEGRATION WITH YOUR CODEBASE */ -/*// */ -/*// The following sections allow you to supply alternate definitions */ -/*// of C library functions used by stb_truetype. */ -#ifdef STB_TRUETYPE_IMPLEMENTATION - /* #define your own (u)stbtt_int8/16/32 before including to override this */ - #ifndef stbtt_uint8 - typedef unsigned char stbtt_uint8; - typedef signed char stbtt_int8; - typedef unsigned short stbtt_uint16; - typedef signed short stbtt_int16; - typedef unsigned int stbtt_uint32; - typedef signed int stbtt_int32; - #endif - - typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; - typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; - - /* #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h */ - #ifndef STBTT_ifloor - #include - #define STBTT_ifloor(x) ((int) floor(x)) - #define STBTT_iceil(x) ((int) ceil(x)) - #endif - - #ifndef STBTT_sqrt - #include - #define STBTT_sqrt(x) sqrt(x) - #endif - - /* #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h */ - #ifndef STBTT_malloc - #include - #define STBTT_malloc(x,u) ((void)(u),malloc(x)) - #define STBTT_free(x,u) ((void)(u),free(x)) - #endif - - #ifndef STBTT_assert - #include - #define STBTT_assert(x) assert(x) - #endif - - #ifndef STBTT_strlen - #include - #define STBTT_strlen(x) strlen(x) - #endif - - #ifndef STBTT_memcpy - #include - #define STBTT_memcpy memcpy - #define STBTT_memset memset - #endif -#endif - -/*///////////////////////////////////////////////////////////////////////////// */ -/*///////////////////////////////////////////////////////////////////////////// */ -/*// */ -/*// INTERFACE */ -/*// */ -/*// */ -#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ -#define __STB_INCLUDE_STB_TRUETYPE_H__ - -#ifdef STBTT_STATIC -#define STBTT_DEF static -#else -#define STBTT_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* TEXTURE BAKING API */ -/* */ -/* If you use this API, you only have to call two functions ever. */ -/* */ - -typedef struct -{ - unsigned short x0,y0,x1,y1; /* coordinates of bbox in bitmap */ - float xoff,yoff,xadvance; -} stbtt_bakedchar; - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, /* font location (use offset=0 for plain .ttf) */ - float pixel_height, /* height of font in pixels */ - unsigned char *pixels, int pw, int ph, /* bitmap to be filled in */ - int first_char, int num_chars, /* characters to bake */ - stbtt_bakedchar *chardata); /* you allocate this, it's num_chars long */ -/* if return is positive, the first unused row of the bitmap */ -/* if return is negative, returns the negative of the number of characters that fit */ -/* if return is 0, no characters fit and no rows were used */ -/* This uses a very crappy packing. */ - -typedef struct -{ - float x0,y0,s0,t0; /* top-left */ - float x1,y1,s1,t1; /* bottom-right */ -} stbtt_aligned_quad; - -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, /* same data as above */ - int char_index, /* character to display */ - float *xpos, float *ypos, /* pointers to current position in screen pixel space */ - stbtt_aligned_quad *q, /* output: quad to draw */ - int opengl_fillrule); /* true if opengl fill rule; false if DX9 or earlier */ -/* Call GetBakedQuad with char_index = 'character - first_char', and it */ -/* creates the quad you need to draw and advances the current position. */ -/* */ -/* The coordinate system used assumes y increases downwards. */ -/* */ -/* Characters will extend both above and below the current position; */ -/* see discussion of "BASELINE" above. */ -/* */ -/* It's inefficient; you might want to c&p it and optimize it. */ - - - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* NEW TEXTURE BAKING API */ -/* */ -/* This provides options for packing multiple fonts into one atlas, not */ -/* perfectly but better than nothing. */ - -typedef struct -{ - unsigned short x0,y0,x1,y1; /* coordinates of bbox in bitmap */ - float xoff,yoff,xadvance; - float xoff2,yoff2; -} stbtt_packedchar; - -typedef struct stbtt_pack_context stbtt_pack_context; -typedef struct stbtt_fontinfo stbtt_fontinfo; -#ifndef STB_RECT_PACK_VERSION -typedef struct stbrp_rect stbrp_rect; -#endif - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); -/* Initializes a packing context stored in the passed-in stbtt_pack_context. */ -/* Future calls using this context will pack characters into the bitmap passed */ -/* in here: a 1-channel bitmap that is weight x height. stride_in_bytes is */ -/* the distance from one row to the next (or 0 to mean they are packed tightly */ -/* together). "padding" is the amount of padding to leave between each */ -/* character (normally you want '1' for bitmaps you'll use as textures with */ -/* bilinear filtering). */ -/* */ -/* Returns 0 on failure, 1 on success. */ - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); -/* Cleans up the packing context and frees all memory. */ - -#define STBTT_POINT_SIZE(x) (-(x)) - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, - int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); -/* Creates character bitmaps from the font_index'th font found in fontdata (use */ -/* font_index=0 if you don't know what that is). It creates num_chars_in_range */ -/* bitmaps for characters with unicode values starting at first_unicode_char_in_range */ -/* and increasing. Data for how to render them is stored in chardata_for_range; */ -/* pass these to stbtt_GetPackedQuad to get back renderable quads. */ -/* */ -/* font_size is the full height of the character from ascender to descender, */ -/* as computed by stbtt_ScaleForPixelHeight. To use a point size as computed */ -/* by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() */ -/* and pass that result as 'font_size': */ -/* ..., 20 , ... // font max minus min y is 20 pixels tall */ -/* ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall */ - -typedef struct -{ - float font_size; - int first_unicode_codepoint_in_range; /* if non-zero, then the chars are continuous, and this is the first codepoint */ - int *array_of_unicode_codepoints; /* if non-zero, then this is an array of unicode codepoints */ - int num_chars; - stbtt_packedchar *chardata_for_range; /* output */ - unsigned char h_oversample, v_oversample; /* don't set these, they're used internally */ -} stbtt_pack_range; - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); -/* Creates character bitmaps from multiple ranges of characters stored in */ -/* ranges. This will usually create a better-packed bitmap than multiple */ -/* calls to stbtt_PackFontRange. Note that you can call this multiple */ -/* times within a single PackBegin/PackEnd. */ - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); -/* Oversampling a font increases the quality by allowing higher-quality subpixel */ -/* positioning, and is especially valuable at smaller text sizes. */ -/* */ -/* This function sets the amount of oversampling for all following calls to */ -/* stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given */ -/* pack context. The default (no oversampling) is achieved by h_oversample=1 */ -/* and v_oversample=1. The total number of pixels required is */ -/* h_oversample*v_oversample larger than the default; for example, 2x2 */ -/* oversampling requires 4x the storage of 1x1. For best results, render */ -/* oversampled textures with bilinear filtering. Look at the readme in */ -/* stb/tests/oversample for information about oversampled fonts */ -/* */ -/* To use with PackFontRangesGather etc., you must set it before calls */ -/* call to PackFontRangesGatherRects. */ - -STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, /* same data as above */ - int char_index, /* character to display */ - float *xpos, float *ypos, /* pointers to current position in screen pixel space */ - stbtt_aligned_quad *q, /* output: quad to draw */ - int align_to_integer); - -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -/* Calling these functions in sequence is roughly equivalent to calling */ -/* stbtt_PackFontRanges(). If you more control over the packing of multiple */ -/* fonts, or if you want to pack custom data into a font texture, take a look */ -/* at the source to of stbtt_PackFontRanges() and create a custom version */ -/* using these functions, e.g. call GatherRects multiple times, */ -/* building up a single array of rects, then call PackRects once, */ -/* then call RenderIntoRects repeatedly. This may result in a */ -/* better packing than calling PackFontRanges multiple times */ -/* (or it may not). */ - -/* this is an opaque structure that you shouldn't mess with which holds */ -/* all the context needed from PackBegin to PackEnd. */ -struct stbtt_pack_context { - void *user_allocator_context; - void *pack_info; - int width; - int height; - int stride_in_bytes; - int padding; - unsigned int h_oversample, v_oversample; - unsigned char *pixels; - void *nodes; -}; - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* FONT LOADING */ -/* */ -/* */ - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); -/* Each .ttf/.ttc file may have more than one font. Each font has a sequential */ -/* index number starting from 0. Call this function to get the font offset for */ -/* a given index; it returns -1 if the index is out of range. A regular .ttf */ -/* file will only define one font and it always be at offset 0, so it will */ -/* return '0' for index 0, and -1 for all other indices. You can just skip */ -/* this step if you know it's that kind of font. */ - - -/* The following structure is defined publically so you can declare one on */ -/* the stack or as a global or etc, but you should treat it as opaque. */ -struct stbtt_fontinfo -{ - void * userdata; - unsigned char * data; /* pointer to .ttf file */ - int fontstart; /* offset of start of font */ - - int numGlyphs; /* number of glyphs, needed for range checking */ - - int loca,head,glyf,hhea,hmtx,kern; /* table locations as offset from start of .ttf */ - int index_map; /* a cmap mapping for our chosen character encoding */ - int indexToLocFormat; /* format needed to map from glyph index to glyph */ -}; - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); -/* Given an offset into the file that defines a font, this function builds */ -/* the necessary cached info for the rest of the system. You must allocate */ -/* the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't */ -/* need to do anything special to free it, because the contents are pure */ -/* value data with no additional data structures. Returns 0 on failure. */ - - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* CHARACTER TO GLYPH-INDEX CONVERSIOn */ - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); -/* If you're going to perform multiple operations on the same character */ -/* and you want a speed-up, call this function with the character you're */ -/* going to process, then use glyph-based functions instead of the */ -/* codepoint-based functions. */ - - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* CHARACTER PROPERTIES */ -/* */ - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); -/* computes a scale factor to produce a font whose "height" is 'pixels' tall. */ -/* Height is measured as the distance from the highest ascender to the lowest */ -/* descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics */ -/* and computing: */ -/* scale = pixels / (ascent - descent) */ -/* so if you prefer to measure height by the ascent only, use a similar calculation. */ - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); -/* computes a scale factor to produce a font whose EM size is mapped to */ -/* 'pixels' tall. This is probably what traditional APIs compute, but */ -/* I'm not positive. */ - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); -/* ascent is the coordinate above the baseline the font extends; descent */ -/* is the coordinate below the baseline the font extends (i.e. it is typically negative) */ -/* lineGap is the spacing between one row's descent and the next row's ascent... */ -/* so you should advance the vertical position by "*ascent - *descent + *lineGap" */ -/* these are expressed in unscaled coordinates, so you must multiply by */ -/* the scale factor for a given size */ - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); -/* the bounding box around all possible characters */ - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); -/* leftSideBearing is the offset from the current horizontal position to the left edge of the character */ -/* advanceWidth is the offset from the current horizontal position to the next horizontal position */ -/* these are expressed in unscaled coordinates */ - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); -/* an additional amount to add to the 'advance' value between ch1 and ch2 */ - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); -/* Gets the bounding box of the visible part of the glyph, in unscaled coordinates */ - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); -/* as above, but takes one or more glyph indices for greater efficiency */ - - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* GLYPH SHAPES (you probably don't need these, but they have to go before */ -/* the bitmaps for C declaration-order reasons) */ -/* */ - -#ifndef STBTT_vmove /* you can predefine these to use different values (but why?) */ - enum { - STBTT_vmove=1, - STBTT_vline, - STBTT_vcurve - }; -#endif - -#ifndef stbtt_vertex /* you can predefine this to use different values */ - /* (we share this with other code at RAD) */ - #define stbtt_vertex_type short /* can't use stbtt_int16 because that's not visible in the header file */ - typedef struct - { - stbtt_vertex_type x,y,cx,cy; - unsigned char type,padding; - } stbtt_vertex; -#endif - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); -/* returns non-zero if nothing is drawn for this glyph */ - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); -/* returns # of vertices and fills *vertices with the pointer to them */ -/* these are expressed in "unscaled" coordinates */ -/* */ -/* The shape is a series of countours. Each one starts with */ -/* a STBTT_moveto, then consists of a series of mixed */ -/* STBTT_lineto and STBTT_curveto segments. A lineto */ -/* draws a line from previous endpoint to its x,y; a curveto */ -/* draws a quadratic bezier from previous endpoint to */ -/* its x,y, using cx,cy as the bezier control point. */ - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); -/* frees the data allocated above */ - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* BITMAP RENDERING */ -/* */ - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); -/* frees the bitmap allocated below */ - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -/* allocates a large-enough single-channel 8bpp bitmap and renders the */ -/* specified character/glyph at the specified scale into it, with */ -/* antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). */ -/* *width & *height are filled out with the width & height of the bitmap, */ -/* which is stored left-to-right, top-to-bottom. */ -/* */ -/* xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap */ - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -/* the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel */ -/* shift for the character */ - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); -/* the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap */ -/* in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap */ -/* is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the */ -/* width and height and positioning info for it first. */ - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); -/* same as stbtt_MakeCodepointBitmap, but you can specify a subpixel */ -/* shift for the character */ - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -/* get the bbox of the bitmap centered around the glyph origin; so the */ -/* bitmap width is ix1-ix0, height is iy1-iy0, and location to place */ -/* the bitmap top left is (leftSideBearing*scale,iy0). */ -/* (Note that the bitmap uses y-increases-down, but the shape uses */ -/* y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) */ - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); -/* same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel */ -/* shift for the character */ - -/* the following functions are equivalent to the above functions, but operate */ -/* on glyph indices instead of Unicode codepoints (for efficiency) */ -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); - - -/* @TODO: don't expose this structure */ -typedef struct -{ - int w,h,stride; - unsigned char *pixels; -} stbtt__bitmap; - -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* Finding the right font... */ -/* */ -/* You should really just solve this offline, keep your own tables */ -/* of what font is what, and don't try to get it out of the .ttf file. */ -/* That's because getting it out of the .ttf file is really hard, because */ -/* the names in the file can appear in many possible encodings, in many */ -/* possible languages, and e.g. if you need a case-insensitive comparison, */ -/* the details of that depend on the encoding & language in a complex way */ -/* (actually underspecified in truetype, but also gigantic). */ -/* */ -/* But you can use the provided functions in two possible ways: */ -/* stbtt_FindMatchingFont() will use *case-sensitive* comparisons on */ -/* unicode-encoded names to try to find the font you want; */ -/* you can run this before calling stbtt_InitFont() */ -/* */ -/* stbtt_GetFontNameString() lets you get any of the various strings */ -/* from the file yourself and do your own comparisons on them. */ -/* You have to have called stbtt_InitFont() first. */ - - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); -/* returns the offset (not index) of the font that matches, or -1 if none */ -/* if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". */ -/* if you use any other flag, use a font name like "Arial"; this checks */ -/* the 'macStyle' header field; i don't know if fonts set this consistently */ -#define STBTT_MACSTYLE_DONTCARE 0 -#define STBTT_MACSTYLE_BOLD 1 -#define STBTT_MACSTYLE_ITALIC 2 -#define STBTT_MACSTYLE_UNDERSCORE 4 -#define STBTT_MACSTYLE_NONE 8 /* <= not same as 0, this makes us check the bitfield is 0 */ - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); -/* returns 1/0 whether the first string interpreted as utf8 is identical to */ -/* the second string interpreted as big-endian utf16... useful for strings from next func */ - -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); -/* returns the string (which may be big-endian double byte, e.g. for unicode) */ -/* and puts the length in bytes in *length. */ -/* */ -/* some of the values for the IDs are below; for more see the truetype spec: */ -/* http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html */ -/* http://www.microsoft.com/typography/otspec/name.htm */ - -enum { /* platformID */ - STBTT_PLATFORM_ID_UNICODE =0, - STBTT_PLATFORM_ID_MAC =1, - STBTT_PLATFORM_ID_ISO =2, - STBTT_PLATFORM_ID_MICROSOFT =3 -}; - -enum { /* encodingID for STBTT_PLATFORM_ID_UNICODE */ - STBTT_UNICODE_EID_UNICODE_1_0 =0, - STBTT_UNICODE_EID_UNICODE_1_1 =1, - STBTT_UNICODE_EID_ISO_10646 =2, - STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, - STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 -}; - -enum { /* encodingID for STBTT_PLATFORM_ID_MICROSOFT */ - STBTT_MS_EID_SYMBOL =0, - STBTT_MS_EID_UNICODE_BMP =1, - STBTT_MS_EID_SHIFTJIS =2, - STBTT_MS_EID_UNICODE_FULL =10 -}; - -enum { /* encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes */ - STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, - STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, - STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, - STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 -}; - -enum { /* languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... */ - /* problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */ - STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, - STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, - STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, - STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, - STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, - STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D -}; - -enum { /* languageID for STBTT_PLATFORM_ID_MAC */ - STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, - STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, - STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, - STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , - STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , - STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, - STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 -}; - -#ifdef __cplusplus -} -#endif - -#endif /* __STB_INCLUDE_STB_TRUETYPE_H__ */ - -/*///////////////////////////////////////////////////////////////////////////// */ -/*///////////////////////////////////////////////////////////////////////////// */ -/*// */ -/*// IMPLEMENTATION */ -/*// */ -/*// */ - -#ifdef STB_TRUETYPE_IMPLEMENTATION - -#ifndef STBTT_MAX_OVERSAMPLE -#define STBTT_MAX_OVERSAMPLE 8 -#endif - -#if STBTT_MAX_OVERSAMPLE > 255 -#error "STBTT_MAX_OVERSAMPLE cannot be > 255" -#endif - -typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; - -#ifndef STBTT_RASTERIZER_VERSION -#define STBTT_RASTERIZER_VERSION 2 -#endif - -/*//////////////////////////////////////////////////////////////////////// */ -/* */ -/* accessors to parse data from file */ -/* */ - -/* on platforms that don't allow misaligned reads, if we want to allow */ -/* truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE */ - -#define ttBYTE(p) (* (stbtt_uint8 *) (p)) -#define ttCHAR(p) (* (stbtt_int8 *) (p)) -#define ttFixed(p) ttLONG(p) - -#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) - - #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) - #define ttSHORT(p) (* (stbtt_int16 *) (p)) - #define ttULONG(p) (* (stbtt_uint32 *) (p)) - #define ttLONG(p) (* (stbtt_int32 *) (p)) - -#else - - static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#endif - -#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) -#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) - -static int stbtt__isfont(const stbtt_uint8 *font) -{ - /* check the version number */ - if (stbtt_tag4(font, '1',0,0,0)) return 1; /* TrueType 1 */ - if (stbtt_tag(font, "typ1")) return 1; /* TrueType with type 1 font -- we don't support this! */ - if (stbtt_tag(font, "OTTO")) return 1; /* OpenType with CFF */ - if (stbtt_tag4(font, 0,1,0,0)) return 1; /* OpenType 1.0 */ - return 0; -} - -/* @OPTIMIZE: binary search */ -static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) -{ - stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); - stbtt_uint32 tabledir = fontstart + 12; - stbtt_int32 i; - for (i=0; i < num_tables; ++i) { - stbtt_uint32 loc = tabledir + 16*i; - if (stbtt_tag(data+loc+0, tag)) - return ttULONG(data+loc+8); - } - return 0; -} - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) -{ - /* if it's just a font, there's only one valid index */ - if (stbtt__isfont(font_collection)) - return index == 0 ? 0 : -1; - - /* check if it's a TTC */ - if (stbtt_tag(font_collection, "ttcf")) { - /* version 1? */ - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - stbtt_int32 n = ttLONG(font_collection+8); - if (index >= n) - return -1; - return ttULONG(font_collection+12+index*4); - } - } - return -1; -} - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) -{ - stbtt_uint8 *data = (stbtt_uint8 *) data2; - stbtt_uint32 cmap, t; - stbtt_int32 i,numTables; - - info->data = data; - info->fontstart = fontstart; - - cmap = stbtt__find_table(data, fontstart, "cmap"); /* required */ - info->loca = stbtt__find_table(data, fontstart, "loca"); /* required */ - info->head = stbtt__find_table(data, fontstart, "head"); /* required */ - info->glyf = stbtt__find_table(data, fontstart, "glyf"); /* required */ - info->hhea = stbtt__find_table(data, fontstart, "hhea"); /* required */ - info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); /* required */ - info->kern = stbtt__find_table(data, fontstart, "kern"); /* not required */ - if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) - return 0; - - t = stbtt__find_table(data, fontstart, "maxp"); - if (t) - info->numGlyphs = ttUSHORT(data+t+4); - else - info->numGlyphs = 0xffff; - - /* find a cmap encoding table we understand *now* to avoid searching */ - /* later. (todo: could make this installable) */ - /* the same regardless of glyph. */ - numTables = ttUSHORT(data + cmap + 2); - info->index_map = 0; - for (i=0; i < numTables; ++i) { - stbtt_uint32 encoding_record = cmap + 4 + 8 * i; - /* find an encoding we understand: */ - switch(ttUSHORT(data+encoding_record)) { - case STBTT_PLATFORM_ID_MICROSOFT: - switch (ttUSHORT(data+encoding_record+2)) { - case STBTT_MS_EID_UNICODE_BMP: - case STBTT_MS_EID_UNICODE_FULL: - /* MS/Unicode */ - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - break; - case STBTT_PLATFORM_ID_UNICODE: - /* Mac/iOS has these */ - /* all the encodingIDs are unicode, so we don't bother to check it */ - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - } - if (info->index_map == 0) - return 0; - - info->indexToLocFormat = ttUSHORT(data+info->head + 50); - return 1; -} - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) -{ - stbtt_uint8 *data = info->data; - stbtt_uint32 index_map = info->index_map; - - stbtt_uint16 format = ttUSHORT(data + index_map + 0); - if (format == 0) { /* apple byte encoding */ - stbtt_int32 bytes = ttUSHORT(data + index_map + 2); - if (unicode_codepoint < bytes-6) - return ttBYTE(data + index_map + 6 + unicode_codepoint); - return 0; - } else if (format == 6) { - stbtt_uint32 first = ttUSHORT(data + index_map + 6); - stbtt_uint32 count = ttUSHORT(data + index_map + 8); - if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) - return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); - return 0; - } else if (format == 2) { - STBTT_assert(0); /* @TODO: high-byte mapping for japanese/chinese/korean */ - return 0; - } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */ - stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; - stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; - stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); - stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; - - /* do a binary search of the segments */ - stbtt_uint32 endCount = index_map + 14; - stbtt_uint32 search = endCount; - - if (unicode_codepoint > 0xffff) - return 0; - - /* they lie from endCount .. endCount + segCount */ - /* but searchRange is the nearest power of two, so... */ - if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) - search += rangeShift*2; - - /* now decrement to bias correctly to find smallest */ - search -= 2; - while (entrySelector) { - stbtt_uint16 end; - searchRange >>= 1; - end = ttUSHORT(data + search + searchRange*2); - if (unicode_codepoint > end) - search += searchRange*2; - --entrySelector; - } - search += 2; - - { - stbtt_uint16 offset, start; - stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); - - STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); - start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); - if (unicode_codepoint < start) - return 0; - - offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); - if (offset == 0) - return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); - - return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); - } - } else if (format == 12 || format == 13) { - stbtt_uint32 ngroups = ttULONG(data+index_map+12); - stbtt_int32 low,high; - low = 0; high = (stbtt_int32)ngroups; - /* Binary search the right group. */ - while (low < high) { - stbtt_int32 mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */ - stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); - stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); - if ((stbtt_uint32) unicode_codepoint < start_char) - high = mid; - else if ((stbtt_uint32) unicode_codepoint > end_char) - low = mid+1; - else { - stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); - if (format == 12) - return start_glyph + unicode_codepoint-start_char; - else /* format == 13 */ - return start_glyph; - } - } - return 0; /* not found */ - } - /* @TODO */ - STBTT_assert(0); - return 0; -} - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) -{ - return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); -} - -static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) -{ - v->type = type; - v->x = (stbtt_int16) x; - v->y = (stbtt_int16) y; - v->cx = (stbtt_int16) cx; - v->cy = (stbtt_int16) cy; -} - -static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) -{ - int g1,g2; - - if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */ - if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */ - - if (info->indexToLocFormat == 0) { - g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; - g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; - } else { - g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); - g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); - } - - return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ -} - -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; - - if (x0) *x0 = ttSHORT(info->data + g + 2); - if (y0) *y0 = ttSHORT(info->data + g + 4); - if (x1) *x1 = ttSHORT(info->data + g + 6); - if (y1) *y1 = ttSHORT(info->data + g + 8); - return 1; -} - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) -{ - return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); -} - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt_int16 numberOfContours; - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 1; - numberOfContours = ttSHORT(info->data + g); - return numberOfContours == 0; -} - -static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, - stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) -{ - if (start_off) { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); - } - return num_vertices; -} - -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - stbtt_int16 numberOfContours; - stbtt_uint8 *endPtsOfContours; - stbtt_uint8 *data = info->data; - stbtt_vertex *vertices=0; - int num_vertices=0; - int g = stbtt__GetGlyfOffset(info, glyph_index); - - *pvertices = NULL; - - if (g < 0) return 0; - - numberOfContours = ttSHORT(data + g); - - if (numberOfContours > 0) { - stbtt_uint8 flags=0,flagcount; - stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; - stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; - stbtt_uint8 *points; - endPtsOfContours = (data + g + 10); - ins = ttUSHORT(data + g + 10 + numberOfContours * 2); - points = data + g + 10 + numberOfContours * 2 + 2 + ins; - - n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); - - m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */ - vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); - if (vertices == 0) - return 0; - - next_move = 0; - flagcount=0; - - /* in first pass, we load uninterpreted data into the allocated array */ - /* above, shifted to the end of the array so we won't overwrite it when */ - /* we create our final data starting from the front */ - - off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */ - - /* first load flags */ - - for (i=0; i < n; ++i) { - if (flagcount == 0) { - flags = *points++; - if (flags & 8) - flagcount = *points++; - } else - --flagcount; - vertices[off+i].type = flags; - } - - /* now load x coordinates */ - x=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 2) { - stbtt_int16 dx = *points++; - x += (flags & 16) ? dx : -dx; /* ??? */ - } else { - if (!(flags & 16)) { - x = x + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].x = (stbtt_int16) x; - } - - /* now load y coordinates */ - y=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 4) { - stbtt_int16 dy = *points++; - y += (flags & 32) ? dy : -dy; /* ??? */ - } else { - if (!(flags & 32)) { - y = y + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].y = (stbtt_int16) y; - } - - /* now convert them to our format */ - num_vertices=0; - sx = sy = cx = cy = scx = scy = 0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - x = (stbtt_int16) vertices[off+i].x; - y = (stbtt_int16) vertices[off+i].y; - - if (next_move == i) { - if (i != 0) - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - - /* now start the new one */ - start_off = !(flags & 1); - if (start_off) { - /* if we start off with an off-curve point, then when we need to find a point on the curve */ - /* where we can start, and we need to save some state for when we wraparound. */ - scx = x; - scy = y; - if (!(vertices[off+i+1].type & 1)) { - /* next point is also a curve point, so interpolate an on-point curve */ - sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; - sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; - } else { - /* otherwise just use the next point as our start point */ - sx = (stbtt_int32) vertices[off+i+1].x; - sy = (stbtt_int32) vertices[off+i+1].y; - ++i; /* we're using point i+1 as the starting point, so skip it */ - } - } else { - sx = x; - sy = y; - } - stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); - was_off = 0; - next_move = 1 + ttUSHORT(endPtsOfContours+j*2); - ++j; - } else { - if (!(flags & 1)) { /* if it's a curve */ - if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */ - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); - cx = x; - cy = y; - was_off = 1; - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); - was_off = 0; - } - } - } - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - } else if (numberOfContours == -1) { - /* Compound shapes. */ - int more = 1; - stbtt_uint8 *comp = data + g + 10; - num_vertices = 0; - vertices = 0; - while (more) { - stbtt_uint16 flags, gidx; - int comp_num_verts = 0, i; - stbtt_vertex *comp_verts = 0, *tmp = 0; - float mtx[6] = {1,0,0,1,0,0}, m, n; - - flags = ttSHORT(comp); comp+=2; - gidx = ttSHORT(comp); comp+=2; - - if (flags & 2) { /* XY values */ - if (flags & 1) { /* shorts */ - mtx[4] = ttSHORT(comp); comp+=2; - mtx[5] = ttSHORT(comp); comp+=2; - } else { - mtx[4] = ttCHAR(comp); comp+=1; - mtx[5] = ttCHAR(comp); comp+=1; - } - } - else { - /* @TODO handle matching point */ - STBTT_assert(0); - } - if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */ - mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */ - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */ - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } - - /* Find transformation scales. */ - m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); - n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); - - /* Get indexed glyph. */ - comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); - if (comp_num_verts > 0) { - /* Transform vertices. */ - for (i = 0; i < comp_num_verts; ++i) { - stbtt_vertex* v = &comp_verts[i]; - stbtt_vertex_type x,y; - x=v->x; y=v->y; - v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - x=v->cx; y=v->cy; - v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - } - /* Append vertices. */ - tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); - if (!tmp) { - if (vertices) STBTT_free(vertices, info->userdata); - if (comp_verts) STBTT_free(comp_verts, info->userdata); - return 0; - } - if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); - STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); - if (vertices) STBTT_free(vertices, info->userdata); - vertices = tmp; - STBTT_free(comp_verts, info->userdata); - num_vertices += comp_num_verts; - } - /* More components ? */ - more = flags & (1<<5); - } - } else if (numberOfContours < 0) { - /* @TODO other compound variations? */ - STBTT_assert(0); - } else { - /* numberOfCounters == 0, do nothing */ - } - - *pvertices = vertices; - return num_vertices; -} - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) -{ - stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); - if (glyph_index < numOfLongHorMetrics) { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); - } else { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); - } -} - -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint8 *data = info->data + info->kern; - stbtt_uint32 needle, straw; - int l, r, m; - - /* we only look at the first table. it must be 'horizontal' and format 0. */ - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) /* number of tables, need at least 1 */ - return 0; - if (ttUSHORT(data+8) != 1) /* horizontal flag must be set in format */ - return 0; - - l = 0; - r = ttUSHORT(data+10) - 1; - needle = glyph1 << 16 | glyph2; - while (l <= r) { - m = (l + r) >> 1; - straw = ttULONG(data+18+(m*6)); /* note: unaligned read */ - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else - return ttSHORT(data+22+(m*6)); - } - return 0; -} - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) -{ - if (!info->kern) /* if no kerning table, don't waste time looking up both codepoint->glyphs */ - return 0; - return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); -} - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) -{ - stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); -} - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) -{ - if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); - if (descent) *descent = ttSHORT(info->data+info->hhea + 6); - if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); -} - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) -{ - *x0 = ttSHORT(info->data + info->head + 36); - *y0 = ttSHORT(info->data + info->head + 38); - *x1 = ttSHORT(info->data + info->head + 40); - *y1 = ttSHORT(info->data + info->head + 42); -} - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) -{ - int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); - return (float) height / fheight; -} - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) -{ - int unitsPerEm = ttUSHORT(info->data + info->head + 18); - return pixels / unitsPerEm; -} - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) -{ - STBTT_free(v, info->userdata); -} - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* antialiasing software rasterizer */ -/* */ - -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - int x0,y0,x1,y1; - if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { - /* e.g. space character */ - if (ix0) *ix0 = 0; - if (iy0) *iy0 = 0; - if (ix1) *ix1 = 0; - if (iy1) *iy1 = 0; - } else { - /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */ - if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); - if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); - if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); - if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); - } -} - -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); -} - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* Rasterizer */ - -typedef struct stbtt__hheap_chunk -{ - struct stbtt__hheap_chunk *next; -} stbtt__hheap_chunk; - -typedef struct stbtt__hheap -{ - struct stbtt__hheap_chunk *head; - void *first_free; - int num_remaining_in_head_chunk; -} stbtt__hheap; - -static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) -{ - if (hh->first_free) { - void *p = hh->first_free; - hh->first_free = * (void **) p; - return p; - } else { - if (hh->num_remaining_in_head_chunk == 0) { - int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); - stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); - if (c == NULL) - return NULL; - c->next = hh->head; - hh->head = c; - hh->num_remaining_in_head_chunk = count; - } - --hh->num_remaining_in_head_chunk; - return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; - } -} - -static void stbtt__hheap_free(stbtt__hheap *hh, void *p) -{ - *(void **) p = hh->first_free; - hh->first_free = p; -} - -static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) -{ - stbtt__hheap_chunk *c = hh->head; - while (c) { - stbtt__hheap_chunk *n = c->next; - STBTT_free(c, userdata); - c = n; - } -} - -typedef struct stbtt__edge { - float x0,y0, x1,y1; - int invert; -} stbtt__edge; - - -typedef struct stbtt__active_edge -{ - struct stbtt__active_edge *next; - #if STBTT_RASTERIZER_VERSION==1 - int x,dx; - float ey; - int direction; - #elif STBTT_RASTERIZER_VERSION==2 - float fx,fdx,fdy; - float direction; - float sy; - float ey; - #else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" - #endif -} stbtt__active_edge; - -#if STBTT_RASTERIZER_VERSION == 1 -#define STBTT_FIXSHIFT 10 -#define STBTT_FIX (1 << STBTT_FIXSHIFT) -#define STBTT_FIXMASK (STBTT_FIX-1) - -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - if (!z) return z; - - /* round dx down to avoid overshooting */ - if (dxdy < 0) - z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); - else - z->dx = STBTT_ifloor(STBTT_FIX * dxdy); - - z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); /* use z->dx so when we offset later it's by the same amount */ - z->x -= off_x * STBTT_FIX; - - z->ey = e->y1; - z->next = 0; - z->direction = e->invert ? 1 : -1; - return z; -} -#elif STBTT_RASTERIZER_VERSION == 2 -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - /*STBTT_assert(e->y0 <= start_point); */ - if (!z) return z; - z->fdx = dxdy; - z->fdy = (1/dxdy); - z->fx = e->x0 + dxdy * (start_point - e->y0); - z->fx -= off_x; - z->direction = e->invert ? 1.0f : -1.0f; - z->sy = e->y0; - z->ey = e->y1; - z->next = 0; - return z; -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#if STBTT_RASTERIZER_VERSION == 1 -/* note: this routine clips fills that extend off the edges... ideally this */ -/* wouldn't happen, but it could happen if the truetype glyph bounding boxes */ -/* are wrong, or if the user supplies a too-small bitmap */ -static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) -{ - /* non-zero winding fill */ - int x0=0, w=0; - - while (e) { - if (w == 0) { - /* if we're currently at zero, we need to record the edge start point */ - x0 = e->x; w += e->direction; - } else { - int x1 = e->x; w += e->direction; - /* if we went to zero, we need to draw */ - if (w == 0) { - int i = x0 >> STBTT_FIXSHIFT; - int j = x1 >> STBTT_FIXSHIFT; - - if (i < len && j >= 0) { - if (i == j) { - /* x0,x1 are the same pixel, so compute combined coverage */ - scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); - } else { - if (i >= 0) /* add antialiasing for x0 */ - scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); - else - i = -1; /* clip */ - - if (j < len) /* add antialiasing for x1 */ - scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); - else - j = len; /* clip */ - - for (++i; i < j; ++i) /* fill pixels between x0 and x1 */ - scanline[i] = scanline[i] + (stbtt_uint8) max_weight; - } - } - } - } - - e = e->next; - } -} - -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0 }; - stbtt__active_edge *active = NULL; - int y,j=0; - int max_weight = (255 / vsubsample); /* weight per vertical scanline */ - int s; /* vertical subsample index */ - unsigned char scanline_data[512], *scanline; - - if (result->w > 512) - scanline = (unsigned char *) STBTT_malloc(result->w, userdata); - else - scanline = scanline_data; - - y = off_y * vsubsample; - e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; - - while (j < result->h) { - STBTT_memset(scanline, 0, result->w); - for (s=0; s < vsubsample; ++s) { - /* find center of pixel for this scanline */ - float scan_y = y + 0.5f; - stbtt__active_edge **step = &active; - - /* update all active edges; */ - /* remove all active edges that terminate before the center of this scanline */ - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y) { - *step = z->next; /* delete from list */ - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - z->x += z->dx; /* advance to position for current scanline */ - step = &((*step)->next); /* advance through list */ - } - } - - /* resort the list if needed */ - for(;;) { - int changed=0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - stbtt__active_edge *t = *step; - stbtt__active_edge *q = t->next; - - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - /* insert all edges that start before the center of this scanline -- omit ones that also end on this scanline */ - while (e->y0 <= scan_y) { - if (e->y1 > scan_y) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); - /* find insertion point */ - if (active == NULL) - active = z; - else if (z->x < active->x) { - /* insert at front */ - z->next = active; - active = z; - } else { - /* find thing to insert AFTER */ - stbtt__active_edge *p = active; - while (p->next && p->next->x < z->x) - p = p->next; - /* at this point, p->next->x is NOT < z->x */ - z->next = p->next; - p->next = z; - } - } - ++e; - } - - /* now process all active edges in XOR fashion */ - if (active) - stbtt__fill_active_edges(scanline, result->w, active, max_weight); - - ++y; - } - STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} - -#elif STBTT_RASTERIZER_VERSION == 2 - -/* the edge passed in here does not cross the vertical line at x or the vertical line at x+1 */ -/* (i.e. it has already been clipped to those) */ -static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) -{ - if (y0 == y1) return; - STBTT_assert(y0 < y1); - STBTT_assert(e->sy <= e->ey); - if (y0 > e->ey) return; - if (y1 < e->sy) return; - if (y0 < e->sy) { - x0 += (x1-x0) * (e->sy - y0) / (y1-y0); - y0 = e->sy; - } - if (y1 > e->ey) { - x1 += (x1-x0) * (e->ey - y1) / (y1-y0); - y1 = e->ey; - } - - if (x0 == x) - STBTT_assert(x1 <= x+1); - else if (x0 == x+1) - STBTT_assert(x1 >= x); - else if (x0 <= x) - STBTT_assert(x1 <= x); - else if (x0 >= x+1) - STBTT_assert(x1 >= x+1); - else - STBTT_assert(x1 >= x && x1 <= x+1); - - if (x0 <= x && x1 <= x) - scanline[x] += e->direction * (y1-y0); - else if (x0 >= x+1 && x1 >= x+1) - ; - else { - STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); - scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); /* coverage = 1 - average x position */ - } -} - -static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) -{ - float y_bottom = y_top+1; - - while (e) { - /* brute force every pixel */ - - /* compute intersection points with top & bottom */ - STBTT_assert(e->ey >= y_top); - - if (e->fdx == 0) { - float x0 = e->fx; - if (x0 < len) { - if (x0 >= 0) { - stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); - stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); - } else { - stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); - } - } - } else { - float x0 = e->fx; - float dx = e->fdx; - float xb = x0 + dx; - float x_top, x_bottom; - float y0,y1; - float dy = e->fdy; - STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); - - /* compute endpoints of line segment clipped to this scanline (if the */ - /* line segment starts on this scanline. x0 is the intersection of the */ - /* line with y_top, but that may be off the line segment. */ - if (e->sy > y_top) { - x_top = x0 + dx * (e->sy - y_top); - y0 = e->sy; - } else { - x_top = x0; - y0 = y_top; - } - if (e->ey < y_bottom) { - x_bottom = x0 + dx * (e->ey - y_top); - y1 = e->ey; - } else { - x_bottom = xb; - y1 = y_bottom; - } - - if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { - /* from here on, we don't have to range check x values */ - - if ((int) x_top == (int) x_bottom) { - float height; - /* simple case, only spans one pixel */ - int x = (int) x_top; - height = y1 - y0; - STBTT_assert(x >= 0 && x < len); - scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; - scanline_fill[x] += e->direction * height; /* everything right of this pixel is filled */ - } else { - int x,x1,x2; - float y_crossing, step, sign, area; - /* covers 2+ pixels */ - if (x_top > x_bottom) { - /* flip scanline vertically; signed area is the same */ - float t; - y0 = y_bottom - (y0 - y_top); - y1 = y_bottom - (y1 - y_top); - t = y0, y0 = y1, y1 = t; - t = x_bottom, x_bottom = x_top, x_top = t; - dx = -dx; - dy = -dy; - t = x0, x0 = xb, xb = t; - } - - x1 = (int) x_top; - x2 = (int) x_bottom; - /* compute intersection with y axis at x1+1 */ - y_crossing = (x1+1 - x0) * dy + y_top; - - sign = e->direction; - /* area of the rectangle covered from y0..y_crossing */ - area = sign * (y_crossing-y0); - /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */ - scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); - - step = sign * dy; - for (x = x1+1; x < x2; ++x) { - scanline[x] += area + step/2; - area += step; - } - y_crossing += dy * (x2 - (x1+1)); - - STBTT_assert(fabs(area) <= 1.01f); - - scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (y1-y_crossing); - - scanline_fill[x2] += sign * (y1-y0); - } - } else { - /* if edge goes outside of box we're drawing, we require */ - /* clipping logic. since this does not match the intended use */ - /* of this library, we use a different, very slow brute */ - /* force implementation */ - int x; - for (x=0; x < len; ++x) { - /* cases: */ - /* */ - /* there can be up to two intersections with the pixel. any intersection */ - /* with left or right edges can be handled by splitting into two (or three) */ - /* regions. intersections with top & bottom do not necessitate case-wise logic. */ - /* */ - /* the old way of doing this found the intersections with the left & right edges, */ - /* then used some simple logic to produce up to three segments in sorted order */ - /* from top-to-bottom. however, this had a problem: if an x edge was epsilon */ - /* across the x border, then the corresponding y position might not be distinct */ - /* from the other y segment, and it might ignored as an empty segment. to avoid */ - /* that, we need to explicitly produce segments based on x positions. */ - - /* rename variables to clear pairs */ - float y0 = y_top; - float x1 = (float) (x); - float x2 = (float) (x+1); - float x3 = xb; - float y3 = y_bottom; - float y1,y2; - - /* x = e->x + e->dx * (y-y_top) */ - /* (y-y_top) = (x - e->x) / e->dx */ - /* y = (x - e->x) / e->dx + y_top */ - y1 = (x - x0) / dx + y_top; - y2 = (x+1 - x0) / dx + y_top; - - if (x0 < x1 && x3 > x2) { /* three segments descending down-right */ - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */ - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */ - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */ - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */ - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */ - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else { /* one segment */ - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); - } - } - } - } - e = e->next; - } -} - -/* directly AA rasterize edges w/o supersampling */ -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - (void)vsubsample; - stbtt__hheap hh = { 0 }; - stbtt__active_edge *active = NULL; - int y,j=0, i; - float scanline_data[129], *scanline, *scanline2; - - if (result->w > 64) - scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); - else - scanline = scanline_data; - - scanline2 = scanline + result->w; - - y = off_y; - e[n].y0 = (float) (off_y + result->h) + 1; - - while (j < result->h) { - /* find center of pixel for this scanline */ - float scan_y_top = y + 0.0f; - float scan_y_bottom = y + 1.0f; - stbtt__active_edge **step = &active; - - STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); - STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); - - /* update all active edges; */ - /* remove all active edges that terminate before the top of this scanline */ - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y_top) { - *step = z->next; /* delete from list */ - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - step = &((*step)->next); /* advance through list */ - } - } - - /* insert all edges that start before the bottom of this scanline */ - while (e->y0 <= scan_y_bottom) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); - STBTT_assert(z->ey >= scan_y_top); - /* insert at front */ - z->next = active; - active = z; - ++e; - } - - /* now process all active edges */ - if (active) - stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); - - { - float sum = 0; - for (i=0; i < result->w; ++i) { - float k; - int m; - sum += scanline2[i]; - k = scanline[i] + sum; - k = (float) fabs(k)*255 + 0.5f; - m = (int) k; - if (m > 255) m = 255; - result->pixels[j*result->stride + i] = (unsigned char) m; - } - } - /* advance all the edges */ - step = &active; - while (*step) { - stbtt__active_edge *z = *step; - z->fx += z->fdx; /* advance to position for current scanline */ - step = &((*step)->next); /* advance through list */ - } - - ++y; - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) - -static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) -{ - int i,j; - for (i=1; i < n; ++i) { - stbtt__edge t = p[i], *a = &t; - j = i; - while (j > 0) { - stbtt__edge *b = &p[j-1]; - int c = STBTT__COMPARE(a,b); - if (!c) break; - p[j] = p[j-1]; - --j; - } - if (i != j) - p[j] = t; - } -} - -static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) -{ - /* threshhold for transitioning to insertion sort */ - while (n > 12) { - stbtt__edge t; - int c01,c12,c,m,i,j; - - /* compute median of three */ - m = n >> 1; - c01 = STBTT__COMPARE(&p[0],&p[m]); - c12 = STBTT__COMPARE(&p[m],&p[n-1]); - /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ - if (c01 != c12) { - /* otherwise, we'll need to swap something else to middle */ - int z; - c = STBTT__COMPARE(&p[0],&p[n-1]); - /* 0>mid && midn => n; 0 0 */ - /* 0n: 0>n => 0; 0 n */ - z = (c == c12) ? 0 : n-1; - t = p[z]; - p[z] = p[m]; - p[m] = t; - } - /* now p[m] is the median-of-three */ - /* swap it to the beginning so it won't move around */ - t = p[0]; - p[0] = p[m]; - p[m] = t; - - /* partition loop */ - i=1; - j=n-1; - for(;;) { - /* handling of equality is crucial here */ - /* for sentinels & efficiency with duplicates */ - for (;;++i) { - if (!STBTT__COMPARE(&p[i], &p[0])) break; - } - for (;;--j) { - if (!STBTT__COMPARE(&p[0], &p[j])) break; - } - /* make sure we haven't crossed */ - if (i >= j) break; - t = p[i]; - p[i] = p[j]; - p[j] = t; - - ++i; - --j; - } - /* recurse on smaller side, iterate on larger */ - if (j < (n-i)) { - stbtt__sort_edges_quicksort(p,j); - p = p+i; - n = n-i; - } else { - stbtt__sort_edges_quicksort(p+i, n-i); - n = j; - } - } -} - -static void stbtt__sort_edges(stbtt__edge *p, int n) -{ - stbtt__sort_edges_quicksort(p, n); - stbtt__sort_edges_ins_sort(p, n); -} - -typedef struct -{ - float x,y; -} stbtt__point; - -static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) -{ - float y_scale_inv = invert ? -scale_y : scale_y; - stbtt__edge *e; - int n,i,j,k,m; -#if STBTT_RASTERIZER_VERSION == 1 - int vsubsample = result->h < 8 ? 15 : 5; -#elif STBTT_RASTERIZER_VERSION == 2 - int vsubsample = 1; -#else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity */ - - /* now we have to blow out the windings into explicit edge lists */ - n = 0; - for (i=0; i < windings; ++i) - n += wcount[i]; - - e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); /* add an extra one as a sentinel */ - if (e == 0) return; - n = 0; - - m=0; - for (i=0; i < windings; ++i) { - stbtt__point *p = pts + m; - m += wcount[i]; - j = wcount[i]-1; - for (k=0; k < wcount[i]; j=k++) { - int a=k,b=j; - /* skip the edge if horizontal */ - if (p[j].y == p[k].y) - continue; - /* add edge from j to k to the list */ - e[n].invert = 0; - if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { - e[n].invert = 1; - a=j,b=k; - } - e[n].x0 = p[a].x * scale_x + shift_x; - e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; - e[n].x1 = p[b].x * scale_x + shift_x; - e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; - ++n; - } - } - - /* now sort the edges by their highest point (should snap to integer, and then by x) */ - /*STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */ - stbtt__sort_edges(e, n); - - /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ - stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); - - STBTT_free(e, userdata); -} - -static void stbtt__add_point(stbtt__point *points, int n, float x, float y) -{ - if (!points) return; /* during first pass, it's unallocated */ - points[n].x = x; - points[n].y = y; -} - -/* tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching */ -static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) -{ - /* midpoint */ - float mx = (x0 + 2*x1 + x2)/4; - float my = (y0 + 2*y1 + y2)/4; - /* versus directly drawn line */ - float dx = (x0+x2)/2 - mx; - float dy = (y0+y2)/2 - my; - if (n > 16) /* 65536 segments on one curve better be enough! */ - return 1; - if (dx*dx+dy*dy > objspace_flatness_squared) { /* half-pixel error allowed... need to be smaller if AA */ - stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x2,y2); - *num_points = *num_points+1; - } - return 1; -} - -/* returns number of contours */ -static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) -{ - stbtt__point *points=0; - int num_points=0; - - float objspace_flatness_squared = objspace_flatness * objspace_flatness; - int i,n=0,start=0, pass; - - /* count how many "moves" there are to get the contour count */ - for (i=0; i < num_verts; ++i) - if (vertices[i].type == STBTT_vmove) - ++n; - - *num_contours = n; - if (n == 0) return 0; - - *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); - - if (*contour_lengths == 0) { - *num_contours = 0; - return 0; - } - - /* make two passes through the points so we don't need to realloc */ - for (pass=0; pass < 2; ++pass) { - float x=0,y=0; - if (pass == 1) { - points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); - if (points == NULL) goto error; - } - num_points = 0; - n= -1; - for (i=0; i < num_verts; ++i) { - switch (vertices[i].type) { - case STBTT_vmove: - /* start the next contour */ - if (n >= 0) - (*contour_lengths)[n] = num_points - start; - ++n; - start = num_points; - - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x,y); - break; - case STBTT_vline: - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x, y); - break; - case STBTT_vcurve: - stbtt__tesselate_curve(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - } - } - (*contour_lengths)[n] = num_points - start; - } - - return points; -error: - STBTT_free(points, userdata); - STBTT_free(*contour_lengths, userdata); - *contour_lengths = 0; - *num_contours = 0; - return NULL; -} - -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) -{ - float scale = scale_x > scale_y ? scale_y : scale_x; - int winding_count, *winding_lengths; - stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); - if (windings) { - stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); - STBTT_free(winding_lengths, userdata); - STBTT_free(windings, userdata); - } -} - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - int ix0,iy0,ix1,iy1; - stbtt__bitmap gbm; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) return NULL; - scale_y = scale_x; - } - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); - - /* now we get the size */ - gbm.w = (ix1 - ix0); - gbm.h = (iy1 - iy0); - gbm.pixels = NULL; /* in case we error */ - - if (width ) *width = gbm.w; - if (height) *height = gbm.h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - if (gbm.w && gbm.h) { - gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); - if (gbm.pixels) { - gbm.stride = gbm.w; - - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); - } - } - STBTT_free(vertices, info->userdata); - return gbm.pixels; -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) -{ - int ix0,iy0; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); - gbm.pixels = output; - gbm.w = out_w; - gbm.h = out_h; - gbm.stride = out_stride; - - if (gbm.w && gbm.h) - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); - - STBTT_free(vertices, info->userdata); -} - -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) -{ - stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); -} - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* bitmap baking */ -/* */ -/* This is SUPER-CRAPPY packing to keep source code small */ - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, /* font location (use offset=0 for plain .ttf) */ - float pixel_height, /* height of font in pixels */ - unsigned char *pixels, int pw, int ph, /* bitmap to be filled in */ - int first_char, int num_chars, /* characters to bake */ - stbtt_bakedchar *chardata) -{ - float scale; - int x,y,bottom_y, i; - stbtt_fontinfo f; - if (!stbtt_InitFont(&f, data, offset)) - return -1; - STBTT_memset(pixels, 0, pw*ph); /* background of 0 around pixels */ - x=y=1; - bottom_y = 1; - - scale = stbtt_ScaleForPixelHeight(&f, pixel_height); - - for (i=0; i < num_chars; ++i) { - int advance, lsb, x0,y0,x1,y1,gw,gh; - int g = stbtt_FindGlyphIndex(&f, first_char + i); - stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); - stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); - gw = x1-x0; - gh = y1-y0; - if (x + gw + 1 >= pw) - y = bottom_y, x = 1; /* advance to next row */ - if (y + gh + 1 >= ph) /* check if it fits vertically AFTER potentially moving to next row */ - return -i; - STBTT_assert(x+gw < pw); - STBTT_assert(y+gh < ph); - stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); - chardata[i].x0 = (stbtt_int16) x; - chardata[i].y0 = (stbtt_int16) y; - chardata[i].x1 = (stbtt_int16) (x + gw); - chardata[i].y1 = (stbtt_int16) (y + gh); - chardata[i].xadvance = scale * advance; - chardata[i].xoff = (float) x0; - chardata[i].yoff = (float) y0; - x = x + gw + 1; - if (y+gh+1 > bottom_y) - bottom_y = y+gh+1; - } - return bottom_y; -} - -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) -{ - float d3d_bias = opengl_fillrule ? 0 : -0.5f; - float ipw = 1.0f / pw, iph = 1.0f / ph; - stbtt_bakedchar *b = chardata + char_index; - int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); - int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); - - q->x0 = round_x + d3d_bias; - q->y0 = round_y + d3d_bias; - q->x1 = round_x + b->x1 - b->x0 + d3d_bias; - q->y1 = round_y + b->y1 - b->y0 + d3d_bias; - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* rectangle packing replacement routines if you don't have stb_rect_pack.h */ -/* */ - -#ifndef STB_RECT_PACK_VERSION -#ifdef _MSC_VER -#define STBTT__NOTUSED(v) (void)(v) -#else -#define STBTT__NOTUSED(v) (void)sizeof(v) -#endif - -typedef int stbrp_coord; - -/*////////////////////////////////////////////////////////////////////////////////// */ -/* // */ -/* // */ -/* COMPILER WARNING ?!?!? // */ -/* // */ -/* // */ -/* if you get a compile warning due to these symbols being defined more than // */ -/* once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // */ -/* // */ -/*////////////////////////////////////////////////////////////////////////////////// */ - -typedef struct -{ - int width,height; - int x,y,bottom_y; -} stbrp_context; - -typedef struct -{ - unsigned char x; -} stbrp_node; - -struct stbrp_rect -{ - stbrp_coord x,y; - int id,w,h,was_packed; -}; - -static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) -{ - con->width = pw; - con->height = ph; - con->x = 0; - con->y = 0; - con->bottom_y = 0; - STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); -} - -static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) -{ - int i; - for (i=0; i < num_rects; ++i) { - if (con->x + rects[i].w > con->width) { - con->x = 0; - con->y = con->bottom_y; - } - if (con->y + rects[i].h > con->height) - break; - rects[i].x = con->x; - rects[i].y = con->y; - rects[i].was_packed = 1; - con->x += rects[i].w; - if (con->y + rects[i].h > con->bottom_y) - con->bottom_y = con->y + rects[i].h; - } - for ( ; i < num_rects; ++i) - rects[i].was_packed = 0; -} -#endif - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* bitmap baking */ -/* */ -/* This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If */ -/* stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. */ - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) -{ - stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); - int num_nodes = pw - padding; - stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); - - if (context == NULL || nodes == NULL) { - if (context != NULL) STBTT_free(context, alloc_context); - if (nodes != NULL) STBTT_free(nodes , alloc_context); - return 0; - } - - spc->user_allocator_context = alloc_context; - spc->width = pw; - spc->height = ph; - spc->pixels = pixels; - spc->pack_info = context; - spc->nodes = nodes; - spc->padding = padding; - spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; - spc->h_oversample = 1; - spc->v_oversample = 1; - - stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); - - if (pixels) - STBTT_memset(pixels, 0, pw*ph); /* background of 0 around pixels */ - - return 1; -} - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) -{ - STBTT_free(spc->nodes , spc->user_allocator_context); - STBTT_free(spc->pack_info, spc->user_allocator_context); -} - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) -{ - STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); - STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); - if (h_oversample <= STBTT_MAX_OVERSAMPLE) - spc->h_oversample = h_oversample; - if (v_oversample <= STBTT_MAX_OVERSAMPLE) - spc->v_oversample = v_oversample; -} - -#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) - -static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_w = w - kernel_width; - int j; - for (j=0; j < h; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - /* make kernel_width a constant in common cases so compiler can optimize out the divide */ - switch (kernel_width) { - case 2: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < w; ++i) { - STBTT_assert(pixels[i] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i] = (unsigned char) (total / kernel_width); - } - - pixels += stride_in_bytes; - } -} - -static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_h = h - kernel_width; - int j; - for (j=0; j < w; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - /* make kernel_width a constant in common cases so compiler can optimize out the divide */ - switch (kernel_width) { - case 2: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < h; ++i) { - STBTT_assert(pixels[i*stride_in_bytes] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - - pixels += 1; - } -} - -static float stbtt__oversample_shift(int oversample) -{ - if (!oversample) - return 0.0f; - - /* The prefilter is a box filter of width "oversample", */ - /* which shifts phase by (oversample - 1)/2 pixels in */ - /* oversampled space. We want to shift in the opposite */ - /* direction to counter this. */ - return (float)-(oversample - 1) / (2.0f * (float)oversample); -} - -/* rects array must be big enough to accommodate all characters in the given ranges */ -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k; - - k=0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - ranges[i].h_oversample = (unsigned char) spc->h_oversample; - ranges[i].v_oversample = (unsigned char) spc->v_oversample; - for (j=0; j < ranges[i].num_chars; ++j) { - int x0,y0,x1,y1; - int codepoint = ranges[i].first_unicode_codepoint_in_range ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - &x0,&y0,&x1,&y1); - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); - ++k; - } - } - - return k; -} - -/* rects array must be big enough to accommodate all characters in the given ranges */ -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k, return_value = 1; - - /* save current values */ - int old_h_over = spc->h_oversample; - int old_v_over = spc->v_oversample; - - k = 0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - float recip_h,recip_v,sub_x,sub_y; - spc->h_oversample = ranges[i].h_oversample; - spc->v_oversample = ranges[i].v_oversample; - recip_h = 1.0f / spc->h_oversample; - recip_v = 1.0f / spc->v_oversample; - sub_x = stbtt__oversample_shift(spc->h_oversample); - sub_y = stbtt__oversample_shift(spc->v_oversample); - for (j=0; j < ranges[i].num_chars; ++j) { - stbrp_rect *r = &rects[k]; - if (r->was_packed) { - stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; - int advance, lsb, x0,y0,x1,y1; - int codepoint = ranges[i].first_unicode_codepoint_in_range ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbrp_coord pad = (stbrp_coord) spc->padding; - - /* pad on left and top */ - r->x += pad; - r->y += pad; - r->w -= pad; - r->h -= pad; - stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); - stbtt_GetGlyphBitmapBox(info, glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - &x0,&y0,&x1,&y1); - stbtt_MakeGlyphBitmapSubpixel(info, - spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w - spc->h_oversample+1, - r->h - spc->v_oversample+1, - spc->stride_in_bytes, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - glyph); - - if (spc->h_oversample > 1) - stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->h_oversample); - - if (spc->v_oversample > 1) - stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->v_oversample); - - bc->x0 = (stbtt_int16) r->x; - bc->y0 = (stbtt_int16) r->y; - bc->x1 = (stbtt_int16) (r->x + r->w); - bc->y1 = (stbtt_int16) (r->y + r->h); - bc->xadvance = scale * advance; - bc->xoff = (float) x0 * recip_h + sub_x; - bc->yoff = (float) y0 * recip_v + sub_y; - bc->xoff2 = (x0 + r->w) * recip_h + sub_x; - bc->yoff2 = (y0 + r->h) * recip_v + sub_y; - } else { - return_value = 0; /* if any fail, report failure */ - } - - ++k; - } - } - - /* restore original values */ - spc->h_oversample = old_h_over; - spc->v_oversample = old_v_over; - - return return_value; -} - -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) -{ - stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); -} - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) -{ - stbtt_fontinfo info; - int i,j,n, return_value = 1; - /*stbrp_context *context = (stbrp_context *) spc->pack_info; */ - stbrp_rect *rects; - - /* flag all characters as NOT packed */ - for (i=0; i < num_ranges; ++i) - for (j=0; j < ranges[i].num_chars; ++j) - ranges[i].chardata_for_range[j].x0 = - ranges[i].chardata_for_range[j].y0 = - ranges[i].chardata_for_range[j].x1 = - ranges[i].chardata_for_range[j].y1 = 0; - - n = 0; - for (i=0; i < num_ranges; ++i) - n += ranges[i].num_chars; - - rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); - if (rects == NULL) - return 0; - - stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); - - n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); - - stbtt_PackFontRangesPackRects(spc, rects, n); - - return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); - - STBTT_free(rects, spc->user_allocator_context); - return return_value; -} - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, - int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) -{ - stbtt_pack_range range; - range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; - range.array_of_unicode_codepoints = NULL; - range.num_chars = num_chars_in_range; - range.chardata_for_range = chardata_for_range; - range.font_size = font_size; - return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); -} - -STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) -{ - float ipw = 1.0f / pw, iph = 1.0f / ph; - stbtt_packedchar *b = chardata + char_index; - - if (align_to_integer) { - float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); - float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); - q->x0 = x; - q->y0 = y; - q->x1 = x + b->xoff2 - b->xoff; - q->y1 = y + b->yoff2 - b->yoff; - } else { - q->x0 = *xpos + b->xoff; - q->y0 = *ypos + b->yoff; - q->x1 = *xpos + b->xoff2; - q->y1 = *ypos + b->yoff2; - } - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - - -/*//////////////////////////////////////////////////////////////////////////// */ -/* */ -/* font name matching -- recommended not to use this */ -/* */ - -/* check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string */ -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) -{ - stbtt_int32 i=0; - - /* convert utf16 to utf8 and compare the results while converting */ - while (len2) { - stbtt_uint16 ch = s2[0]*256 + s2[1]; - if (ch < 0x80) { - if (i >= len1) return -1; - if (s1[i++] != ch) return -1; - } else if (ch < 0x800) { - if (i+1 >= len1) return -1; - if (s1[i++] != 0xc0 + (ch >> 6)) return -1; - if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; - } else if (ch >= 0xd800 && ch < 0xdc00) { - stbtt_uint32 c; - stbtt_uint16 ch2 = s2[2]*256 + s2[3]; - if (i+3 >= len1) return -1; - c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; - if (s1[i++] != 0xf0 + (c >> 18)) return -1; - if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; - s2 += 2; /* plus another 2 below */ - len2 -= 2; - } else if (ch >= 0xdc00 && ch < 0xe000) { - return -1; - } else { - if (i+2 >= len1) return -1; - if (s1[i++] != 0xe0 + (ch >> 12)) return -1; - if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; - } - s2 += 2; - len2 -= 2; - } - return i; -} - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) -{ - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); -} - -/* returns results in whatever encoding you request... but note that 2-byte encodings */ -/* will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare */ -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) -{ - stbtt_int32 i,count,stringOffset; - stbtt_uint8 *fc = font->data; - stbtt_uint32 offset = font->fontstart; - stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return NULL; - - count = ttUSHORT(fc+nm+2); - stringOffset = nm + ttUSHORT(fc+nm+4); - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) - && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { - *length = ttUSHORT(fc+loc+8); - return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); - } - } - return NULL; -} - -static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) -{ - stbtt_int32 i; - stbtt_int32 count = ttUSHORT(fc+nm+2); - stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); - - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - stbtt_int32 id = ttUSHORT(fc+loc+6); - if (id == target_id) { - /* find the encoding */ - stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); - - /* is this a Unicode encoding? */ - if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { - stbtt_int32 slen = ttUSHORT(fc+loc+8); - stbtt_int32 off = ttUSHORT(fc+loc+10); - - /* check if there's a prefix match */ - stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); - if (matchlen >= 0) { - /* check for target_id+1 immediately following, with same encoding & language */ - if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { - slen = ttUSHORT(fc+loc+12+8); - off = ttUSHORT(fc+loc+12+10); - if (slen == 0) { - if (matchlen == nlen) - return 1; - } else if (matchlen < nlen && name[matchlen] == ' ') { - ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) - return 1; - } - } else { - /* if nothing immediately following */ - if (matchlen == nlen) - return 1; - } - } - } - - /* @TODO handle other encodings */ - } - } - return 0; -} - -static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) -{ - stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); - stbtt_uint32 nm,hd; - if (!stbtt__isfont(fc+offset)) return 0; - - /* check italics/bold/underline flags in macStyle... */ - if (flags) { - hd = stbtt__find_table(fc, offset, "head"); - if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; - } - - nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return 0; - - if (flags) { - /* if we checked the macStyle flags, then just check the family and ignore the subfamily */ - if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } else { - if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } - - return 0; -} - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) -{ - stbtt_int32 i; - for (i=0;;++i) { - stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); - if (off < 0) return off; - if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) - return off; - } -} - -#endif /* STB_TRUETYPE_IMPLEMENTATION */ - - -/* FULL VERSION HISTORY */ -/* */ -/* 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; */ -/* allow PackFontRanges to pack and render in separate phases; */ -/* fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); */ -/* fixed an assert() bug in the new rasterizer */ -/* replace assert() with STBTT_assert() in new rasterizer */ -/* 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) */ -/* also more precise AA rasterizer, except if shapes overlap */ -/* remove need for STBTT_sort */ -/* 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC */ -/* 1.04 (2015-04-15) typo in example */ -/* 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes */ -/* 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ */ -/* 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match */ -/* non-oversampled; STBTT_POINT_SIZE for packed case only */ -/* 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling */ -/* 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) */ -/* 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID */ -/* 0.8b (2014-07-07) fix a warning */ -/* 0.8 (2014-05-25) fix a few more warnings */ -/* 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back */ -/* 0.6c (2012-07-24) improve documentation */ -/* 0.6b (2012-07-20) fix a few more warnings */ -/* 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, */ -/* stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty */ -/* 0.5 (2011-12-09) bugfixes: */ -/* subpixel glyph renderer computed wrong bounding box */ -/* first vertex of shape can be off-curve (FreeSans) */ -/* 0.4b (2011-12-03) fixed an error in the font baking example */ -/* 0.4 (2011-12-01) kerning, subpixel rendering (tor) */ -/* bugfixes for: */ -/* codepoint-to-glyph conversion using table fmt=12 */ -/* codepoint-to-glyph conversion using table fmt=4 */ -/* stbtt_GetBakedQuad with non-square texture (Zer) */ -/* updated Hello World! sample to use kerning and subpixel */ -/* fixed some warnings */ -/* 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) */ -/* userdata, malloc-from-userdata, non-zero fill (stb) */ -/* 0.2 (2009-03-11) Fix unsigned/signed char warnings */ -/* 0.1 (2009-03-09) First public release */ -/* */ diff --git a/zahnrad.c b/zahnrad.c deleted file mode 100644 index 9f5bd19..0000000 --- a/zahnrad.c +++ /dev/null @@ -1,14191 +0,0 @@ -/* - Copyright (c) 2016 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 "zahnrad.h" - -/* ============================================================== - * - * INTERNAL - * - * =============================================================== */ -#define ZR_POOL_DEFAULT_CAPACITY 16 -#define ZR_VALUE_PAGE_CAPACITY 32 -#define ZR_DEFAULT_COMMAND_BUFFER_SIZE (4*1024) - -#if ZR_COMPILE_WITH_DEFAULT_ALLOCATOR -#include /* malloc, free */ -#endif -#if ZR_COMPILE_WITH_STANDARD_IO -#include /* fopen, fclose,... */ -#include -#endif -/* ============================================================== - * MATH - * =============================================================== */ -#define ZR_MIN(a,b) ((a) < (b) ? (a) : (b)) -#define ZR_MAX(a,b) ((a) < (b) ? (b) : (a)) -#define ZR_CLAMP(i,v,x) (ZR_MAX(ZR_MIN(v,x), i)) - -#define ZR_PI 3.141592654f -#define ZR_UTF_INVALID 0xFFFD -#define ZR_MAX_FLOAT_PRECISION 2 - -#define ZR_UNUSED(x) ((void)(x)) -#define ZR_SATURATE(x) (ZR_MAX(0, ZR_MIN(1.0f, x))) -#define ZR_LEN(a) (sizeof(a)/sizeof(a)[0]) -#define ZR_ABS(a) (((a) < 0) ? -(a) : (a)) -#define ZR_BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) -#define ZR_INBOX(px, py, x, y, w, h)\ - (ZR_BETWEEN(px,x,x+w) && ZR_BETWEEN(py,y,y+h)) -#define ZR_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \ - (!(((x1 > (x0 + w0)) || ((x1 + w1) < x0) || (y1 > (y0 + h0)) || (y1 + h1) < y0))) -#define ZR_CONTAINS(x, y, w, h, bx, by, bw, bh)\ - (ZR_INBOX(x,y, bx, by, bw, bh) && ZR_INBOX(x+w,y+h, bx, by, bw, bh)) - -#define zr_vec2_mov(to,from) (to).x = (from).x, (to).y = (from).y -#define zr_vec2_sub(a, b) zr_vec2((a).x - (b).x, (a).y - (b).y) -#define zr_vec2_add(a, b) zr_vec2((a).x + (b).x, (a).y + (b).y) -#define zr_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y) -#define zr_vec2_muls(a, t) zr_vec2((a).x * (t), (a).y * (t)) - -#define zr_ptr_add(t, p, i) ((t*)((void*)((zr_byte*)(p) + (i)))) -#define zr_ptr_add_const(t, p, i) ((const t*)((const void*)((const zr_byte*)(p) + (i)))) -#define zr_zero_struct(s) zr_zero(&s, sizeof(s)) - -/* ============================================================== - * ALIGNMENT - * =============================================================== */ -/* Pointer to Integer type conversion for pointer alignment */ -#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC*/ -# define ZR_UINT_TO_PTR(x) ((void*)(__PTRDIFF_TYPE__)(x)) -# define ZR_PTR_TO_UINT(x) ((zr_size)(__PTRDIFF_TYPE__)(x)) -#elif !defined(__GNUC__) /* works for compilers other than LLVM */ -# define ZR_UINT_TO_PTR(x) ((void*)&((char*)0)[x]) -# define ZR_PTR_TO_UINT(x) ((zr_size)(((char*)x)-(char*)0)) -#elif defined(ZR_USE_FIXED_TYPES) /* used if we have */ -# define ZR_UINT_TO_PTR(x) ((void*)(uintptr_t)(x)) -# define ZR_PTR_TO_UINT(x) ((uintptr_t)(x)) -#else /* generates warning but works */ -# define ZR_UINT_TO_PTR(x) ((void*)(x)) -# define ZR_PTR_TO_UINT(x) ((zr_size)(x)) -#endif - -#define ZR_ALIGN_PTR(x, mask)\ - (ZR_UINT_TO_PTR((ZR_PTR_TO_UINT((zr_byte*)(x) + (mask-1)) & ~(mask-1)))) -#define ZR_ALIGN_PTR_BACK(x, mask)\ - (ZR_UINT_TO_PTR((ZR_PTR_TO_UINT((zr_byte*)(x)) & ~(mask-1)))) - -#ifdef __cplusplus -template struct zr_alignof; -template struct zr_helper{enum {value = size_diff};}; -template struct zr_helper{enum {value = zr_alignof::value};}; -template struct zr_alignof{struct Big {T x; char c;}; enum { - diff = sizeof(Big) - sizeof(T), value = zr_helper::value};}; -#define ZR_ALIGNOF(t) (zr_alignof::value); -#else -#define ZR_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0) -#endif - -/* make sure correct type size */ -typedef int zr__check_size[(sizeof(zr_size) >= sizeof(void*)) ? 1 : -1]; -typedef int zr__check_ptr[(sizeof(zr_ptr) == sizeof(void*)) ? 1 : -1]; -typedef int zr__check_flags[(sizeof(zr_flags) >= 4) ? 1 : -1]; -typedef int zr__check_rune[(sizeof(zr_rune) >= 4) ? 1 : -1]; -typedef int zr__check_uint[(sizeof(zr_uint) == 4) ? 1 : -1]; -typedef int zr__check_byte[(sizeof(zr_byte) == 1) ? 1 : -1]; - -static const struct zr_rect zr_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; -static const float FLOAT_PRECISION = 0.00000000000001f; -/* - * ============================================================== - * - * MATH - * - * =============================================================== - */ -/* Since zahnrad is supposed to work on all systems providing floating point - math without any dependencies I also had to implement my own math functions - for sqrt, sin and cos. Since the actual highly accurate implementations for - the standard library functions are quite complex and I do not need high - precision for my use cases I use approximations. - - Sqrt - ---- - For square root zahnrad uses the famous fast inverse square root: - https://en.wikipedia.org/wiki/Fast_inverse_square_root with - slightly tweaked magic constant. While on todays hardware it is - probably not faster it is still fast and accurate enough for - zahnrads use cases. - - Sine/Cosine - ----------- - All constants inside both function are generated Remez's minimax - approximations for value range 0...2*PI. The reason why I decided to - approximate exactly that range is that zahnrad only needs sine and - cosine to generate circles which only requires that exact range. - In addition I used Remez instead of Taylor for additional precision: - www.lolengine.net/blog/2011/12/21/better-function-approximatations. - - The tool I used to generate constants for both sine and cosine - (it can actually approximate a lot more functions) can be - found here: www.lolengine.net/wiki/oss/lolremez -*/ -static float -zr_inv_sqrt(float number) -{ - float x2; - const float threehalfs = 1.5f; - union {zr_uint i; float f;} conv = {0}; - conv.f = number; - x2 = number * 0.5f; - conv.i = 0x5f375A84 - (conv.i >> 1); - conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); - return conv.f; -} - -static float -zr_sin(float x) -{ - static const float a0 = +1.91059300966915117e-31f; - static const float a1 = +1.00086760103908896f; - static const float a2 = -1.21276126894734565e-2f; - static const float a3 = -1.38078780785773762e-1f; - static const float a4 = -2.67353392911981221e-2f; - static const float a5 = +2.08026600266304389e-2f; - static const float a6 = -3.03996055049204407e-3f; - static const float a7 = +1.38235642404333740e-4f; - return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); -} - -static float -zr_cos(float x) -{ - static const float a0 = +1.00238601909309722f; - static const float a1 = -3.81919947353040024e-2f; - static const float a2 = -3.94382342128062756e-1f; - static const float a3 = -1.18134036025221444e-1f; - static const float a4 = +1.07123798512170878e-1f; - static const float a5 = -1.86637164165180873e-2f; - static const float a6 = +9.90140908664079833e-4f; - static const float a7 = -5.23022132118824778e-14f; - return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); -} - -static zr_uint -zr_round_up_pow2(zr_uint v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -} - -struct zr_rect -zr_get_null_rect(void) -{ - return zr_null_rect; -} - -struct zr_rect -zr_rect(float x, float y, float w, float h) -{ - struct zr_rect r; - r.x = x, r.y = y; - r.w = w, r.h = h; - return r; -} - - -struct zr_rect -zr_recti(int x, int y, int w, int h) -{ - struct zr_rect r; - r.x = (float)x; - r.y = (float)y; - r.w = (float)w; - r.h = (float)h; - return r; -} - -struct zr_rect -zr_recta(struct zr_vec2 pos, struct zr_vec2 size) -{ - return zr_rect(pos.x, pos.y, size.x, size.y); -} - -struct zr_rect -zr_rectv(const float *r) -{ - return zr_rect(r[0], r[1], r[2], r[3]); -} - -struct zr_rect -zr_rectiv(const int *r) -{ - return zr_recti(r[0], r[1], r[2], r[3]); -} - -static struct zr_rect -zr_shrink_rect(struct zr_rect r, float amount) -{ - struct zr_rect res; - r.w = ZR_MAX(r.w, 2 * amount); - r.h = ZR_MAX(r.h, 2 * amount); - res.x = r.x + amount; - res.y = r.y + amount; - res.w = r.w - 2 * amount; - res.h = r.h - 2 * amount; - return res; -} - -static struct zr_rect -zr_pad_rect(struct zr_rect r, struct zr_vec2 pad) -{ - r.w = ZR_MAX(r.w, 2 * pad.x); - r.h = ZR_MAX(r.h, 2 * pad.y); - r.x += pad.x; r.y += pad.y; - r.w -= 2 * pad.x; - r.h -= 2 * pad.y; - return r; -} - -struct zr_vec2 -zr_vec2(float x, float y) -{ - struct zr_vec2 ret; - ret.x = x; ret.y = y; - return ret; -} - -struct zr_vec2 -zr_vec2i(int x, int y) -{ - struct zr_vec2 ret; - ret.x = (float)x; - ret.y = (float)y; - return ret; -} - -struct zr_vec2 -zr_vec2v(const float *v) -{ - return zr_vec2(v[0], v[1]); -} - -struct zr_vec2 -zr_vec2iv(const int *v) -{ - return zr_vec2i(v[0], v[1]); -} -/* - * ============================================================== - * - * UTIL - * - * =============================================================== - */ -static int zr_str_match_here(const char *regexp, const char *text); -static int zr_str_match_star(int c, const char *regexp, const char *text); -static int zr_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} -static int zr_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} -static int zr_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} -static int zr_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} - -static void* -zr_memcopy(void *dst0, const void *src0, zr_size length) -{ - zr_ptr t; - typedef int word; - char *dst = (char*)dst0; - const char *src = (const char*)src0; - if (length == 0 || dst == src) - goto done; - - #define wsize sizeof(word) - #define wmask (wsize-1) - #define TLOOP(s) if (t) TLOOP1(s) - #define TLOOP1(s) do { s; } while (--t) - - if (dst < src) { - t = (zr_ptr)src; /* only need low bits */ - if ((t | (zr_ptr)dst) & wmask) { - if ((t ^ (zr_ptr)dst) & wmask || length < wsize) - t = length; - else - t = wsize - (t & wmask); - length -= t; - TLOOP1(*dst++ = *src++); - } - t = length / wsize; - TLOOP(*(word*)(void*)dst = *(const word*)(const void*)src; - src += wsize; dst += wsize); - t = length & wmask; - TLOOP(*dst++ = *src++); - } else { - src += length; - dst += length; - t = (zr_ptr)src; - if ((t | (zr_ptr)dst) & wmask) { - if ((t ^ (zr_ptr)dst) & wmask || length <= wsize) - t = length; - else - t &= wmask; - length -= t; - TLOOP1(*--dst = *--src); - } - t = length / wsize; - TLOOP(src -= wsize; dst -= wsize; - *(word*)(void*)dst = *(const word*)(const void*)src); - t = length & wmask; - TLOOP(*--dst = *--src); - } - #undef wsize - #undef wmask - #undef TLOOP - #undef TLOOP1 -done: - return (dst0); -} - -static void -zr_memset(void *ptr, int c0, zr_size size) -{ - #define word unsigned - #define wsize sizeof(word) - #define wmask (wsize - 1) - zr_byte *dst = (zr_byte*)ptr; - unsigned c = 0; - zr_size t = 0; - - if ((c = (zr_byte)c0) != 0) { - c = (c << 8) | c; /* at least 16-bits */ - if (sizeof(unsigned int) > 2) - c = (c << 16) | c; /* at least 32-bits*/ - } - - /* to small of a word count */ - dst = (zr_byte*)ptr; - if (size < 3 * wsize) { - while (size--) *dst++ = (zr_byte)c0; - return; - } - - /* align destination */ - if ((t = ZR_PTR_TO_UINT(dst) & wmask) != 0) { - t = wsize -t; - size -= t; - do { - *dst++ = (zr_byte)c0; - } while (--t != 0); - } - - /* fill word */ - t = size / wsize; - do { - *(word*)((void*)dst) = c; - dst += wsize; - } while (--t != 0); - - /* fill trailing bytes */ - t = (size & wmask); - if (t != 0) { - do { - *dst++ = (zr_byte)c0; - } while (--t != 0); - } - - #undef word - #undef wsize - #undef wmask -} - -static void -zr_zero(void *ptr, zr_size size) -{ - ZR_ASSERT(ptr); - zr_memset(ptr, 0, size); -} - -zr_size -zr_strlen(const char *str) -{ - zr_size siz = 0; - ZR_ASSERT(str); - while (str && *str++ != '\0') siz++; - return siz; -} - -int -zr_strtof(float *number, const char *buffer) -{ - float m; - float neg = 1.0f; - const char *p = buffer; - float floatvalue = 0; - - ZR_ASSERT(number); - ZR_ASSERT(buffer); - if (!number || !buffer) return 0; - *number = 0; - - /* skip whitespace */ - while (*p && *p == ' ') p++; - if (*p == '-') { - neg = -1.0f; - p++; - } - - while( *p && *p != '.' && *p != 'e' ) { - floatvalue = floatvalue * 10.0f + (float) (*p - '0'); - p++; - } - - if ( *p == '.' ) { - p++; - for(m = 0.1f; *p && *p != 'e'; p++ ) { - floatvalue = floatvalue + (float) (*p - '0') * m; - m *= 0.1f; - } - } - if ( *p == 'e' ) { - int i, pow, div; - p++; - if ( *p == '-' ) { - div = zr_true; - p++; - } else if ( *p == '+' ) { - div = zr_false; - p++; - } else div = zr_false; - - for ( pow = 0; *p; p++ ) - pow = pow * 10 + (int) (*p - '0'); - - for ( m = 1.0, i = 0; i < pow; i++ ) - m *= 10.0f; - - if ( div ) - floatvalue /= m; - else floatvalue *= m; - } - *number = floatvalue * neg; - return 1; -} - -int -zr_stricmp(const char *s1, const char *s2) -{ - zr_int c1,c2,d; - do { - c1 = *s1++; - c2 = *s2++; - d = c1 - c2; - while (d) { - if (c1 <= 'Z' && c1 >= 'A') { - d += ('a' - 'A'); - if (!d) break; - } - if (c2 <= 'Z' && c2 >= 'A') { - d -= ('a' - 'A'); - if (!d) break; - } - return ((d >= 0) << 1) - 1; - } - } while (c1); - return 0; -} - -int -zr_stricmpn(const char *s1, const char *s2, int n) -{ - int c1,c2,d; - assert(n >= 0); - do { - c1 = *s1++; - c2 = *s2++; - if (!n--) return 0; - - d = c1 - c2; - while (d) { - if (c1 <= 'Z' && c1 >= 'A') { - d += ('a' - 'A'); - if (!d) break; - } - if (c2 <= 'Z' && c2 >= 'A') { - d -= ('a' - 'A'); - if (!d) break; - } - return ((d >= 0) << 1) - 1; - } - } while (c1); - return 0; -} - -static int -zr_str_match_here(const char *regexp, const char *text) -{ - if (regexp[0] == '\0') - return 1; - if (regexp[1] == '*') - return zr_str_match_star(regexp[0], regexp+2, text); - if (regexp[0] == '$' && regexp[1] == '\0') - return *text == '\0'; - if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) - return zr_str_match_here(regexp+1, text+1); - return 0; -} - -static int -zr_str_match_star(int c, const char *regexp, const char *text) -{ - do {/* a '* matches zero or more instances */ - if (zr_str_match_here(regexp, text)) - return 1; - } while (*text != '\0' && (*text++ == c || c == '.')); - return 0; -} - -int -zr_strfilter(const char *text, const char *regexp) -{ - /* - c matches any literal character c - . matches any single character - ^ matches the beginning of the input string - $ matches the end of the input string - * matches zero or more occurrences of the previous character*/ - if (regexp[0] == '^') - return zr_str_match_here(regexp+1, text); - do { /* must look even if string is empty */ - if (zr_str_match_here(regexp, text)) - return 1; - } while (*text++ != '\0'); - return 0; -} - -int -zr_strmatch_fuzzy_text(const char *str, int str_len, - const char *pattern, int *out_score) -{ - /* Returns true if each character in pattern is found sequentially within str - * if found then outScore is also set. Score value has no intrinsic meaning. - * Range varies with pattern. Can only compare scores with same search pattern. */ - - /* ------- scores --------- */ - /* bonus for adjacent matches */ - #define ZR_ADJACENCY_BONUS 5 - /* bonus if match occurs after a separator */ - #define ZR_SEPARATOR_BONUS 10 - /* bonus if match is uppercase and prev is lower */ - #define ZR_CAMEL_BONUS 10 - /* penalty applied for every letter in str before the first match */ - #define ZR_LEADING_LETTER_PENALTY (-3) - /* maximum penalty for leading letters */ - #define ZR_MAX_LEADING_LETTER_PENALTY (-9) - /* penalty for every letter that doesn't matter */ - #define ZR_UNMATCHED_LETTER_PENALTY (-1) - - /* loop variables */ - int score = 0; - char const * pattern_iter = pattern; - int str_iter = 0; - int prev_matched = zr_false; - int prev_lower = zr_false; - /* true so if first letter match gets separator bonus*/ - int prev_separator = zr_true; - - /* use "best" matched letter if multiple string letters match the pattern */ - char const * best_letter = 0; - int best_letter_score = 0; - - /* loop over strings */ - while (str_iter < str_len) - { - const char pattern_letter = *pattern_iter; - const char str_letter = str[str_iter]; - - int next_match = *pattern_iter != '\0' && - zr_to_lower(pattern_letter) == zr_to_lower(str_letter); - int rematch = best_letter && zr_to_lower(*best_letter) == zr_to_lower(str_letter); - - int advanced = next_match && best_letter; - int pattern_repeat = best_letter && pattern_iter != '\0'; - pattern_repeat = pattern_repeat && - zr_to_lower(*best_letter) == zr_to_lower(pattern_letter); - - if (advanced || pattern_repeat) { - score += best_letter_score; - best_letter = 0; - best_letter_score = 0; - } - - if (next_match || rematch) - { - int new_score = 0; - /* Apply penalty for each letter before the first pattern match */ - if (pattern_iter == pattern) - { - int count = (int)(&str[str_iter] - str); - int penalty = ZR_LEADING_LETTER_PENALTY * count; - if (penalty < ZR_MAX_LEADING_LETTER_PENALTY) - penalty = ZR_MAX_LEADING_LETTER_PENALTY; - - score += penalty; - } - - /* apply bonus for consecutive bonuses */ - if (prev_matched) - new_score += ZR_ADJACENCY_BONUS; - - /* apply bonus for matches after a separator */ - if (prev_separator) - new_score += ZR_SEPARATOR_BONUS; - - /* apply bonus across camel case boundaries */ - if (prev_lower && zr_is_upper(str_letter)) - new_score += ZR_CAMEL_BONUS; - - /* update pattern iter IFF the next pattern letter was matched */ - if (next_match) - ++pattern_iter; - - /* update best letter in str which may be for a "next" letter or a rematch */ - if (new_score >= best_letter_score) - { - /* apply penalty for now skipped letter */ - if (best_letter != 0) - score += ZR_UNMATCHED_LETTER_PENALTY; - - best_letter = &str[str_iter]; - best_letter_score = new_score; - } - - prev_matched = zr_true; - } - else - { - score += ZR_UNMATCHED_LETTER_PENALTY; - prev_matched = zr_false; - } - - /* separators should be more easily defined */ - prev_lower = zr_is_lower(str_letter) != 0; - prev_separator = str_letter == '_' || str_letter == ' '; - - ++str_iter; - } - - /* apply score for last match */ - if (best_letter) - score += best_letter_score; - - /* did not match full pattern */ - if (*pattern_iter != '\0') - return zr_false; - - if (out_score) - *out_score = score; - return zr_true; -} - -#if ZR_COMPILE_WITH_STANDARD_IO -int -zr_strfmt(char *buf, zr_size buf_size, const char *fmt,...) -{ - int w; - va_list args; - va_start(args, fmt); - w = vsnprintf(buf, buf_size, fmt, args); - va_end(args); - buf[buf_size-1] = 0; - return (w == -1) ?(int)buf_size:w; -} - -static int -zr_strfmtv(char *buf, zr_size buf_size, const char *fmt, va_list args) -{ - int w = vsnprintf(buf, buf_size, fmt, args); - buf[buf_size-1] = 0; - return (w == -1) ? (int)buf_size:w; -} -#endif - -static float -zr_pow(float x, int n) -{ - /* check the sign of n */ - float r = 1; - int plus = n >= 0; - n = (plus) ? n : -n; - while (n > 0) { - if ((n & 1) == 1) - r *= x; - n /= 2; - x *= x; - } - return plus ? r : 1.0f / r; -} - -static float -zr_floor(float x) -{ - return (float)((int)x - ((x < 0.0) ? 1 : 0)); -} - -static int -zr_log10(float n) -{ - int neg; - int ret; - int exp = 0; - - neg = (n < 0) ? 1 : 0; - ret = (neg) ? (int)-n : (int)n; - while ((ret / 10) > 0) { - ret /= 10; - exp++; - } - if (neg) exp = -exp; - return exp; -} - -static zr_size -zr_ftos(char *s, float n) -{ - int useExp = 0; - int digit = 0, m = 0, m1 = 0; - char *c = s; - int neg = 0; - - if (n == 0.0) { - s[0] = '0'; s[1] = '\0'; - return 1; - } - - neg = (n < 0); - if (neg) n = -n; - - /* calculate magnitude */ - m = zr_log10(n); - useExp = (m >= 14 || (neg && m >= 9) || m <= -9); - if (neg) *(c++) = '-'; - - /* set up for scientific notation */ - if (useExp) { - if (m < 0) - m -= 1; - n = n / zr_pow(10.0, m); - m1 = m; - m = 0; - } - if (m < 1.0) { - m = 0; - } - - /* convert the number */ - while (n > FLOAT_PRECISION || m >= 0) { - float weight = zr_pow(10.0, m); - if (weight > 0) { - float t = (float)n / weight; - float tmp = zr_floor(t); - digit = (int)tmp; - n -= ((float)digit * weight); - *(c++) = (char)('0' + (char)digit); - } - if (m == 0 && n > 0) - *(c++) = '.'; - m--; - } - - if (useExp) { - /* convert the exponent */ - int i, j; - *(c++) = 'e'; - if (m1 > 0) { - *(c++) = '+'; - } else { - *(c++) = '-'; - m1 = -m1; - } - m = 0; - while (m1 > 0) { - *(c++) = (char)('0' + (char)(m1 % 10)); - m1 /= 10; - m++; - } - c -= m; - for (i = 0, j = m-1; i> (32 - r))) - union {const zr_uint *i; const zr_byte *b;} conv = {0}; - const zr_byte *data = (const zr_byte*)key; - const int nblocks = len/4; - zr_uint h1 = seed; - const zr_uint c1 = 0xcc9e2d51; - const zr_uint c2 = 0x1b873593; - const zr_byte *tail; - const zr_uint *blocks; - zr_uint k1; - int i; - - /* body */ - if (!key) return 0; - conv.b = (data + nblocks*4); - blocks = (const zr_uint*)conv.i; - for (i = -nblocks; i; ++i) { - k1 = blocks[i]; - k1 *= c1; - k1 = ZR_ROTL(k1,15); - k1 *= c2; - - h1 ^= k1; - h1 = ZR_ROTL(h1,13); - h1 = h1*5+0xe6546b64; - } - - /* tail */ - tail = (const zr_byte*)(data + nblocks*4); - k1 = 0; - switch (len & 3) { - case 3: k1 ^= (zr_uint)(tail[2] << 16); - case 2: k1 ^= (zr_uint)(tail[1] << 8u); - case 1: k1 ^= tail[0]; - k1 *= c1; - k1 = ZR_ROTL(k1,15); - k1 *= c2; - h1 ^= k1; - default: break; - } - - /* finalization */ - h1 ^= (zr_uint)len; - /* fmix32 */ - h1 ^= h1 >> 16; - h1 *= 0x85ebca6b; - h1 ^= h1 >> 13; - h1 *= 0xc2b2ae35; - h1 ^= h1 >> 16; - - #undef ZR_ROTL - return h1; -} - -#if ZR_COMPILE_WITH_STANDARD_IO -static char* -zr_file_load(const char* path, zr_size* siz, struct zr_allocator *alloc) -{ - char *buf; - FILE *fd; - - ZR_ASSERT(path); - ZR_ASSERT(siz); - ZR_ASSERT(alloc); - if (!path || !siz || !alloc) - return 0; - - fd = fopen(path, "rb"); - if (!fd) return 0; - fseek(fd, 0, SEEK_END); - *siz = (size_t)ftell(fd); - fseek(fd, 0, SEEK_SET); - buf = (char*)alloc->alloc(alloc->userdata, *siz); - ZR_ASSERT(buf); - if (!buf) { - fclose(fd); - return 0; - } - fread(buf, *siz, 1, fd); - fclose(fd); - return buf; -} -#endif - -/* - * ============================================================== - * - * COLOR - * - * =============================================================== - */ -static int -zr_parse_hex(const char *p, int length) -{ - int i = 0; - int len = 0; - while (len < length) { - i <<= 4; - if (p[len] >= 'a' && p[len] <= 'f') - i += ((p[len] - 'a') + 10); - else if (p[len] >= 'A' && p[len] <= 'F') { - i += ((p[len] - 'A') + 10); - } else i += (p[len] - '0'); - len++; - } - return i; -} - -struct zr_color -zr_rgba(int r, int g, int b, int a) -{ - struct zr_color ret; - ret.r = (zr_byte)ZR_CLAMP(0, r, 255); - ret.g = (zr_byte)ZR_CLAMP(0, g, 255); - ret.b = (zr_byte)ZR_CLAMP(0, b, 255); - ret.a = (zr_byte)ZR_CLAMP(0, a, 255); - return ret; -} - -struct zr_color -zr_rgb_hex(const char *rgb) -{ - struct zr_color col; - const char *c = rgb; - if (*c == '#') c++; - col.r = (zr_byte)zr_parse_hex(c, 2); - col.g = (zr_byte)zr_parse_hex(c+2, 2); - col.b = (zr_byte)zr_parse_hex(c+4, 2); - col.a = 255; - return col; -} - -struct zr_color -zr_rgba_hex(const char *rgb) -{ - struct zr_color col; - const char *c = rgb; - if (*c == '#') c++; - col.r = (zr_byte)zr_parse_hex(c, 2); - col.g = (zr_byte)zr_parse_hex(c+2, 2); - col.b = (zr_byte)zr_parse_hex(c+4, 2); - col.a = (zr_byte)zr_parse_hex(c+6, 2); - return col; -} - -void -zr_color_hex_rgba(char *output, struct zr_color col) -{ - #define ZR_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) - output[0] = (char)ZR_TO_HEX((col.r & 0x0F)); - output[1] = (char)ZR_TO_HEX((col.r & 0xF0) >> 4); - output[2] = (char)ZR_TO_HEX((col.g & 0x0F)); - output[3] = (char)ZR_TO_HEX((col.g & 0xF0) >> 4); - output[4] = (char)ZR_TO_HEX((col.b & 0x0F)); - output[5] = (char)ZR_TO_HEX((col.b & 0xF0) >> 4); - output[6] = (char)ZR_TO_HEX((col.a & 0x0F)); - output[7] = (char)ZR_TO_HEX((col.a & 0xF0) >> 4); - output[8] = '\0'; - #undef ZR_TO_HEX -} - -void -zr_color_hex_rgb(char *output, struct zr_color col) -{ - #define ZR_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) - output[0] = (char)ZR_TO_HEX((col.r & 0x0F)); - output[1] = (char)ZR_TO_HEX((col.r & 0xF0) >> 4); - output[2] = (char)ZR_TO_HEX((col.g & 0x0F)); - output[3] = (char)ZR_TO_HEX((col.g & 0xF0) >> 4); - output[4] = (char)ZR_TO_HEX((col.b & 0x0F)); - output[5] = (char)ZR_TO_HEX((col.b & 0xF0) >> 4); - output[6] = '\0'; - #undef ZR_TO_HEX -} - -struct zr_color -zr_rgba_iv(const int *c) -{ - return zr_rgba(c[0], c[1], c[2], c[3]); -} - -struct zr_color -zr_rgba_bv(const zr_byte *c) -{ - return zr_rgba(c[0], c[1], c[2], c[3]); -} - -struct zr_color -zr_rgb(int r, int g, int b) -{ - struct zr_color ret; - ret.r =(zr_byte)ZR_CLAMP(0, r, 255); - ret.g =(zr_byte)ZR_CLAMP(0, g, 255); - ret.b =(zr_byte)ZR_CLAMP(0, b, 255); - ret.a =(zr_byte)255; - return ret; -} - -struct zr_color -zr_rgb_iv(const int *c) -{ - return zr_rgb(c[0], c[1], c[2]); -} - -struct zr_color -zr_rgb_bv(const zr_byte* c) -{ - return zr_rgb(c[0], c[1], c[2]); -} - -struct zr_color -zr_rgba_u32(zr_uint in) -{ - struct zr_color ret; - ret.r = (in & 0xFF); - ret.g = ((in >> 8) & 0xFF); - ret.b = ((in >> 16) & 0xFF); - ret.a = (zr_byte)((in >> 24) & 0xFF); - return ret; -} - -struct zr_color -zr_rgba_f(float r, float g, float b, float a) -{ - struct zr_color ret; - ret.r = (zr_byte)(ZR_SATURATE(r) * 255.0f); - ret.g = (zr_byte)(ZR_SATURATE(g) * 255.0f); - ret.b = (zr_byte)(ZR_SATURATE(b) * 255.0f); - ret.a = (zr_byte)(ZR_SATURATE(a) * 255.0f); - return ret; -} - -struct zr_color -zr_rgba_fv(const float *c) -{ - return zr_rgba_f(c[0], c[1], c[2], c[3]); -} - -struct zr_color -zr_rgb_f(float r, float g, float b) -{ - struct zr_color ret; - ret.r = (zr_byte)(ZR_SATURATE(r) * 255.0f); - ret.g = (zr_byte)(ZR_SATURATE(g) * 255.0f); - ret.b = (zr_byte)(ZR_SATURATE(b) * 255.0f); - ret.a = 255; - return ret; -} - -struct zr_color -zr_rgb_fv(const float *c) -{ - return zr_rgb_f(c[0], c[1], c[2]); -} - -struct zr_color -zr_hsv(int h, int s, int v) -{ - return zr_hsva(h, s, v, 255); -} - -struct zr_color -zr_hsv_iv(const int *c) -{ - return zr_hsv(c[0], c[1], c[2]); -} - -struct zr_color -zr_hsv_bv(const zr_byte *c) -{ - return zr_hsv(c[0], c[1], c[2]); -} - -struct zr_color -zr_hsv_f(float h, float s, float v) -{ - return zr_hsva_f(h, s, v, 1.0f); -} - -struct zr_color -zr_hsv_fv(const float *c) -{ - return zr_hsv_f(c[0], c[1], c[2]); -} - -struct zr_color -zr_hsva(int h, int s, int v, int a) -{ - float hf = ((float)ZR_CLAMP(0, h, 255)) / 255.0f; - float sf = ((float)ZR_CLAMP(0, s, 255)) / 255.0f; - float vf = ((float)ZR_CLAMP(0, v, 255)) / 255.0f; - float af = ((float)ZR_CLAMP(0, a, 255)) / 255.0f; - return zr_hsva_f(hf, sf, vf, af); -} - -struct zr_color -zr_hsva_iv(const int *c) -{ - return zr_hsva(c[0], c[1], c[2], c[3]); -} - -struct zr_color -zr_hsva_bv(const zr_byte *c) -{ - return zr_hsva(c[0], c[1], c[2], c[3]); -} - -struct zr_color -zr_hsva_f(float h, float s, float v, float a) -{ - struct zr_colorf {float r,g,b;} out = {0,0,0}; - float p, q, t, f; - int i; - - if (s <= 0.0f) { - out.r = v; out.g = v; out.b = v; - return zr_rgb_f(out.r, out.g, out.b); - } - - h = h / (60.0f/360.0f); - i = (int)h; - f = h - (float)i; - p = v * (1.0f - s); - q = v * (1.0f - (s * f)); - t = v * (1.0f - s * (1.0f - f)); - - switch (i) { - case 0: out.r = v; out.g = t; out.b = p; break; - case 1: out.r = q; out.g = v; out.b = p; break; - case 2: out.r = p; out.g = v; out.b = t; break; - case 3: out.r = p; out.g = q; out.b = v; break; - case 4: out.r = t; out.g = p; out.b = v; break; - case 5: default: out.r = v; out.g = p; out.b = q; break; - } - return zr_rgba_f(out.r, out.g, out.b, a); -} - -struct zr_color -zr_hsva_fv(const float *c) -{ - return zr_hsva_f(c[0], c[1], c[2], c[3]); -} - -zr_uint -zr_color_u32(struct zr_color in) -{ - zr_uint out = (zr_uint)in.r; - out |= ((zr_uint)in.g << 8); - out |= ((zr_uint)in.b << 16); - out |= ((zr_uint)in.a << 24); - return out; -} - -void -zr_color_f(float *r, float *g, float *b, float *a, struct zr_color in) -{ - static const float s = 1.0f/255.0f; - *r = (float)in.r * s; - *g = (float)in.g * s; - *b = (float)in.b * s; - *a = (float)in.a * s; -} - -void -zr_color_fv(float *c, struct zr_color in) -{ - zr_color_f(&c[0], &c[1], &c[2], &c[3], in); -} - -void -zr_color_hsv_f(float *out_h, float *out_s, float *out_v, struct zr_color in) -{ - float a; - zr_color_hsva_f(out_h, out_s, out_v, &a, in); -} - -void -zr_color_hsv_fv(float *out, struct zr_color in) -{ - float a; - zr_color_hsva_f(&out[0], &out[1], &out[2], &a, in); -} - -void -zr_color_hsva_f(float *out_h, float *out_s, - float *out_v, float *out_a, struct zr_color in) -{ - float chroma; - float K = 0.0f; - float r,g,b,a; - - zr_color_f(&r,&g,&b,&a, in); - if (g < b) { - const float t = g; g = b; b = t; - K = -1.f; - } - if (r < g) { - const float t = r; r = g; g = t; - K = -2.f/6.0f - K; - } - chroma = r - ((g < b) ? g: b); - *out_h = ZR_ABS(K + (g - b)/(6.0f * chroma + 1e-20f)); - *out_s = chroma / (r + 1e-20f); - *out_v = r; - *out_a = (float)in.a / 255.0f; -} - -void -zr_color_hsva_fv(float *out, struct zr_color in) -{ - zr_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); -} - -void -zr_color_hsva_i(int *out_h, int *out_s, int *out_v, - int *out_a, struct zr_color in) -{ - float h,s,v,a; - zr_color_hsva_f(&h, &s, &v, &a, in); - *out_h = (zr_byte)(h * 255.0f); - *out_s = (zr_byte)(s * 255.0f); - *out_v = (zr_byte)(v * 255.0f); - *out_a = (zr_byte)(a * 255.0f); -} - -void -zr_color_hsva_iv(int *out, struct zr_color in) -{ - zr_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); -} - -void -zr_color_hsva_bv(zr_byte *out, struct zr_color in) -{ - int tmp[4]; - zr_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); - out[0] = (zr_byte)tmp[0]; - out[1] = (zr_byte)tmp[1]; - out[2] = (zr_byte)tmp[2]; - out[3] = (zr_byte)tmp[3]; -} - -void -zr_color_hsv_i(int *out_h, int *out_s, int *out_v, struct zr_color in) -{ - int a; - zr_color_hsva_i(out_h, out_s, out_v, &a, in); -} - -void -zr_color_hsv_iv(int *out, struct zr_color in) -{ - zr_color_hsv_i(&out[0], &out[1], &out[2], in); -} - -void -zr_color_hsv_bv(zr_byte *out, struct zr_color in) -{ - int tmp[4]; - zr_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); - out[0] = (zr_byte)tmp[0]; - out[1] = (zr_byte)tmp[1]; - out[2] = (zr_byte)tmp[2]; -} -/* - * ============================================================== - * - * IMAGE - * - * =============================================================== - */ -zr_handle -zr_handle_ptr(void *ptr) -{ - zr_handle handle = {0}; - handle.ptr = ptr; - return handle; -} - -zr_handle -zr_handle_id(int id) -{ - zr_handle handle; - handle.id = id; - return handle; -} - -struct zr_image -zr_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct zr_rect r) -{ - struct zr_image s; - zr_zero(&s, sizeof(s)); - s.handle.ptr = ptr; - s.w = w; s.h = h; - s.region[0] = (unsigned short)r.x; - s.region[1] = (unsigned short)r.y; - s.region[2] = (unsigned short)r.w; - s.region[3] = (unsigned short)r.h; - return s; -} - -struct zr_image -zr_subimage_id(int id, unsigned short w, unsigned short h, struct zr_rect r) -{ - struct zr_image s; - zr_zero(&s, sizeof(s)); - s.handle.id = id; - s.w = w; s.h = h; - s.region[0] = (unsigned short)r.x; - s.region[1] = (unsigned short)r.y; - s.region[2] = (unsigned short)r.w; - s.region[3] = (unsigned short)r.h; - return s; -} - -struct zr_image -zr_image_ptr(void *ptr) -{ - struct zr_image s; - zr_zero(&s, sizeof(s)); - ZR_ASSERT(ptr); - s.handle.ptr = ptr; - s.w = 0; s.h = 0; - s.region[0] = 0; - s.region[1] = 0; - s.region[2] = 0; - s.region[3] = 0; - return s; -} - -struct zr_image -zr_image_id(int id) -{ - struct zr_image s; - zr_zero(&s, sizeof(s)); - s.handle.id = id; - s.w = 0; s.h = 0; - s.region[0] = 0; - s.region[1] = 0; - s.region[2] = 0; - s.region[3] = 0; - return s; -} - -int -zr_image_is_subimage(const struct zr_image* img) -{ - ZR_ASSERT(img); - return !(img->w == 0 && img->h == 0); -} - -static void -zr_unify(struct zr_rect *clip, const struct zr_rect *a, float x0, float y0, - float x1, float y1) -{ - ZR_ASSERT(a); - ZR_ASSERT(clip); - clip->x = ZR_MAX(a->x, x0); - clip->y = ZR_MAX(a->y, y0); - clip->w = ZR_MIN(a->x + a->w, x1) - clip->x; - clip->h = ZR_MIN(a->y + a->h, y1) - clip->y; - clip->w = ZR_MAX(0, clip->w); - clip->h = ZR_MAX(0, clip->h); -} - -void -zr_triangle_from_direction(struct zr_vec2 *result, struct zr_rect r, - float pad_x, float pad_y, enum zr_heading direction) -{ - float w_half, h_half; - ZR_ASSERT(result); - - r.w = ZR_MAX(2 * pad_x, r.w); - r.h = ZR_MAX(2 * pad_y, r.h); - r.w = r.w - 2 * pad_x; - r.h = r.h - 2 * pad_y; - - r.x = r.x + pad_x; - r.y = r.y + pad_y; - - w_half = r.w / 2.0f; - h_half = r.h / 2.0f; - - if (direction == ZR_UP) { - result[0] = zr_vec2(r.x + w_half, r.y); - result[1] = zr_vec2(r.x + r.w, r.y + r.h); - result[2] = zr_vec2(r.x, r.y + r.h); - } else if (direction == ZR_RIGHT) { - result[0] = zr_vec2(r.x, r.y); - result[1] = zr_vec2(r.x + r.w, r.y + h_half); - result[2] = zr_vec2(r.x, r.y + r.h); - } else if (direction == ZR_DOWN) { - result[0] = zr_vec2(r.x, r.y); - result[1] = zr_vec2(r.x + r.w, r.y); - result[2] = zr_vec2(r.x + w_half, r.y + r.h); - } else { - result[0] = zr_vec2(r.x, r.y + h_half); - result[1] = zr_vec2(r.x + r.w, r.y); - result[2] = zr_vec2(r.x + r.w, r.y + r.h); - } -} - -static zr_size -zr_string_float_limit(char *string, int prec) -{ - int dot = 0; - char *c = string; - while (*c) { - if (*c == '.') { - dot = 1; - c++; - continue; - } - if (dot == (prec+1)) { - *c = 0; - break; - } - if (dot > 0) dot++; - c++; - } - return (zr_size)(c - string); -} -/* ============================================================== - * - * UTF-8 - * - * ===============================================================*/ -static const zr_byte zr_utfbyte[ZR_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; -static const zr_byte zr_utfmask[ZR_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; -static const zr_uint zr_utfmin[ZR_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x10000}; -static const zr_uint zr_utfmax[ZR_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; - -static zr_size -zr_utf_validate(zr_rune *u, zr_size i) -{ - ZR_ASSERT(u); - if (!u) return 0; - if (!ZR_BETWEEN(*u, zr_utfmin[i], zr_utfmax[i]) || - ZR_BETWEEN(*u, 0xD800, 0xDFFF)) - *u = ZR_UTF_INVALID; - for (i = 1; *u > zr_utfmax[i]; ++i); - return i; -} - -static zr_rune -zr_utf_decode_byte(char c, zr_size *i) -{ - ZR_ASSERT(i); - if (!i) return 0; - for(*i = 0; *i < ZR_LEN(zr_utfmask); ++(*i)) { - if (((zr_byte)c & zr_utfmask[*i]) == zr_utfbyte[*i]) - return (zr_byte)(c & ~zr_utfmask[*i]); - } - return 0; -} - -zr_size -zr_utf_decode(const char *c, zr_rune *u, zr_size clen) -{ - zr_size i, j, len, type=0; - zr_rune udecoded; - - ZR_ASSERT(c); - ZR_ASSERT(u); - - if (!c || !u) return 0; - if (!clen) return 0; - *u = ZR_UTF_INVALID; - - udecoded = zr_utf_decode_byte(c[0], &len); - if (!ZR_BETWEEN(len, 1, ZR_UTF_SIZE)) - return 1; - - for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { - udecoded = (udecoded << 6) | zr_utf_decode_byte(c[i], &type); - if (type != 0) - return j; - } - if (j < len) - return 0; - *u = udecoded; - zr_utf_validate(u, len); - return len; -} - -static char -zr_utf_encode_byte(zr_rune u, zr_size i) -{ - return (char)((zr_utfbyte[i]) | ((zr_byte)u & ~zr_utfmask[i])); -} - -zr_size -zr_utf_encode(zr_rune u, char *c, zr_size clen) -{ - zr_size len, i; - len = zr_utf_validate(&u, 0); - if (clen < len || !len || len > ZR_UTF_SIZE) - return 0; - - for (i = len - 1; i != 0; --i) { - c[i] = zr_utf_encode_byte(u, 0); - u >>= 6; - } - c[0] = zr_utf_encode_byte(u, len); - return len; -} - -zr_size -zr_utf_len(const char *str, zr_size len) -{ - const char *text; - zr_size glyphs = 0; - zr_size text_len; - zr_size glyph_len; - zr_size src_len = 0; - zr_rune unicode; - - ZR_ASSERT(str); - if (!str || !len) return 0; - - text = str; - text_len = len; - glyph_len = zr_utf_decode(text, &unicode, text_len); - while (glyph_len && src_len < len) { - glyphs++; - src_len = src_len + glyph_len; - glyph_len = zr_utf_decode(text + src_len, &unicode, text_len - src_len); - } - return glyphs; -} - -const char* -zr_utf_at(const char *buffer, zr_size length, int index, - zr_rune *unicode, zr_size *len) -{ - int i = 0; - zr_size src_len = 0; - zr_size glyph_len = 0; - const char *text; - zr_size text_len; - - ZR_ASSERT(buffer); - ZR_ASSERT(unicode); - ZR_ASSERT(len); - - if (!buffer || !unicode || !len) return 0; - if (index < 0) { - *unicode = ZR_UTF_INVALID; - *len = 0; - return 0; - } - - text = buffer; - text_len = length; - glyph_len = zr_utf_decode(text, unicode, text_len); - while (glyph_len) { - if (i == index) { - *len = glyph_len; - break; - } - - i++; - src_len = src_len + glyph_len; - glyph_len = zr_utf_decode(text + src_len, unicode, text_len - src_len); - } - if (i != index) return 0; - return buffer + src_len; -} - -/* - * ============================================================== - * - * USER FONT - * - * =============================================================== - */ -static zr_size -zr_user_font_glyph_index_at_pos(const struct zr_user_font *font, const char *text, - zr_size text_len, float xoff) -{ - zr_rune unicode; - zr_size glyph_offset = 0; - zr_size glyph_len = zr_utf_decode(text, &unicode, text_len); - zr_size text_width = font->width(font->userdata, font->height, text, glyph_len); - zr_size src_len = glyph_len; - - while (text_len && glyph_len) { - if (text_width >= xoff) - return glyph_offset; - - glyph_offset++; - text_len -= glyph_len; - glyph_len = zr_utf_decode(text + src_len, &unicode, text_len); - src_len += glyph_len; - text_width = font->width(font->userdata, font->height, text, src_len); - } - return glyph_offset; -} - -static zr_size -zr_use_font_glyph_clamp(const struct zr_user_font *font, const char *text, - zr_size text_len, float space, zr_size *glyphs, float *text_width) -{ - zr_size glyph_len = 0; - float last_width = 0; - zr_rune unicode = 0; - float width = 0; - zr_size len = 0; - zr_size g = 0; - - glyph_len = zr_utf_decode(text, &unicode, text_len); - while (glyph_len && (width < space) && (len < text_len)) { - zr_size s; - len += glyph_len; - s = font->width(font->userdata, font->height, text, len); - - last_width = width; - width = (float)s; - glyph_len = zr_utf_decode(&text[len], &unicode, text_len - len); - g++; - } - - *glyphs = g; - *text_width = last_width; - return len; -} - -static zr_size -zr_user_font_glyphs_fitting_in_space(const struct zr_user_font *font, - const char *text, zr_size len, float space, zr_size *row_len, - zr_size *glyphs, float *text_width, int has_newline) -{ - zr_size glyph_len; - zr_size width = 0; - zr_rune unicode = 0; - zr_size text_len = 0; - zr_size row_advance = 0; - - ZR_ASSERT(glyphs); - ZR_ASSERT(text_width); - ZR_ASSERT(row_len); - - *glyphs = 0; - *row_len = 0; - *text_width = 0; - - glyph_len = text_len = zr_utf_decode(text, &unicode, len); - if (!glyph_len) return 0; - width = font->width(font->userdata, font->height, text, text_len); - while ((width <= space) && (text_len <= len) && glyph_len) { - *text_width = (float)width; - *glyphs+=1; - *row_len = text_len; - row_advance += glyph_len; - - if (has_newline && (unicode == '\n' || unicode == '\r')) { - zr_rune next = 0; - zr_utf_decode(text+text_len, &next, len - text_len); - if (unicode == '\r') { - *row_len-=1;; - } else if ((unicode == '\n') && (next == '\r')) { - *row_len-= 2; - } else { - *row_len-=1; - } - width = font->width(font->userdata, font->height, text, *row_len); - *text_width = (float)width; - break; - } - glyph_len = zr_utf_decode(text + text_len, &unicode, len - text_len); - text_len += glyph_len; - width = font->width(font->userdata, font->height, text, text_len); - } - return row_advance; -} - -/* ============================================================== - * - * BUFFER - * - * ===============================================================*/ -#if ZR_COMPILE_WITH_DEFAULT_ALLOCATOR - -static void* zr_malloc(zr_handle unused, zr_size size) -{ZR_UNUSED(unused); return malloc(size);} -static void zr_mfree(zr_handle unused, void *ptr) -{ZR_UNUSED(unused); free(ptr);} - -void -zr_buffer_init_default(struct zr_buffer *buffer) -{ - struct zr_allocator alloc; - alloc.userdata.ptr = 0; - alloc.alloc = zr_malloc; - alloc.free = zr_mfree; - zr_buffer_init(buffer, &alloc, ZR_BUFFER_DEFAULT_INITIAL_SIZE); -} -#endif - -void -zr_buffer_init(struct zr_buffer *b, const struct zr_allocator *a, - zr_size initial_size) -{ - ZR_ASSERT(b); - ZR_ASSERT(a); - ZR_ASSERT(initial_size); - if (!b || !a || !initial_size) return; - - zr_zero(b, sizeof(*b)); - b->type = ZR_BUFFER_DYNAMIC; - b->memory.ptr = a->alloc(a->userdata, initial_size); - b->memory.size = initial_size; - b->size = initial_size; - b->grow_factor = 2.0f; - b->pool = *a; -} - -void -zr_buffer_init_fixed(struct zr_buffer *b, void *m, zr_size size) -{ - ZR_ASSERT(b); - ZR_ASSERT(m); - ZR_ASSERT(size); - if (!b || !m || !size) return; - - zr_zero(b, sizeof(*b)); - b->type = ZR_BUFFER_FIXED; - b->memory.ptr = m; - b->memory.size = size; - b->size = size; -} - -static void* -zr_buffer_align(void *unaligned, zr_size align, zr_size *alignment, - enum zr_buffer_allocation_type type) -{ - void *memory = 0; - switch (type) { - default: - case ZR_BUFFER_MAX: - case ZR_BUFFER_FRONT: - if (align) { - memory = ZR_ALIGN_PTR(unaligned, align); - *alignment = (zr_size)((zr_byte*)memory - (zr_byte*)unaligned); - } else { - memory = unaligned; - *alignment = 0; - } - break; - case ZR_BUFFER_BACK: - if (align) { - memory = ZR_ALIGN_PTR_BACK(unaligned, align); - *alignment = (zr_size)((zr_byte*)unaligned - (zr_byte*)memory); - } else { - memory = unaligned; - *alignment = 0; - } - break; - } - return memory; -} - -static void* -zr_buffer_realloc(struct zr_buffer *b, zr_size capacity, zr_size *size) -{ - void *temp; - zr_size buffer_size; - - ZR_ASSERT(b); - ZR_ASSERT(size); - if (!b || !size || !b->pool.alloc || !b->pool.free) - return 0; - - buffer_size = b->memory.size; - temp = b->pool.alloc(b->pool.userdata, capacity); - ZR_ASSERT(temp); - if (!temp) return 0; - zr_memcopy(temp, b->memory.ptr, buffer_size); - b->pool.free(b->pool.userdata, b->memory.ptr); - *size = capacity; - - if (b->size == buffer_size) { - /* no back buffer so just set correct size */ - b->size = capacity; - return temp; - } else { - /* copy back buffer to the end of the new buffer */ - void *dst, *src; - zr_size back_size; - back_size = buffer_size - b->size; - dst = zr_ptr_add(void, temp, capacity - back_size); - src = zr_ptr_add(void, temp, b->size); - zr_memcopy(dst, src, back_size); - b->size = capacity - back_size; - } - return temp; -} - -static void* -zr_buffer_alloc(struct zr_buffer *b, enum zr_buffer_allocation_type type, - zr_size size, zr_size align) -{ - int full; - zr_size alignment; - void *unaligned; - void *memory; - - ZR_ASSERT(b); - ZR_ASSERT(size); - if (!b || !size) return 0; - b->needed += size; - - /* calculate total size with needed alignment + size */ - if (type == ZR_BUFFER_FRONT) - unaligned = zr_ptr_add(void, b->memory.ptr, b->allocated); - else unaligned = zr_ptr_add(void, b->memory.ptr, b->size - size); - memory = zr_buffer_align(unaligned, align, &alignment, type); - - /* check if buffer has enough memory*/ - if (type == ZR_BUFFER_FRONT) - full = ((b->allocated + size + alignment) > b->size); - else full = ((b->size - (size + alignment)) <= b->allocated); - - if (full) { - zr_size capacity; - ZR_ASSERT(b->type == ZR_BUFFER_DYNAMIC); - ZR_ASSERT(b->pool.alloc && b->pool.free); - if (b->type != ZR_BUFFER_DYNAMIC || !b->pool.alloc || !b->pool.free) - return 0; - - /* buffer is full so allocate bigger buffer if dynamic */ - capacity = (zr_size)((float)b->memory.size * b->grow_factor); - capacity = ZR_MAX(capacity, zr_round_up_pow2((zr_uint)(b->allocated + size))); - b->memory.ptr = zr_buffer_realloc(b, capacity, &b->memory.size); - if (!b->memory.ptr) return 0; - - /* align newly allocated pointer */ - if (type == ZR_BUFFER_FRONT) - unaligned = zr_ptr_add(void, b->memory.ptr, b->allocated); - else unaligned = zr_ptr_add(void, b->memory.ptr, b->size); - memory = zr_buffer_align(unaligned, align, &alignment, type); - } - - if (type == ZR_BUFFER_FRONT) - b->allocated += size + alignment; - else b->size -= (size + alignment); - b->needed += alignment; - b->calls++; - return memory; -} - -void -zr_buffer_push(struct zr_buffer *b, enum zr_buffer_allocation_type type, - void *memory, zr_size size, zr_size align) -{ - void *mem = zr_buffer_alloc(b, type, size, align); - if (!mem) return; - zr_memcopy(mem, memory, size); -} - -void -zr_buffer_mark(struct zr_buffer *buffer, enum zr_buffer_allocation_type type) -{ - ZR_ASSERT(buffer); - if (!buffer) return; - buffer->marker[type].active = zr_true; - if (type == ZR_BUFFER_BACK) - buffer->marker[type].offset = buffer->size; - else buffer->marker[type].offset = buffer->allocated; -} - -void -zr_buffer_reset(struct zr_buffer *buffer, enum zr_buffer_allocation_type type) -{ - ZR_ASSERT(buffer); - if (!buffer) return; - if (type == ZR_BUFFER_BACK) { - /* reset back buffer either back to marker or empty */ - buffer->needed -= (buffer->memory.size - buffer->marker[type].offset); - if (buffer->marker[type].active) - buffer->size = buffer->marker[type].offset; - else buffer->size = buffer->memory.size; - buffer->marker[type].active = zr_false; - } else { - /* reset front buffer either back to back marker or empty */ - buffer->needed -= (buffer->allocated - buffer->marker[type].offset); - if (buffer->marker[type].active) - buffer->allocated = buffer->marker[type].offset; - else buffer->allocated = 0; - buffer->marker[type].active = zr_false; - } -} - -void -zr_buffer_clear(struct zr_buffer *b) -{ - ZR_ASSERT(b); - if (!b) return; - b->allocated = 0; - b->size = b->memory.size; - b->calls = 0; - b->needed = 0; -} - -void -zr_buffer_free(struct zr_buffer *b) -{ - ZR_ASSERT(b); - if (!b || !b->memory.ptr) return; - if (b->type == ZR_BUFFER_FIXED) return; - if (!b->pool.free) return; - ZR_ASSERT(b->pool.free); - b->pool.free(b->pool.userdata, b->memory.ptr); -} - -void -zr_buffer_info(struct zr_memory_status *s, struct zr_buffer *b) -{ - ZR_ASSERT(b); - ZR_ASSERT(s); - if (!s || !b) return; - s->allocated = b->allocated; - s->size = b->memory.size; - s->needed = b->needed; - s->memory = b->memory.ptr; - s->calls = b->calls; -} - -void* -zr_buffer_memory(struct zr_buffer *buffer) -{ - ZR_ASSERT(buffer); - if (!buffer) return 0; - return buffer->memory.ptr; -} - -const void* -zr_buffer_memory_const(const struct zr_buffer *buffer) -{ - ZR_ASSERT(buffer); - if (!buffer) return 0; - return buffer->memory.ptr; -} - -zr_size -zr_buffer_total(struct zr_buffer *buffer) -{ - ZR_ASSERT(buffer); - if (!buffer) return 0; - return buffer->memory.size; -} - -/* - * ============================================================== - * - * Command buffer - * - * =============================================================== -*/ -static void -zr_command_buffer_init(struct zr_command_buffer *cmdbuf, - struct zr_buffer *buffer, enum zr_command_clipping clip) -{ - ZR_ASSERT(cmdbuf); - ZR_ASSERT(buffer); - if (!cmdbuf || !buffer) return; - cmdbuf->base = buffer; - cmdbuf->use_clipping = clip; - cmdbuf->begin = buffer->allocated; - cmdbuf->end = buffer->allocated; - cmdbuf->last = buffer->allocated; -} - -static void -zr_command_buffer_reset(struct zr_command_buffer *buffer) -{ - ZR_ASSERT(buffer); - if (!buffer) return; - buffer->begin = 0; - buffer->end = 0; - buffer->last = 0; - buffer->clip = zr_null_rect; -#if ZR_COMPILE_WITH_COMMAND_USERDATA - buffer->userdata.ptr = 0; -#endif -} - -static void* -zr_command_buffer_push(struct zr_command_buffer* b, - enum zr_command_type t, zr_size size) -{ - static const zr_size align = ZR_ALIGNOF(struct zr_command); - struct zr_command *cmd; - zr_size alignment; - void *unaligned; - void *memory; - - ZR_ASSERT(b); - ZR_ASSERT(b->base); - if (!b) return 0; - - cmd = (struct zr_command*)zr_buffer_alloc(b->base,ZR_BUFFER_FRONT,size,align); - if (!cmd) return 0; - - /* make sure the offset to the next command is aligned */ - b->last = (zr_size)((zr_byte*)cmd - (zr_byte*)b->base->memory.ptr); - unaligned = (zr_byte*)cmd + size; - memory = ZR_ALIGN_PTR(unaligned, align); - alignment = (zr_size)((zr_byte*)memory - (zr_byte*)unaligned); - - cmd->type = t; - cmd->next = b->base->allocated + alignment; -#if ZR_COMPILE_WITH_COMMAND_USERDATA - cmd->userdata = b->userdata; -#endif - b->end = cmd->next; - return cmd; -} - -void -zr_push_scissor(struct zr_command_buffer *b, struct zr_rect r) -{ - struct zr_command_scissor *cmd; - ZR_ASSERT(b); - if (!b) return; - - b->clip.x = r.x; - b->clip.y = r.y; - b->clip.w = r.w; - b->clip.h = r.h; - cmd = (struct zr_command_scissor*) - zr_command_buffer_push(b, ZR_COMMAND_SCISSOR, sizeof(*cmd)); - - if (!cmd) return; - cmd->x = (short)r.x; - cmd->y = (short)r.y; - cmd->w = (unsigned short)ZR_MAX(0, r.w); - cmd->h = (unsigned short)ZR_MAX(0, r.h); -} - -void -zr_stroke_line(struct zr_command_buffer *b, float x0, float y0, - float x1, float y1, float line_thickness, struct zr_color c) -{ - struct zr_command_line *cmd; - ZR_ASSERT(b); - if (!b) return; - cmd = (struct zr_command_line*) - zr_command_buffer_push(b, ZR_COMMAND_LINE, sizeof(*cmd)); - if (!cmd) return; - cmd->line_thickness = (unsigned short)line_thickness; - cmd->begin.x = (short)x0; - cmd->begin.y = (short)y0; - cmd->end.x = (short)x1; - cmd->end.y = (short)y1; - cmd->color = c; -} - -void -zr_stroke_curve(struct zr_command_buffer *b, float ax, float ay, - float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y, - float bx, float by, float line_thickness, struct zr_color col) -{ - struct zr_command_curve *cmd; - ZR_ASSERT(b); - if (!b || col.a == 0) return; - - cmd = (struct zr_command_curve*) - zr_command_buffer_push(b, ZR_COMMAND_CURVE, sizeof(*cmd)); - if (!cmd) return; - cmd->line_thickness = (unsigned short)line_thickness; - cmd->begin.x = (short)ax; - cmd->begin.y = (short)ay; - cmd->ctrl[0].x = (short)ctrl0x; - cmd->ctrl[0].y = (short)ctrl0y; - cmd->ctrl[1].x = (short)ctrl1x; - cmd->ctrl[1].y = (short)ctrl1y; - cmd->end.x = (short)bx; - cmd->end.y = (short)by; - cmd->color = col; -} - -void -zr_stroke_rect(struct zr_command_buffer *b, struct zr_rect rect, - float rounding, float line_thickness, struct zr_color c) -{ - struct zr_command_rect *cmd; - ZR_ASSERT(b); - if (!b || c.a == 0) return; - if (b->use_clipping) { - const struct zr_rect *clip = &b->clip; - if (!ZR_INTERSECT(rect.x, rect.y, rect.w, rect.h, - clip->x, clip->y, clip->w, clip->h)) return; - } - - cmd = (struct zr_command_rect*) - zr_command_buffer_push(b, ZR_COMMAND_RECT, sizeof(*cmd)); - if (!cmd) return; - cmd->rounding = (unsigned short)rounding; - cmd->line_thickness = (unsigned short)line_thickness; - cmd->x = (short)rect.x; - cmd->y = (short)rect.y; - cmd->w = (unsigned short)ZR_MAX(0, rect.w); - cmd->h = (unsigned short)ZR_MAX(0, rect.h); - cmd->color = c; -} - -void -zr_fill_rect(struct zr_command_buffer *b, struct zr_rect rect, - float rounding, struct zr_color c) -{ - struct zr_command_rect_filled *cmd; - ZR_ASSERT(b); - if (!b || c.a == 0) return; - if (b->use_clipping) { - const struct zr_rect *clip = &b->clip; - if (!ZR_INTERSECT(rect.x, rect.y, rect.w, rect.h, - clip->x, clip->y, clip->w, clip->h)) return; - } - - cmd = (struct zr_command_rect_filled*) - zr_command_buffer_push(b, ZR_COMMAND_RECT_FILLED, sizeof(*cmd)); - if (!cmd) return; - cmd->rounding = (unsigned short)rounding; - cmd->x = (short)rect.x; - cmd->y = (short)rect.y; - cmd->w = (unsigned short)ZR_MAX(0, rect.w); - cmd->h = (unsigned short)ZR_MAX(0, rect.h); - cmd->color = c; -} - -void -zr_fill_rect_multi_color(struct zr_command_buffer *b, struct zr_rect rect, - struct zr_color left, struct zr_color top, struct zr_color right, - struct zr_color bottom) -{ - struct zr_command_rect_multi_color *cmd; - ZR_ASSERT(b); - if (!b) return; - if (b->use_clipping) { - const struct zr_rect *clip = &b->clip; - if (!ZR_INTERSECT(rect.x, rect.y, rect.w, rect.h, - clip->x, clip->y, clip->w, clip->h)) return; - } - - cmd = (struct zr_command_rect_multi_color*) - zr_command_buffer_push(b, ZR_COMMAND_RECT_MULTI_COLOR, sizeof(*cmd)); - cmd->x = (short)rect.x; - cmd->y = (short)rect.y; - cmd->w = (unsigned short)ZR_MAX(0, rect.w); - cmd->h = (unsigned short)ZR_MAX(0, rect.h); - cmd->left = left; - cmd->top = top; - cmd->right = right; - cmd->bottom = bottom; -} - -void -zr_stroke_circle(struct zr_command_buffer *b, struct zr_rect r, - float line_thickness, struct zr_color c) -{ - struct zr_command_circle *cmd; - if (!b || c.a == 0) return; - if (b->use_clipping) { - const struct zr_rect *clip = &b->clip; - if (!ZR_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) - return; - } - - cmd = (struct zr_command_circle*) - zr_command_buffer_push(b, ZR_COMMAND_CIRCLE, sizeof(*cmd)); - if (!cmd) return; - cmd->line_thickness = (unsigned short)line_thickness; - cmd->x = (short)r.x; - cmd->y = (short)r.y; - cmd->w = (unsigned short)ZR_MAX(r.w, 0); - cmd->h = (unsigned short)ZR_MAX(r.h, 0); - cmd->color = c; -} - -void -zr_fill_circle(struct zr_command_buffer *b, struct zr_rect r, struct zr_color c) -{ - struct zr_command_circle_filled *cmd; - ZR_ASSERT(b); - if (!b || c.a == 0) return; - if (b->use_clipping) { - const struct zr_rect *clip = &b->clip; - if (!ZR_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) - return; - } - - cmd = (struct zr_command_circle_filled*) - zr_command_buffer_push(b, ZR_COMMAND_CIRCLE_FILLED, sizeof(*cmd)); - if (!cmd) return; - cmd->x = (short)r.x; - cmd->y = (short)r.y; - cmd->w = (unsigned short)ZR_MAX(r.w, 0); - cmd->h = (unsigned short)ZR_MAX(r.h, 0); - cmd->color = c; -} - -void -zr_stroke_arc(struct zr_command_buffer *b, float cx, float cy, float radius, - float a_min, float a_max, float line_thickness, struct zr_color c) -{ - struct zr_command_arc *cmd; - if (!b || c.a == 0) return; - cmd = (struct zr_command_arc*) - zr_command_buffer_push(b, ZR_COMMAND_ARC, sizeof(*cmd)); - if (!cmd) return; - cmd->line_thickness = (unsigned short)line_thickness; - cmd->cx = (short)cx; - cmd->cy = (short)cy; - cmd->r = (unsigned short)radius; - cmd->a[0] = a_min; - cmd->a[1] = a_max; - cmd->color = c; -} - -void -zr_fill_arc(struct zr_command_buffer *b, float cx, float cy, float radius, - float a_min, float a_max, struct zr_color c) -{ - struct zr_command_arc_filled *cmd; - ZR_ASSERT(b); - if (!b || c.a == 0) return; - cmd = (struct zr_command_arc_filled*) - zr_command_buffer_push(b, ZR_COMMAND_ARC_FILLED, sizeof(*cmd)); - if (!cmd) return; - cmd->cx = (short)cx; - cmd->cy = (short)cy; - cmd->r = (unsigned short)radius; - cmd->a[0] = a_min; - cmd->a[1] = a_max; - cmd->color = c; -} - -void -zr_stroke_triangle(struct zr_command_buffer *b, float x0, float y0, float x1, - float y1, float x2, float y2, float line_thickness, struct zr_color c) -{ - struct zr_command_triangle *cmd; - ZR_ASSERT(b); - if (!b || c.a == 0) return; - if (b->use_clipping) { - const struct zr_rect *clip = &b->clip; - if (!ZR_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) || - !ZR_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) || - !ZR_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) - return; - } - - cmd = (struct zr_command_triangle*) - zr_command_buffer_push(b, ZR_COMMAND_TRIANGLE, sizeof(*cmd)); - if (!cmd) return; - cmd->line_thickness = (unsigned short)line_thickness; - cmd->a.x = (short)x0; - cmd->a.y = (short)y0; - cmd->b.x = (short)x1; - cmd->b.y = (short)y1; - cmd->c.x = (short)x2; - cmd->c.y = (short)y2; - cmd->color = c; -} - -void -zr_fill_triangle(struct zr_command_buffer *b, float x0, float y0, float x1, - float y1, float x2, float y2, struct zr_color c) -{ - struct zr_command_triangle_filled *cmd; - ZR_ASSERT(b); - if (!b || c.a == 0) return; - if (!b) return; - if (b->use_clipping) { - const struct zr_rect *clip = &b->clip; - if (!ZR_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) || - !ZR_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) || - !ZR_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) - return; - } - - cmd = (struct zr_command_triangle_filled*) - zr_command_buffer_push(b, ZR_COMMAND_TRIANGLE_FILLED, sizeof(*cmd)); - if (!cmd) return; - cmd->a.x = (short)x0; - cmd->a.y = (short)y0; - cmd->b.x = (short)x1; - cmd->b.y = (short)y1; - cmd->c.x = (short)x2; - cmd->c.y = (short)y2; - cmd->color = c; -} - -void -zr_stroke_polygon(struct zr_command_buffer *b, float *points, int point_count, - float line_thickness, struct zr_color col) -{ - int i; - zr_size size = 0; - struct zr_command_polygon *cmd; - - ZR_ASSERT(b); - if (!b || col.a == 0) return; - size = sizeof(*cmd) + sizeof(short) * 2 * (zr_size)point_count; - cmd = (struct zr_command_polygon*) zr_command_buffer_push(b, ZR_COMMAND_POLYGON, size); - if (!cmd) return; - cmd->line_thickness = (unsigned short)line_thickness; - cmd->color = col; - cmd->point_count = (unsigned short)point_count; - for (i = 0; i < point_count; ++i) { - cmd->points[i].x = (short)points[i*2]; - cmd->points[i].y = (short)points[i*2+1]; - } -} - -void -zr_fill_polygon(struct zr_command_buffer *b, float *points, int point_count, - struct zr_color col) -{ - int i; - zr_size size = 0; - struct zr_command_polygon_filled *cmd; - - ZR_ASSERT(b); - if (!b || col.a == 0) return; - size = sizeof(*cmd) + sizeof(short) * 2 * (zr_size)point_count; - cmd = (struct zr_command_polygon_filled*) - zr_command_buffer_push(b, ZR_COMMAND_POLYGON_FILLED, size); - if (!cmd) return; - cmd->color = col; - cmd->point_count = (unsigned short)point_count; - for (i = 0; i < point_count; ++i) { - cmd->points[i].x = (short)points[i*2]; - cmd->points[i].y = (short)points[i*2+1]; - } -} - -void -zr_stroke_polyline(struct zr_command_buffer *b, float *points, int point_count, - float line_thickness, struct zr_color col) -{ - int i; - zr_size size = 0; - struct zr_command_polyline *cmd; - - ZR_ASSERT(b); - if (!b || col.a == 0) return; - size = sizeof(*cmd) + sizeof(short) * 2 * (zr_size)point_count; - cmd = (struct zr_command_polyline*) zr_command_buffer_push(b, ZR_COMMAND_POLYLINE, size); - if (!cmd) return; - cmd->color = col; - cmd->point_count = (unsigned short)point_count; - cmd->line_thickness = (unsigned short)line_thickness; - for (i = 0; i < point_count; ++i) { - cmd->points[i].x = (short)points[i*2]; - cmd->points[i].y = (short)points[i*2+1]; - } -} - -void -zr_draw_image(struct zr_command_buffer *b, struct zr_rect r, - const struct zr_image *img) -{ - struct zr_command_image *cmd; - ZR_ASSERT(b); - if (!b) return; - if (b->use_clipping) { - const struct zr_rect *c = &b->clip; - if (!ZR_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) - return; - } - - cmd = (struct zr_command_image*) - zr_command_buffer_push(b, ZR_COMMAND_IMAGE, sizeof(*cmd)); - if (!cmd) return; - cmd->x = (short)r.x; - cmd->y = (short)r.y; - cmd->w = (unsigned short)ZR_MAX(0, r.w); - cmd->h = (unsigned short)ZR_MAX(0, r.h); - cmd->img = *img; -} - -void -zr_draw_text(struct zr_command_buffer *b, struct zr_rect r, - const char *string, zr_size length, const struct zr_user_font *font, - struct zr_color bg, struct zr_color fg) -{ - zr_size text_width = 0; - struct zr_command_text *cmd; - - ZR_ASSERT(b); - ZR_ASSERT(font); - if (!b || !string || !length || (bg.a == 0 && fg.a == 0)) return; - if (b->use_clipping) { - const struct zr_rect *c = &b->clip; - if (!ZR_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) - return; - } - - /* make sure text fits inside bounds */ - text_width = font->width(font->userdata, font->height, string, length); - if (text_width > r.w){ - float txt_width = (float)text_width; - zr_size glyphs = 0; - length = zr_use_font_glyph_clamp(font, string, length, - r.w, &glyphs, &txt_width); - } - - if (!length) return; - cmd = (struct zr_command_text*) - zr_command_buffer_push(b, ZR_COMMAND_TEXT, sizeof(*cmd) + length + 1); - if (!cmd) return; - cmd->x = (short)r.x; - cmd->y = (short)r.y; - cmd->w = (unsigned short)r.w; - cmd->h = (unsigned short)r.h; - cmd->background = bg; - cmd->foreground = fg; - cmd->font = font; - cmd->length = length; - cmd->height = font->height; - zr_memcopy(cmd->string, string, length); - cmd->string[length] = '\0'; -} - -/* ============================================================== - * - * CANVAS - * - * ===============================================================*/ -#if ZR_COMPILE_WITH_VERTEX_BUFFER -void -zr_draw_list_init(struct zr_draw_list *list) -{ - zr_size i = 0; - zr_zero(list, sizeof(*list)); - for (i = 0; i < ZR_LEN(list->circle_vtx); ++i) { - const float a = ((float)i / (float)ZR_LEN(list->circle_vtx)) * 2 * ZR_PI; - list->circle_vtx[i].x = (float)zr_cos(a); - list->circle_vtx[i].y = (float)zr_sin(a); - } -} - -void -zr_draw_list_setup(struct zr_draw_list *canvas, float global_alpha, - enum zr_anti_aliasing line_AA, enum zr_anti_aliasing shape_AA, - struct zr_draw_null_texture null, struct zr_buffer *cmds, - struct zr_buffer *vertices, struct zr_buffer *elements) -{ - canvas->null = null; - canvas->clip_rect = zr_null_rect; - canvas->vertices = vertices; - canvas->elements = elements; - canvas->buffer = cmds; - canvas->line_AA = line_AA; - canvas->shape_AA = shape_AA; - canvas->global_alpha = global_alpha; -} - -const struct zr_draw_command* -zr__draw_list_begin(const struct zr_draw_list *canvas, const struct zr_buffer *buffer) -{ - zr_byte *memory; - zr_size offset; - const struct zr_draw_command *cmd; - - ZR_ASSERT(buffer); - if (!buffer || !buffer->size || !canvas->cmd_count) - return 0; - - memory = (zr_byte*)buffer->memory.ptr; - offset = buffer->memory.size - canvas->cmd_offset; - cmd = zr_ptr_add(const struct zr_draw_command, memory, offset); - return cmd; -} - -const struct zr_draw_command* -zr__draw_list_next(const struct zr_draw_command *cmd, - const struct zr_buffer *buffer, const struct zr_draw_list *canvas) -{ - zr_byte *memory; - zr_size size; - zr_size offset; - const struct zr_draw_command *end; - - ZR_ASSERT(buffer); - ZR_ASSERT(canvas); - if (!cmd || !buffer || !canvas) - return 0; - - memory = (zr_byte*)buffer->memory.ptr; - size = buffer->memory.size; - offset = size - canvas->cmd_offset; - end = zr_ptr_add(const struct zr_draw_command, memory, offset); - end -= (canvas->cmd_count-1); - - if (cmd <= end) return 0; - return (cmd-1); -} - -void -zr_draw_list_clear(struct zr_draw_list *list) -{ - ZR_ASSERT(list); - if (!list) return; - if (list->buffer) - zr_buffer_clear(list->buffer); - if (list->elements) - zr_buffer_clear(list->vertices); - if (list->vertices) - zr_buffer_clear(list->elements); - - list->element_count = 0; - list->vertex_count = 0; - list->cmd_offset = 0; - list->cmd_count = 0; - list->path_count = 0; - list->vertices = 0; - list->elements = 0; - list->clip_rect = zr_null_rect; -} - -static struct zr_vec2* -zr_draw_list_alloc_path(struct zr_draw_list *list, zr_size count) -{ - struct zr_vec2 *points; - static const zr_size point_align = ZR_ALIGNOF(struct zr_vec2); - static const zr_size point_size = sizeof(struct zr_vec2); - points = (struct zr_vec2*) - zr_buffer_alloc(list->buffer, ZR_BUFFER_FRONT, - point_size * count, point_align); - - if (!points) return 0; - if (!list->path_offset) { - void *memory = zr_buffer_memory(list->buffer); - list->path_offset = (unsigned int)((zr_byte*)points - (zr_byte*)memory); - } - list->path_count += (unsigned int)count; - return points; -} - -static struct zr_vec2 -zr_draw_list_path_last(struct zr_draw_list *list) -{ - void *memory; - struct zr_vec2 *point; - ZR_ASSERT(list->path_count); - memory = zr_buffer_memory(list->buffer); - point = zr_ptr_add(struct zr_vec2, memory, list->path_offset); - point += (list->path_count-1); - return *point; -} - -static struct zr_draw_command* -zr_draw_list_push_command(struct zr_draw_list *list, struct zr_rect clip, - zr_handle texture) -{ - static const zr_size cmd_align = ZR_ALIGNOF(struct zr_draw_command); - static const zr_size cmd_size = sizeof(struct zr_draw_command); - struct zr_draw_command *cmd; - - ZR_ASSERT(list); - cmd = (struct zr_draw_command*) - zr_buffer_alloc(list->buffer, ZR_BUFFER_BACK, cmd_size, cmd_align); - - if (!cmd) return 0; - if (!list->cmd_count) { - zr_byte *memory = (zr_byte*)zr_buffer_memory(list->buffer); - zr_size total = zr_buffer_total(list->buffer); - memory = zr_ptr_add(zr_byte, memory, total); - list->cmd_offset = (zr_size)(memory - (zr_byte*)cmd); - } - - cmd->elem_count = 0; - cmd->clip_rect = clip; - cmd->texture = texture; - - list->cmd_count++; - list->clip_rect = clip; - return cmd; -} - -static struct zr_draw_command* -zr_draw_list_command_last(struct zr_draw_list *list) -{ - void *memory; - zr_size size; - struct zr_draw_command *cmd; - ZR_ASSERT(list->cmd_count); - - memory = zr_buffer_memory(list->buffer); - size = zr_buffer_total(list->buffer); - cmd = zr_ptr_add(struct zr_draw_command, memory, size - list->cmd_offset); - return (cmd - (list->cmd_count-1)); -} - -static void -zr_draw_list_add_clip(struct zr_draw_list *list, struct zr_rect rect) -{ - ZR_ASSERT(list); - if (!list) return; - if (!list->cmd_count) { - zr_draw_list_push_command(list, rect, list->null.texture); - } else { - struct zr_draw_command *prev = zr_draw_list_command_last(list); - if (prev->elem_count == 0) - prev->clip_rect = rect; - zr_draw_list_push_command(list, rect, prev->texture); - } -} - -static void -zr_draw_list_push_image(struct zr_draw_list *list, zr_handle texture) -{ - ZR_ASSERT(list); - if (!list) return; - if (!list->cmd_count) { - zr_draw_list_push_command(list, zr_null_rect, list->null.texture); - } else { - struct zr_draw_command *prev = zr_draw_list_command_last(list); - if (prev->elem_count == 0) - prev->texture = texture; - else if (prev->texture.id != texture.id) - zr_draw_list_push_command(list, prev->clip_rect, texture); - } -} - -#if ZR_COMPILE_WITH_COMMAND_USERDATA -void -zr_draw_list_push_userdata(struct zr_draw_list *list, zr_handle userdata) -{ - ZR_ASSERT(list); - if (!list) return; - if (!list->cmd_count) { - struct zr_draw_command *prev; - zr_draw_list_push_command(list, zr_null_rect, list->null.texture); - prev = zr_draw_list_command_last(list); - prev->userdata = userdata; - } else { - struct zr_draw_command *prev = zr_draw_list_command_last(list); - if (prev->elem_count == 0) { - prev->userdata = userdata; - } else if (prev->userdata.ptr != userdata.ptr) { - zr_draw_list_push_command(list, prev->clip_rect, prev->texture); - prev = zr_draw_list_command_last(list); - prev->userdata = userdata; - } - } -} -#endif - -static struct zr_draw_vertex* -zr_draw_list_alloc_vertices(struct zr_draw_list *list, zr_size count) -{ - struct zr_draw_vertex *vtx; - static const zr_size vtx_align = ZR_ALIGNOF(struct zr_draw_vertex); - static const zr_size vtx_size = sizeof(struct zr_draw_vertex); - ZR_ASSERT(list); - if (!list) return 0; - - vtx = (struct zr_draw_vertex*) - zr_buffer_alloc(list->vertices, ZR_BUFFER_FRONT, vtx_size*count, vtx_align); - if (!vtx) return 0; - list->vertex_count += (unsigned int)count; - return vtx; -} - -static zr_draw_index* -zr_draw_list_alloc_elements(struct zr_draw_list *list, zr_size count) -{ - zr_draw_index *ids; - struct zr_draw_command *cmd; - static const zr_size elem_align = ZR_ALIGNOF(zr_draw_index); - static const zr_size elem_size = sizeof(zr_draw_index); - ZR_ASSERT(list); - if (!list) return 0; - - ids = (zr_draw_index*) - zr_buffer_alloc(list->elements, ZR_BUFFER_FRONT, elem_size*count, elem_align); - if (!ids) return 0; - cmd = zr_draw_list_command_last(list); - list->element_count += (unsigned int)count; - cmd->elem_count += (unsigned int)count; - return ids; -} - -static struct zr_draw_vertex -zr_draw_vertex(struct zr_vec2 pos, struct zr_vec2 uv, zr_draw_vertex_color col) -{ - struct zr_draw_vertex out; - out.position = pos; - out.uv = uv; - out.col = col; - return out; -} - -void -zr_draw_list_stroke_poly_line(struct zr_draw_list *list, const struct zr_vec2 *points, - const unsigned int points_count, struct zr_color color, enum zr_draw_list_stroke closed, - float thickness, enum zr_anti_aliasing aliasing) -{ - zr_size count; - int thick_line; - zr_draw_vertex_color col; - ZR_ASSERT(list); - if (!list || points_count < 2) return; - - color.a = (zr_byte)((float)color.a * list->global_alpha); - col = zr_color_u32(color); - count = points_count; - if (!closed) count = points_count-1; - thick_line = thickness > 1.0f; - -#if ZR_COMPILE_WITH_COMMAND_USERDATA - zr_draw_list_push_userdata(list, list->userdata); -#endif - - if (aliasing == ZR_ANTI_ALIASING_ON) { - /* ANTI-ALIASED STROKE */ - const float AA_SIZE = 1.0f; - static const zr_size pnt_align = ZR_ALIGNOF(struct zr_vec2); - static const zr_size pnt_size = sizeof(struct zr_vec2); - const zr_draw_vertex_color col_trans = col & 0x00ffffff; - - /* allocate vertices and elements */ - zr_size i1 = 0; - zr_size index = list->vertex_count; - const zr_size idx_count = (thick_line) ? (count * 18) : (count * 12); - const zr_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3); - struct zr_draw_vertex *vtx = zr_draw_list_alloc_vertices(list, vtx_count); - zr_draw_index *ids = zr_draw_list_alloc_elements(list, idx_count); - - zr_size size; - struct zr_vec2 *normals, *temp; - if (!vtx || !ids) return; - - /* temporary allocate normals + points */ - zr_buffer_mark(list->vertices, ZR_BUFFER_FRONT); - size = pnt_size * ((thick_line) ? 5 : 3) * points_count; - normals = (struct zr_vec2*) - zr_buffer_alloc(list->vertices, ZR_BUFFER_FRONT, size, pnt_align); - if (!normals) return; - temp = normals + points_count; - - /* calculate normals */ - for (i1 = 0; i1 < count; ++i1) { - const zr_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); - struct zr_vec2 diff = zr_vec2_sub(points[i2], points[i1]); - float len; - - /* vec2 inverted lenth */ - len = zr_vec2_len_sqr(diff); - if (len != 0.0f) - len = zr_inv_sqrt(len); - else len = 1.0f; - - diff = zr_vec2_muls(diff, len); - normals[i1].x = diff.y; - normals[i1].y = -diff.x; - } - - if (!closed) - normals[points_count-1] = normals[points_count-2]; - - if (!thick_line) { - zr_size idx1, i; - if (!closed) { - struct zr_vec2 d; - temp[0] = zr_vec2_add(points[0], zr_vec2_muls(normals[0], AA_SIZE)); - temp[1] = zr_vec2_sub(points[0], zr_vec2_muls(normals[0], AA_SIZE)); - d = zr_vec2_muls(normals[points_count-1], AA_SIZE); - temp[(points_count-1) * 2 + 0] = zr_vec2_add(points[points_count-1], d); - temp[(points_count-1) * 2 + 1] = zr_vec2_sub(points[points_count-1], d); - } - - /* fill elements */ - idx1 = index; - for (i1 = 0; i1 < count; i1++) { - struct zr_vec2 dm; - float dmr2; - zr_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); - zr_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3); - - /* average normals */ - dm = zr_vec2_muls(zr_vec2_add(normals[i1], normals[i2]), 0.5f); - dmr2 = dm.x * dm.x + dm.y* dm.y; - if (dmr2 > 0.000001f) { - float scale = 1.0f/dmr2; - scale = ZR_MIN(100.0f, scale); - dm = zr_vec2_muls(dm, scale); - } - - dm = zr_vec2_muls(dm, AA_SIZE); - temp[i2*2+0] = zr_vec2_add(points[i2], dm); - temp[i2*2+1] = zr_vec2_sub(points[i2], dm); - - ids[0] = (zr_draw_index)(idx2 + 0); ids[1] = (zr_draw_index)(idx1+0); - ids[2] = (zr_draw_index)(idx1 + 2); ids[3] = (zr_draw_index)(idx1+2); - ids[4] = (zr_draw_index)(idx2 + 2); ids[5] = (zr_draw_index)(idx2+0); - ids[6] = (zr_draw_index)(idx2 + 1); ids[7] = (zr_draw_index)(idx1+1); - ids[8] = (zr_draw_index)(idx1 + 0); ids[9] = (zr_draw_index)(idx1+0); - ids[10]= (zr_draw_index)(idx2 + 0); ids[11]= (zr_draw_index)(idx2+1); - ids += 12; - idx1 = idx2; - } - - /* fill vertices */ - for (i = 0; i < points_count; ++i) { - const struct zr_vec2 uv = list->null.uv; - vtx[0] = zr_draw_vertex(points[i], uv, col); - vtx[1] = zr_draw_vertex(temp[i*2+0], uv, col_trans); - vtx[2] = zr_draw_vertex(temp[i*2+1], uv, col_trans); - vtx += 3; - } - } else { - zr_size idx1, i; - const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; - if (!closed) { - struct zr_vec2 d1 = zr_vec2_muls(normals[0], half_inner_thickness + AA_SIZE); - struct zr_vec2 d2 = zr_vec2_muls(normals[0], half_inner_thickness); - - temp[0] = zr_vec2_add(points[0], d1); - temp[1] = zr_vec2_add(points[0], d2); - temp[2] = zr_vec2_sub(points[0], d2); - temp[3] = zr_vec2_sub(points[0], d1); - - d1 = zr_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE); - d2 = zr_vec2_muls(normals[points_count-1], half_inner_thickness); - - temp[(points_count-1)*4+0] = zr_vec2_add(points[points_count-1], d1); - temp[(points_count-1)*4+1] = zr_vec2_add(points[points_count-1], d2); - temp[(points_count-1)*4+2] = zr_vec2_sub(points[points_count-1], d2); - temp[(points_count-1)*4+3] = zr_vec2_sub(points[points_count-1], d1); - } - - /* add all elements */ - idx1 = index; - for (i1 = 0; i1 < count; ++i1) { - struct zr_vec2 dm_out, dm_in; - const zr_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1); - zr_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4); - - /* average normals */ - struct zr_vec2 dm = zr_vec2_muls(zr_vec2_add(normals[i1], normals[i2]), 0.5f); - float dmr2 = dm.x * dm.x + dm.y* dm.y; - if (dmr2 > 0.000001f) { - float scale = 1.0f/dmr2; - scale = ZR_MIN(100.0f, scale); - dm = zr_vec2_muls(dm, scale); - } - - dm_out = zr_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE)); - dm_in = zr_vec2_muls(dm, half_inner_thickness); - temp[i2*4+0] = zr_vec2_add(points[i2], dm_out); - temp[i2*4+1] = zr_vec2_add(points[i2], dm_in); - temp[i2*4+2] = zr_vec2_sub(points[i2], dm_in); - temp[i2*4+3] = zr_vec2_sub(points[i2], dm_out); - - /* add indexes */ - ids[0] = (zr_draw_index)(idx2 + 1); ids[1] = (zr_draw_index)(idx1+1); - ids[2] = (zr_draw_index)(idx1 + 2); ids[3] = (zr_draw_index)(idx1+2); - ids[4] = (zr_draw_index)(idx2 + 2); ids[5] = (zr_draw_index)(idx2+1); - ids[6] = (zr_draw_index)(idx2 + 1); ids[7] = (zr_draw_index)(idx1+1); - ids[8] = (zr_draw_index)(idx1 + 0); ids[9] = (zr_draw_index)(idx1+0); - ids[10]= (zr_draw_index)(idx2 + 0); ids[11] = (zr_draw_index)(idx2+1); - ids[12]= (zr_draw_index)(idx2 + 2); ids[13] = (zr_draw_index)(idx1+2); - ids[14]= (zr_draw_index)(idx1 + 3); ids[15] = (zr_draw_index)(idx1+3); - ids[16]= (zr_draw_index)(idx2 + 3); ids[17] = (zr_draw_index)(idx2+2); - ids += 18; - idx1 = idx2; - } - - /* add vertices */ - for (i = 0; i < points_count; ++i) { - const struct zr_vec2 uv = list->null.uv; - vtx[0] = zr_draw_vertex(temp[i*4+0], uv, col_trans); - vtx[1] = zr_draw_vertex(temp[i*4+1], uv, col); - vtx[2] = zr_draw_vertex(temp[i*4+2], uv, col); - vtx[3] = zr_draw_vertex(temp[i*4+3], uv, col_trans); - vtx += 4; - } - } - - /* free temporary normals + points */ - zr_buffer_reset(list->vertices, ZR_BUFFER_FRONT); - } else { - /* NON ANTI-ALIASED STROKE */ - zr_size i1 = 0; - zr_size idx = list->vertex_count; - const zr_size idx_count = count * 6; - const zr_size vtx_count = count * 4; - struct zr_draw_vertex *vtx = zr_draw_list_alloc_vertices(list, vtx_count); - zr_draw_index *ids = zr_draw_list_alloc_elements(list, idx_count); - if (!vtx || !ids) return; - - for (i1 = 0; i1 < count; ++i1) { - float dx, dy; - const struct zr_vec2 uv = list->null.uv; - const zr_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; - const struct zr_vec2 p1 = points[i1]; - const struct zr_vec2 p2 = points[i2]; - struct zr_vec2 diff = zr_vec2_sub(p2, p1); - float len; - - /* vec2 inverted lenth */ - len = zr_vec2_len_sqr(diff); - if (len != 0.0f) - len = zr_inv_sqrt(len); - else len = 1.0f; - diff = zr_vec2_muls(diff, len); - - /* add vertices */ - dx = diff.x * (thickness * 0.5f); - dy = diff.y * (thickness * 0.5f); - - vtx[0] = zr_draw_vertex(zr_vec2(p1.x + dy, p1.y - dx), uv, col); - vtx[1] = zr_draw_vertex(zr_vec2(p2.x + dy, p2.y - dx), uv, col); - vtx[2] = zr_draw_vertex(zr_vec2(p2.x - dy, p2.y + dx), uv, col); - vtx[3] = zr_draw_vertex(zr_vec2(p1.x - dy, p1.y + dx), uv, col); - vtx += 4; - - ids[0] = (zr_draw_index)(idx+0); ids[1] = (zr_draw_index)(idx+1); - ids[2] = (zr_draw_index)(idx+2); ids[3] = (zr_draw_index)(idx+0); - ids[4] = (zr_draw_index)(idx+2); ids[5] = (zr_draw_index)(idx+3); - ids += 6; - idx += 4; - } - } -} - -void -zr_draw_list_fill_poly_convex(struct zr_draw_list *list, - const struct zr_vec2 *points, const unsigned int points_count, - struct zr_color color, enum zr_anti_aliasing aliasing) -{ - static const zr_size pnt_align = ZR_ALIGNOF(struct zr_vec2); - static const zr_size pnt_size = sizeof(struct zr_vec2); - zr_draw_vertex_color col; - ZR_ASSERT(list); - if (!list || points_count < 3) return; - -#if ZR_COMPILE_WITH_COMMAND_USERDATA - zr_draw_list_push_userdata(list, list->userdata); -#endif - - color.a = (zr_byte)((float)color.a * list->global_alpha); - col = zr_color_u32(color); - if (aliasing == ZR_ANTI_ALIASING_ON) { - zr_size i = 0; - zr_size i0 = 0; - zr_size i1 = 0; - - const float AA_SIZE = 1.0f; - const zr_draw_vertex_color col_trans = col & 0x00ffffff; - zr_size index = list->vertex_count; - const zr_size idx_count = (points_count-2)*3 + points_count*6; - const zr_size vtx_count = (points_count*2); - struct zr_draw_vertex *vtx = zr_draw_list_alloc_vertices(list, vtx_count); - zr_draw_index *ids = zr_draw_list_alloc_elements(list, idx_count); - - unsigned int vtx_inner_idx = (unsigned int)(index + 0); - unsigned int vtx_outer_idx = (unsigned int)(index + 1); - struct zr_vec2 *normals = 0; - zr_size size = 0; - if (!vtx || !ids) return; - - /* temporary allocate normals */ - zr_buffer_mark(list->vertices, ZR_BUFFER_FRONT); - size = pnt_size * points_count; - normals = (struct zr_vec2*) - zr_buffer_alloc(list->vertices, ZR_BUFFER_FRONT, size, pnt_align); - if (!normals) return; - - /* add elements */ - for (i = 2; i < points_count; i++) { - ids[0] = (zr_draw_index)(vtx_inner_idx); - ids[1] = (zr_draw_index)(vtx_inner_idx + ((i-1) << 1)); - ids[2] = (zr_draw_index)(vtx_inner_idx + (i << 1)); - ids += 3; - } - - /* compute normals */ - for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { - struct zr_vec2 p0 = points[i0]; - struct zr_vec2 p1 = points[i1]; - struct zr_vec2 diff = zr_vec2_sub(p1, p0); - - /* vec2 inverted lenth */ - float len = zr_vec2_len_sqr(diff); - if (len != 0.0f) - len = zr_inv_sqrt(len); - else len = 1.0f; - diff = zr_vec2_muls(diff, len); - - normals[i0].x = diff.y; - normals[i0].y = -diff.x; - } - - /* add vertices + indexes */ - for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { - const struct zr_vec2 uv = list->null.uv; - struct zr_vec2 n0 = normals[i0]; - struct zr_vec2 n1 = normals[i1]; - struct zr_vec2 dm = zr_vec2_muls(zr_vec2_add(n0, n1), 0.5f); - - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) { - float scale = 1.0f / dmr2; - scale = ZR_MIN(scale, 100.0f); - dm = zr_vec2_muls(dm, scale); - } - dm = zr_vec2_muls(dm, AA_SIZE * 0.5f); - - /* add vertices */ - vtx[0] = zr_draw_vertex(zr_vec2_sub(points[i1], dm), uv, col); - vtx[1] = zr_draw_vertex(zr_vec2_add(points[i1], dm), uv, col_trans); - vtx += 2; - - /* add indexes */ - ids[0] = (zr_draw_index)(vtx_inner_idx+(i1<<1)); - ids[1] = (zr_draw_index)(vtx_inner_idx+(i0<<1)); - ids[2] = (zr_draw_index)(vtx_outer_idx+(i0<<1)); - ids[3] = (zr_draw_index)(vtx_outer_idx+(i0<<1)); - ids[4] = (zr_draw_index)(vtx_outer_idx+(i1<<1)); - ids[5] = (zr_draw_index)(vtx_inner_idx+(i1<<1)); - ids += 6; - } - /* free temporary normals + points */ - zr_buffer_reset(list->vertices, ZR_BUFFER_FRONT); - } else { - zr_size i = 0; - zr_size index = list->vertex_count; - const zr_size idx_count = (points_count-2)*3; - const zr_size vtx_count = points_count; - struct zr_draw_vertex *vtx = zr_draw_list_alloc_vertices(list, vtx_count); - zr_draw_index *ids = zr_draw_list_alloc_elements(list, idx_count); - if (!vtx || !ids) return; - for (i = 0; i < vtx_count; ++i) { - vtx[0] = zr_draw_vertex(points[i], list->null.uv, col); - vtx++; - } - for (i = 2; i < points_count; ++i) { - ids[0] = (zr_draw_index)index; - ids[1] = (zr_draw_index)(index+ i - 1); - ids[2] = (zr_draw_index)(index+i); - ids += 3; - } - } -} - -void -zr_draw_list_path_clear(struct zr_draw_list *list) -{ - ZR_ASSERT(list); - if (!list) return; - zr_buffer_reset(list->buffer, ZR_BUFFER_FRONT); - list->path_count = 0; - list->path_offset = 0; -} - -void -zr_draw_list_path_line_to(struct zr_draw_list *list, struct zr_vec2 pos) -{ - struct zr_vec2 *points = 0; - struct zr_draw_command *cmd = 0; - ZR_ASSERT(list); - if (!list) return; - if (!list->cmd_count) - zr_draw_list_add_clip(list, zr_null_rect); - - cmd = zr_draw_list_command_last(list); - if (cmd && cmd->texture.ptr != list->null.texture.ptr) - zr_draw_list_push_image(list, list->null.texture); - - points = zr_draw_list_alloc_path(list, 1); - if (!points) return; - points[0] = pos; -} - -void -zr_draw_list_path_arc_to_fast(struct zr_draw_list *list, struct zr_vec2 center, - float radius, int a_min, int a_max) -{ - ZR_ASSERT(list); - if (!list) return; - if (a_min <= a_max) { - int a = 0; - for (a = a_min; a <= a_max; a++) { - const struct zr_vec2 c = list->circle_vtx[(zr_size)a % ZR_LEN(list->circle_vtx)]; - const float x = center.x + c.x * radius; - const float y = center.y + c.y * radius; - zr_draw_list_path_line_to(list, zr_vec2(x, y)); - } - } -} - -void -zr_draw_list_path_arc_to(struct zr_draw_list *list, struct zr_vec2 center, - float radius, float a_min, float a_max, unsigned int segments) -{ - unsigned int i = 0; - ZR_ASSERT(list); - if (!list) return; - if (radius == 0.0f) return; - for (i = 0; i <= segments; ++i) { - const float a = a_min + ((float)i / ((float)segments) * (a_max - a_min)); - const float x = center.x + (float)zr_cos(a) * radius; - const float y = center.y + (float)zr_sin(a) * radius; - zr_draw_list_path_line_to(list, zr_vec2(x, y)); - } -} - -void -zr_draw_list_path_rect_to(struct zr_draw_list *list, struct zr_vec2 a, - struct zr_vec2 b, float rounding) -{ - float r; - ZR_ASSERT(list); - if (!list) return; - r = rounding; - r = ZR_MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x)); - r = ZR_MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y)); - - if (r == 0.0f) { - zr_draw_list_path_line_to(list, a); - zr_draw_list_path_line_to(list, zr_vec2(b.x,a.y)); - zr_draw_list_path_line_to(list, b); - zr_draw_list_path_line_to(list, zr_vec2(a.x,b.y)); - } else { - zr_draw_list_path_arc_to_fast(list, zr_vec2(a.x + r, a.y + r), r, 6, 9); - zr_draw_list_path_arc_to_fast(list, zr_vec2(b.x - r, a.y + r), r, 9, 12); - zr_draw_list_path_arc_to_fast(list, zr_vec2(b.x - r, b.y - r), r, 0, 3); - zr_draw_list_path_arc_to_fast(list, zr_vec2(a.x + r, b.y - r), r, 3, 6); - } -} - -void -zr_draw_list_path_curve_to(struct zr_draw_list *list, struct zr_vec2 p2, - struct zr_vec2 p3, struct zr_vec2 p4, unsigned int num_segments) -{ - unsigned int i_step; - float t_step; - struct zr_vec2 p1; - - ZR_ASSERT(list); - ZR_ASSERT(list->path_count); - if (!list || !list->path_count) return; - num_segments = ZR_MAX(num_segments, 1); - - p1 = zr_draw_list_path_last(list); - t_step = 1.0f/(float)num_segments; - for (i_step = 1; i_step <= num_segments; ++i_step) { - float t = t_step * (float)i_step; - float u = 1.0f - t; - float w1 = u*u*u; - float w2 = 3*u*u*t; - float w3 = 3*u*t*t; - float w4 = t * t *t; - float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; - float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; - zr_draw_list_path_line_to(list, zr_vec2(x,y)); - } -} - -void -zr_draw_list_path_fill(struct zr_draw_list *list, struct zr_color color) -{ - struct zr_vec2 *points; - ZR_ASSERT(list); - if (!list) return; - points = (struct zr_vec2*)zr_buffer_memory(list->buffer); - zr_draw_list_fill_poly_convex(list, points, list->path_count, color, list->shape_AA); - zr_draw_list_path_clear(list); -} - -void -zr_draw_list_path_stroke(struct zr_draw_list *list, struct zr_color color, - enum zr_draw_list_stroke closed, float thickness) -{ - struct zr_vec2 *points; - ZR_ASSERT(list); - if (!list) return; - points = (struct zr_vec2*)zr_buffer_memory(list->buffer); - zr_draw_list_stroke_poly_line(list, points, list->path_count, color, - closed, thickness, list->line_AA); - zr_draw_list_path_clear(list); -} - -void -zr_draw_list_stroke_line(struct zr_draw_list *list, struct zr_vec2 a, - struct zr_vec2 b, struct zr_color col, float thickness) -{ - ZR_ASSERT(list); - if (!list || !col.a) return; - zr_draw_list_path_line_to(list, zr_vec2_add(a, zr_vec2(0.5f, 0.5f))); - zr_draw_list_path_line_to(list, zr_vec2_add(b, zr_vec2(0.5f, 0.5f))); - zr_draw_list_path_stroke(list, col, ZR_STROKE_OPEN, thickness); -} - -void -zr_draw_list_fill_rect(struct zr_draw_list *list, struct zr_rect rect, - struct zr_color col, float rounding) -{ - ZR_ASSERT(list); - if (!list || !col.a) return; - zr_draw_list_path_rect_to(list, zr_vec2(rect.x + 0.5f, rect.y + 0.5f), - zr_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); - zr_draw_list_path_fill(list, col); -} - -void -zr_draw_list_stroke_rect(struct zr_draw_list *list, struct zr_rect rect, - struct zr_color col, float rounding, float thickness) -{ - ZR_ASSERT(list); - if (!list || !col.a) return; - zr_draw_list_path_rect_to(list, zr_vec2(rect.x + 0.5f, rect.y + 0.5f), - zr_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); - zr_draw_list_path_stroke(list, col, ZR_STROKE_CLOSED, thickness); -} - -void -zr_draw_list_fill_rect_multi_color(struct zr_draw_list *list, struct zr_rect rect, - struct zr_color left, struct zr_color top, struct zr_color right, - struct zr_color bottom) -{ - zr_draw_vertex_color col_left = zr_color_u32(left); - zr_draw_vertex_color col_top = zr_color_u32(top); - zr_draw_vertex_color col_right = zr_color_u32(right); - zr_draw_vertex_color col_bottom = zr_color_u32(bottom); - - struct zr_draw_vertex *vtx; - zr_draw_index *idx; - zr_draw_index index; - ZR_ASSERT(list); - if (!list) return; - - zr_draw_list_push_image(list, list->null.texture); - index = (zr_draw_index)list->vertex_count; - vtx = zr_draw_list_alloc_vertices(list, 4); - idx = zr_draw_list_alloc_elements(list, 6); - if (!vtx || !idx) return; - - idx[0] = (zr_draw_index)(index+0); idx[1] = (zr_draw_index)(index+1); - idx[2] = (zr_draw_index)(index+2); idx[3] = (zr_draw_index)(index+0); - idx[4] = (zr_draw_index)(index+2); idx[5] = (zr_draw_index)(index+3); - - vtx[0] = zr_draw_vertex(zr_vec2(rect.x, rect.y), list->null.uv, col_left); - vtx[1] = zr_draw_vertex(zr_vec2(rect.x + rect.w, rect.y), list->null.uv, col_top); - vtx[2] = zr_draw_vertex(zr_vec2(rect.x + rect.w, rect.y + rect.h), list->null.uv, col_right); - vtx[3] = zr_draw_vertex(zr_vec2(rect.x, rect.y + rect.h), list->null.uv, col_bottom); -} - -void -zr_draw_list_fill_triangle(struct zr_draw_list *list, struct zr_vec2 a, - struct zr_vec2 b, struct zr_vec2 c, struct zr_color col) -{ - ZR_ASSERT(list); - if (!list || !col.a) return; - zr_draw_list_path_line_to(list, a); - zr_draw_list_path_line_to(list, b); - zr_draw_list_path_line_to(list, c); - zr_draw_list_path_fill(list, col); -} - -void -zr_draw_list_stroke_triangle(struct zr_draw_list *list, struct zr_vec2 a, - struct zr_vec2 b, struct zr_vec2 c, struct zr_color col, float thickness) -{ - ZR_ASSERT(list); - if (!list || !col.a) return; - zr_draw_list_path_line_to(list, a); - zr_draw_list_path_line_to(list, b); - zr_draw_list_path_line_to(list, c); - zr_draw_list_path_stroke(list, col, ZR_STROKE_CLOSED, thickness); -} - -void -zr_draw_list_fill_circle(struct zr_draw_list *list, struct zr_vec2 center, - float radius, struct zr_color col, unsigned int segs) -{ - float a_max; - ZR_ASSERT(list); - if (!list || !col.a) return; - a_max = ZR_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; - zr_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); - zr_draw_list_path_fill(list, col); -} - -void -zr_draw_list_stroke_circle(struct zr_draw_list *list, struct zr_vec2 center, - float radius, struct zr_color col, unsigned int segs, float thickness) -{ - float a_max; - ZR_ASSERT(list); - if (!list || !col.a) return; - a_max = ZR_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; - zr_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); - zr_draw_list_path_stroke(list, col, ZR_STROKE_CLOSED, thickness); -} - -void -zr_draw_list_stroke_curve(struct zr_draw_list *list, struct zr_vec2 p0, - struct zr_vec2 cp0, struct zr_vec2 cp1, struct zr_vec2 p1, - struct zr_color col, unsigned int segments, float thickness) -{ - ZR_ASSERT(list); - if (!list || !col.a) return; - zr_draw_list_path_line_to(list, p0); - zr_draw_list_path_curve_to(list, cp0, cp1, p1, segments); - zr_draw_list_path_stroke(list, col, ZR_STROKE_OPEN, thickness); -} - -static void -zr_draw_list_push_rect_uv(struct zr_draw_list *list, struct zr_vec2 a, - struct zr_vec2 c, struct zr_vec2 uva, struct zr_vec2 uvc, - struct zr_color color) -{ - zr_draw_vertex_color col = zr_color_u32(color); - struct zr_draw_vertex *vtx; - struct zr_vec2 uvb; - struct zr_vec2 uvd; - struct zr_vec2 b; - struct zr_vec2 d; - zr_draw_index *idx; - zr_draw_index index; - ZR_ASSERT(list); - if (!list) return; - - uvb = zr_vec2(uvc.x, uva.y); - uvd = zr_vec2(uva.x, uvc.y); - b = zr_vec2(c.x, a.y); - d = zr_vec2(a.x, c.y); - - index = (zr_draw_index)list->vertex_count; - vtx = zr_draw_list_alloc_vertices(list, 4); - idx = zr_draw_list_alloc_elements(list, 6); - if (!vtx || !idx) return; - - idx[0] = (zr_draw_index)(index+0); idx[1] = (zr_draw_index)(index+1); - idx[2] = (zr_draw_index)(index+2); idx[3] = (zr_draw_index)(index+0); - idx[4] = (zr_draw_index)(index+2); idx[5] = (zr_draw_index)(index+3); - - vtx[0] = zr_draw_vertex(a, uva, col); - vtx[1] = zr_draw_vertex(b, uvb, col); - vtx[2] = zr_draw_vertex(c, uvc, col); - vtx[3] = zr_draw_vertex(d, uvd, col); -} - -void -zr_draw_list_add_image(struct zr_draw_list *list, struct zr_image texture, - struct zr_rect rect, struct zr_color color) -{ - ZR_ASSERT(list); - if (!list) return; - /* push new command with given texture */ - zr_draw_list_push_image(list, texture.handle); - if (zr_image_is_subimage(&texture)) { - /* add region inside of the texture */ - struct zr_vec2 uv[2]; - uv[0].x = (float)texture.region[0]/(float)texture.w; - uv[0].y = (float)texture.region[1]/(float)texture.h; - uv[1].x = (float)(texture.region[0] + texture.region[2])/(float)texture.w; - uv[1].y = (float)(texture.region[1] + texture.region[3])/(float)texture.h; - zr_draw_list_push_rect_uv(list, zr_vec2(rect.x, rect.y), - zr_vec2(rect.x + rect.w, rect.y + rect.h), uv[0], uv[1], color); - } else zr_draw_list_push_rect_uv(list, zr_vec2(rect.x, rect.y), - zr_vec2(rect.x + rect.w, rect.y + rect.h), - zr_vec2(0.0f, 0.0f), zr_vec2(1.0f, 1.0f),color); -} - -void -zr_draw_list_add_text(struct zr_draw_list *list, const struct zr_user_font *font, - struct zr_rect rect, const char *text, zr_size len, float font_height, - struct zr_color fg) -{ - float x; - zr_size text_len; - zr_rune unicode; - zr_rune next; - zr_size glyph_len; - zr_size next_glyph_len; - struct zr_user_font_glyph g; - - ZR_ASSERT(list); - if (!list || !len || !text) return; - if (rect.x > (list->clip_rect.x + list->clip_rect.w) || - rect.y > (list->clip_rect.y + list->clip_rect.h) || - rect.x < list->clip_rect.x || rect.y < list->clip_rect.y) - return; - - zr_draw_list_push_image(list, font->texture); - x = rect.x; - glyph_len = text_len = zr_utf_decode(text, &unicode, len); - if (!glyph_len) return; - - /* draw every glyph image */ - while (text_len <= len && glyph_len) { - float gx, gy, gh, gw; - float char_width = 0; - if (unicode == ZR_UTF_INVALID) break; - - /* query currently drawn glyph information */ - next_glyph_len = zr_utf_decode(text + text_len, &next, len - text_len); - font->query(font->userdata, font_height, &g, unicode, - (next == ZR_UTF_INVALID) ? '\0' : next); - - /* calculate and draw glyph drawing rectangle and image */ - gx = x + g.offset.x; - /*gy = rect.y + (rect.h/2) - (font->height/2) + g.offset.y;*/ - gy = rect.y + g.offset.y; - gw = g.width; gh = g.height; - char_width = g.xadvance; - fg.a = (zr_byte)((float)fg.a * list->global_alpha); - zr_draw_list_push_rect_uv(list, zr_vec2(gx,gy), zr_vec2(gx + gw, gy+ gh), - g.uv[0], g.uv[1], fg); - - /* offset next glyph */ - text_len += glyph_len; - x += char_width; - glyph_len = next_glyph_len; - unicode = next; - } -} - -void -zr_convert(struct zr_context *ctx, struct zr_buffer *cmds, - struct zr_buffer *vertices, struct zr_buffer *elements, - const struct zr_convert_config *config) -{ - const struct zr_command *cmd; - ZR_ASSERT(ctx); - ZR_ASSERT(cmds); - ZR_ASSERT(vertices); - ZR_ASSERT(elements); - if (!ctx || !cmds || !vertices || !elements) - return; - - zr_draw_list_setup(&ctx->draw_list, config->global_alpha, config->line_AA, - config->shape_AA, config->null, cmds, vertices, elements); - zr_foreach(cmd, ctx) - { -#if ZR_COMPILE_WITH_COMMAND_USERDATA - list->userdata = cmd->userdata; -#endif - switch (cmd->type) { - case ZR_COMMAND_NOP: break; - case ZR_COMMAND_SCISSOR: { - const struct zr_command_scissor *s = zr_command(scissor, cmd); - zr_draw_list_add_clip(&ctx->draw_list, zr_rect(s->x, s->y, s->w, s->h)); - } break; - case ZR_COMMAND_LINE: { - const struct zr_command_line *l = zr_command(line, cmd); - zr_draw_list_stroke_line(&ctx->draw_list, zr_vec2(l->begin.x, l->begin.y), - zr_vec2(l->end.x, l->end.y), l->color, l->line_thickness); - } break; - case ZR_COMMAND_CURVE: { - const struct zr_command_curve *q = zr_command(curve, cmd); - zr_draw_list_stroke_curve(&ctx->draw_list, zr_vec2(q->begin.x, q->begin.y), - zr_vec2(q->ctrl[0].x, q->ctrl[0].y), zr_vec2(q->ctrl[1].x, - q->ctrl[1].y), zr_vec2(q->end.x, q->end.y), q->color, - config->curve_segment_count, q->line_thickness); - } break; - case ZR_COMMAND_RECT: { - const struct zr_command_rect *r = zr_command(rect, cmd); - zr_draw_list_stroke_rect(&ctx->draw_list, zr_rect(r->x, r->y, r->w, r->h), - r->color, (float)r->rounding, r->line_thickness); - } break; - case ZR_COMMAND_RECT_FILLED: { - const struct zr_command_rect_filled *r = zr_command(rect_filled, cmd); - zr_draw_list_fill_rect(&ctx->draw_list, zr_rect(r->x, r->y, r->w, r->h), - r->color, (float)r->rounding); - } break; - case ZR_COMMAND_RECT_MULTI_COLOR: { - const struct zr_command_rect_multi_color *r = zr_command(rect_multi_color, cmd); - zr_draw_list_fill_rect_multi_color(&ctx->draw_list, zr_rect(r->x, r->y, r->w, r->h), - r->left, r->top, r->right, r->bottom); - } break; - case ZR_COMMAND_CIRCLE: { - const struct zr_command_circle *c = zr_command(circle, cmd); - zr_draw_list_stroke_circle(&ctx->draw_list, zr_vec2((float)c->x + (float)c->w/2, - (float)c->y + (float)c->h/2), (float)c->w/2, c->color, - config->circle_segment_count, c->line_thickness); - } break; - case ZR_COMMAND_CIRCLE_FILLED: { - const struct zr_command_circle_filled *c = zr_command(circle_filled, cmd); - zr_draw_list_fill_circle(&ctx->draw_list, zr_vec2((float)c->x + (float)c->w/2, - (float)c->y + (float)c->h/2), (float)c->w/2, c->color, - config->circle_segment_count); - } break; - case ZR_COMMAND_ARC: { - const struct zr_command_arc *c = zr_command(arc, cmd); - zr_draw_list_path_line_to(&ctx->draw_list, zr_vec2(c->cx, c->cy)); - zr_draw_list_path_arc_to(&ctx->draw_list, zr_vec2(c->cx, c->cy), c->r, - c->a[0], c->a[1], config->arc_segment_count); - zr_draw_list_path_stroke(&ctx->draw_list, c->color, ZR_STROKE_CLOSED, c->line_thickness); - } break; - case ZR_COMMAND_ARC_FILLED: { - const struct zr_command_arc_filled *c = zr_command(arc_filled, cmd); - zr_draw_list_path_line_to(&ctx->draw_list, zr_vec2(c->cx, c->cy)); - zr_draw_list_path_arc_to(&ctx->draw_list, zr_vec2(c->cx, c->cy), c->r, - c->a[0], c->a[1], config->arc_segment_count); - zr_draw_list_path_fill(&ctx->draw_list, c->color); - } break; - case ZR_COMMAND_TRIANGLE: { - const struct zr_command_triangle *t = zr_command(triangle, cmd); - zr_draw_list_stroke_triangle(&ctx->draw_list, zr_vec2(t->a.x, t->a.y), - zr_vec2(t->b.x, t->b.y), zr_vec2(t->c.x, t->c.y), t->color, - t->line_thickness); - } break; - case ZR_COMMAND_TRIANGLE_FILLED: { - const struct zr_command_triangle_filled *t = zr_command(triangle_filled, cmd); - zr_draw_list_fill_triangle(&ctx->draw_list, zr_vec2(t->a.x, t->a.y), - zr_vec2(t->b.x, t->b.y), zr_vec2(t->c.x, t->c.y), t->color); - } break; - case ZR_COMMAND_POLYGON: { - int i; - const struct zr_command_polygon*p = zr_command(polygon, cmd); - for (i = 0; i < p->point_count; ++i) { - struct zr_vec2 pnt = zr_vec2((float)p->points[i].x, (float)p->points[i].y); - zr_draw_list_path_line_to(&ctx->draw_list, pnt); - } - zr_draw_list_path_stroke(&ctx->draw_list, p->color, ZR_STROKE_CLOSED, p->line_thickness); - } break; - case ZR_COMMAND_POLYGON_FILLED: { - int i; - const struct zr_command_polygon_filled *p = zr_command(polygon_filled, cmd); - for (i = 0; i < p->point_count; ++i) { - struct zr_vec2 pnt = zr_vec2((float)p->points[i].x, (float)p->points[i].y); - zr_draw_list_path_line_to(&ctx->draw_list, pnt); - } - zr_draw_list_path_fill(&ctx->draw_list, p->color); - } break; - case ZR_COMMAND_POLYLINE: { - int i; - const struct zr_command_polyline *p = zr_command(polyline, cmd); - for (i = 0; i < p->point_count; ++i) { - struct zr_vec2 pnt = zr_vec2((float)p->points[i].x, (float)p->points[i].y); - zr_draw_list_path_line_to(&ctx->draw_list, pnt); - } - zr_draw_list_path_stroke(&ctx->draw_list, p->color, ZR_STROKE_OPEN, p->line_thickness); - } break; - case ZR_COMMAND_TEXT: { - const struct zr_command_text *t = zr_command(text, cmd); - zr_draw_list_add_text(&ctx->draw_list, t->font, zr_rect(t->x, t->y, t->w, t->h), - t->string, t->length, t->height, t->foreground); - } break; - case ZR_COMMAND_IMAGE: { - const struct zr_command_image *i = zr_command(image, cmd); - zr_draw_list_add_image(&ctx->draw_list, i->img, zr_rect(i->x, i->y, i->w, i->h), - zr_rgb(255, 255, 255)); - } break; - default: break; - } - } -} - -const struct zr_draw_command* -zr__draw_begin(const struct zr_context *ctx, - const struct zr_buffer *buffer) -{return zr__draw_list_begin(&ctx->draw_list, buffer);} - -const struct zr_draw_command* -zr__draw_next(const struct zr_draw_command *cmd, - const struct zr_buffer *buffer, const struct zr_context *ctx) -{return zr__draw_list_next(cmd, buffer, &ctx->draw_list);} - -#endif -/* - * ============================================================== - * - * FONT - * - * =============================================================== - */ -#if ZR_COMPILE_WITH_FONT -/* this is a bloody mess but 'fixing' both stb libraries would be a pain */ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#pragma clang diagnostic ignored "-Wfloat-equal" -#pragma clang diagnostic ignored "-Wbad-function-cast" -#pragma clang diagnostic ignored "-Wcast-qual" -#pragma clang diagnostic ignored "-Wshadow" -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#pragma clang diagnostic ignored "-Wdeclaration-after-statement" -#pragma clang diagnostic ignored "-Wunused-function" -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wbad-function-cast" -#pragma GCC diagnostic ignored "-Wcast-qual" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" -#pragma GCC diagnostic ignored "-Wtype-limits" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wunused-function" -#elif _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4456) -#endif - -#if !ZR_DISABLE_STB_RECT_PACK_IMPLEMENTATION -#define STBRP_STATIC -#define STB_RECT_PACK_IMPLEMENTATION -#endif -#include "stb_rect_pack.h" - -#if !ZR_DISABLE_STB_TRUETYPE_IMPLEMENTATION -#define STBTT_STATIC -#define STB_TRUETYPE_IMPLEMENTATION -#endif -#include "stb_truetype.h" - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic pop -#elif _MSC_VER -#pragma warning (pop) -#endif - -/* ------------------------------------------------------------- - * - * FONT BAKING - * - * --------------------------------------------------------------*/ -struct zr_font_bake_data { - stbtt_fontinfo info; - stbrp_rect *rects; - stbtt_pack_range *ranges; - zr_rune range_count; -}; - -struct zr_font_baker { - stbtt_pack_context spc; - struct zr_font_bake_data *build; - stbtt_packedchar *packed_chars; - stbrp_rect *rects; - stbtt_pack_range *ranges; -}; - -static const zr_size zr_rect_align = ZR_ALIGNOF(stbrp_rect); -static const zr_size zr_range_align = ZR_ALIGNOF(stbtt_pack_range); -static const zr_size zr_char_align = ZR_ALIGNOF(stbtt_packedchar); -static const zr_size zr_build_align = ZR_ALIGNOF(struct zr_font_bake_data); -static const zr_size zr_baker_align = ZR_ALIGNOF(struct zr_font_baker); - -static int -zr_range_count(const zr_rune *range) -{ - const zr_rune *iter = range; - ZR_ASSERT(range); - if (!range) return 0; - while (*(iter++) != 0); - return (iter == range) ? 0 : (int)((iter - range)/2); -} - -static int -zr_range_glyph_count(const zr_rune *range, int count) -{ - int i = 0; - int total_glyphs = 0; - for (i = 0; i < count; ++i) { - int diff; - zr_rune f = range[(i*2)+0]; - zr_rune t = range[(i*2)+1]; - ZR_ASSERT(t >= f); - diff = (int)((t - f) + 1); - total_glyphs += diff; - } - return total_glyphs; -} - -const zr_rune* -zr_font_default_glyph_ranges(void) -{ - static const zr_rune ranges[] = {0x0020, 0x00FF, 0}; - return ranges; -} - -const zr_rune* -zr_font_chinese_glyph_ranges(void) -{ - static const zr_rune ranges[] = { - 0x0020, 0x00FF, - 0x3000, 0x30FF, - 0x31F0, 0x31FF, - 0xFF00, 0xFFEF, - 0x4e00, 0x9FAF, - 0 - }; - return ranges; -} - -const zr_rune* -zr_font_cyrillic_glyph_ranges(void) -{ - static const zr_rune ranges[] = { - 0x0020, 0x00FF, - 0x0400, 0x052F, - 0x2DE0, 0x2DFF, - 0xA640, 0xA69F, - 0 - }; - return ranges; -} - -const zr_rune* -zr_font_korean_glyph_ranges(void) -{ - static const zr_rune ranges[] = { - 0x0020, 0x00FF, - 0x3131, 0x3163, - 0xAC00, 0xD79D, - 0 - }; - return ranges; -} - -void -zr_font_bake_memory(zr_size *temp, int *glyph_count, - struct zr_font_config *config, int count) -{ - int i; - int range_count = 0; - ZR_ASSERT(config); - ZR_ASSERT(glyph_count); - if (!config) { - *temp = 0; - *glyph_count = 0; - return; - } - - *glyph_count = 0; - if (!config->range) - config->range = zr_font_default_glyph_ranges(); - for (i = 0; i < count; ++i) { - range_count += zr_range_count(config[i].range); - *glyph_count += zr_range_glyph_count(config[i].range, range_count); - } - - *temp = (zr_size)*glyph_count * sizeof(stbrp_rect); - *temp += (zr_size)range_count * sizeof(stbtt_pack_range); - *temp += (zr_size)*glyph_count * sizeof(stbtt_packedchar); - *temp += (zr_size)count * sizeof(struct zr_font_bake_data); - *temp += sizeof(struct zr_font_baker); - *temp += zr_rect_align + zr_range_align + zr_char_align; - *temp += zr_build_align + zr_baker_align; -} - -static struct zr_font_baker* -zr_font_baker(void *memory, int glyph_count, int count) -{ - struct zr_font_baker *baker; - if (!memory) return 0; - /* setup baker inside a memory block */ - baker = (struct zr_font_baker*)ZR_ALIGN_PTR(memory, zr_baker_align); - baker->build = (struct zr_font_bake_data*)ZR_ALIGN_PTR((baker + 1), zr_build_align); - baker->packed_chars = (stbtt_packedchar*)ZR_ALIGN_PTR((baker->build + count), zr_char_align); - baker->rects = (stbrp_rect*)ZR_ALIGN_PTR((baker->packed_chars + glyph_count), zr_rect_align); - baker->ranges = (stbtt_pack_range*)ZR_ALIGN_PTR((baker->rects + glyph_count), zr_range_align); - return baker; -} - -int -zr_font_bake_pack(zr_size *image_memory, int *width, int *height, - struct zr_recti *custom, void *temp, zr_size temp_size, - const struct zr_font_config *config, int count) -{ - static const zr_size max_height = 1024 * 32; - struct zr_font_baker* baker; - int total_glyph_count = 0; - int total_range_count = 0; - int i = 0; - - ZR_ASSERT(image_memory); - ZR_ASSERT(width); - ZR_ASSERT(height); - ZR_ASSERT(config); - ZR_ASSERT(temp); - ZR_ASSERT(temp_size); - ZR_ASSERT(count); - if (!image_memory || !width || !height || !config || !temp || - !temp_size || !count) return zr_false; - - for (i = 0; i < count; ++i) { - total_range_count += zr_range_count(config[i].range); - total_glyph_count += zr_range_glyph_count(config[i].range, total_range_count); - } - - /* setup font baker from temporary memory */ - zr_zero(temp, temp_size); - baker = zr_font_baker(temp, total_glyph_count, count); - if (!baker) return zr_false; - for (i = 0; i < count; ++i) { - const struct zr_font_config *cfg = &config[i]; - if (!stbtt_InitFont(&baker->build[i].info, (const unsigned char*)cfg->ttf_blob, 0)) - return zr_false; - } - - *height = 0; - *width = (total_glyph_count > 1000) ? 1024 : 512; - stbtt_PackBegin(&baker->spc, 0, (int)*width, (int)max_height, 0, 1, 0); - { - int input_i = 0; - int range_n = 0; - int rect_n = 0; - int char_n = 0; - - /* pack custom user data first so it will be in the upper left corner*/ - if (custom) { - stbrp_rect custom_space; - zr_zero(&custom_space, sizeof(custom_space)); - custom_space.w = (stbrp_coord)((custom->w * 2) + 1); - custom_space.h = (stbrp_coord)(custom->h + 1); - - stbtt_PackSetOversampling(&baker->spc, 1, 1); - stbrp_pack_rects((stbrp_context*)baker->spc.pack_info, &custom_space, 1); - *height = ZR_MAX(*height, (int)(custom_space.y + custom_space.h)); - - custom->x = (short)custom_space.x; - custom->y = (short)custom_space.y; - custom->w = (short)custom_space.w; - custom->h = (short)custom_space.h; - } - - /* first font pass: pack all glyphs */ - for (input_i = 0; input_i < count; input_i++) { - int n = 0; - const zr_rune *in_range; - const struct zr_font_config *cfg = &config[input_i]; - struct zr_font_bake_data *tmp = &baker->build[input_i]; - int glyph_count; - int range_count; - - /* count glyphs + ranges in current font */ - glyph_count = 0; range_count = 0; - for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) { - glyph_count += (int)(in_range[1] - in_range[0]) + 1; - range_count++; - } - - /* setup ranges */ - tmp->ranges = baker->ranges + range_n; - tmp->range_count = (zr_rune)range_count; - range_n += range_count; - for (i = 0; i < range_count; ++i) { - in_range = &cfg->range[i * 2]; - tmp->ranges[i].font_size = cfg->size; - tmp->ranges[i].first_unicode_codepoint_in_range = (int)in_range[0]; - tmp->ranges[i].num_chars = (int)(in_range[1]- in_range[0]) + 1; - tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n; - char_n += tmp->ranges[i].num_chars; - } - - /* pack */ - tmp->rects = baker->rects + rect_n; - rect_n += glyph_count; - stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); - n = stbtt_PackFontRangesGatherRects(&baker->spc, &tmp->info, - tmp->ranges, (int)tmp->range_count, tmp->rects); - stbrp_pack_rects((stbrp_context*)baker->spc.pack_info, tmp->rects, (int)n); - - /* texture height */ - for (i = 0; i < n; ++i) { - if (tmp->rects[i].was_packed) - *height = ZR_MAX(*height, tmp->rects[i].y + tmp->rects[i].h); - } - } - ZR_ASSERT(rect_n == total_glyph_count); - ZR_ASSERT(char_n == total_glyph_count); - ZR_ASSERT(range_n == total_range_count); - } - *height = (int)zr_round_up_pow2((zr_uint)*height); - *image_memory = (zr_size)(*width) * (zr_size)(*height); - return zr_true; -} - -void -zr_font_bake(void *image_memory, int width, int height, - void *temp, zr_size temp_size, struct zr_font_glyph *glyphs, - int glyphs_count, const struct zr_font_config *config, int font_count) -{ - int input_i = 0; - struct zr_font_baker* baker; - zr_rune glyph_n = 0; - - ZR_ASSERT(image_memory); - ZR_ASSERT(width); - ZR_ASSERT(height); - ZR_ASSERT(config); - ZR_ASSERT(temp); - ZR_ASSERT(temp_size); - ZR_ASSERT(font_count); - ZR_ASSERT(glyphs_count); - if (!image_memory || !width || !height || !config || !temp || - !temp_size || !font_count || !glyphs || !glyphs_count) - return; - - /* second font pass: render glyphs */ - baker = (struct zr_font_baker*)ZR_ALIGN_PTR(temp, zr_baker_align); - zr_zero(image_memory, (zr_size)((zr_size)width * (zr_size)height)); - baker->spc.pixels = (unsigned char*)image_memory; - baker->spc.height = (int)height; - for (input_i = 0; input_i < font_count; ++input_i) { - const struct zr_font_config *cfg = &config[input_i]; - struct zr_font_bake_data *tmp = &baker->build[input_i]; - stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); - stbtt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges, - (int)tmp->range_count, tmp->rects); - } - stbtt_PackEnd(&baker->spc); - - /* third pass: setup font and glyphs */ - for (input_i = 0; input_i < font_count; ++input_i) - { - zr_size i = 0; - int char_idx = 0; - zr_rune glyph_count = 0; - const struct zr_font_config *cfg = &config[input_i]; - struct zr_font_bake_data *tmp = &baker->build[input_i]; - struct zr_baked_font *dst_font = cfg->font; - - float font_scale = stbtt_ScaleForPixelHeight(&tmp->info, cfg->size); - int unscaled_ascent, unscaled_descent, unscaled_line_gap; - stbtt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent, - &unscaled_line_gap); - - /* fill baked font */ - if (!cfg->merge_mode) { - dst_font->ranges = cfg->range; - dst_font->height = cfg->size; - dst_font->ascent = ((float)unscaled_ascent * font_scale); - dst_font->descent = ((float)unscaled_descent * font_scale); - dst_font->glyph_offset = glyph_n; - } - - /* fill own baked font glyph array */ - for (i = 0; i < tmp->range_count; ++i) - { - stbtt_pack_range *range = &tmp->ranges[i]; - for (char_idx = 0; char_idx < range->num_chars; char_idx++) - { - zr_rune codepoint = 0; - float dummy_x = 0, dummy_y = 0; - stbtt_aligned_quad q; - struct zr_font_glyph *glyph; - - /* query glyph bounds from stb_truetype */ - const stbtt_packedchar *pc = &range->chardata_for_range[char_idx]; - glyph_count++; - if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue; - codepoint = (zr_rune)(range->first_unicode_codepoint_in_range + char_idx); - stbtt_GetPackedQuad(range->chardata_for_range, (int)width, - (int)height, char_idx, &dummy_x, &dummy_y, &q, 0); - - /* fill own glyph type with data */ - glyph = &glyphs[dst_font->glyph_offset + (unsigned int)char_idx]; - glyph->codepoint = codepoint; - glyph->x0 = q.x0; glyph->y0 = q.y0; - glyph->x1 = q.x1; glyph->y1 = q.y1; - glyph->y0 += (dst_font->ascent + 0.5f); - glyph->y1 += (dst_font->ascent + 0.5f); - glyph->w = glyph->x1 - glyph->x0 + 0.5f; - glyph->h = glyph->y1 - glyph->y0; - - if (cfg->coord_type == ZR_COORD_PIXEL) { - glyph->u0 = q.s0 * (float)width; - glyph->v0 = q.t0 * (float)height; - glyph->u1 = q.s1 * (float)width; - glyph->v1 = q.t1 * (float)height; - } else { - glyph->u0 = q.s0; - glyph->v0 = q.t0; - glyph->u1 = q.s1; - glyph->v1 = q.t1; - } - glyph->xadvance = (pc->xadvance + cfg->spacing.x); - if (cfg->pixel_snap) - glyph->xadvance = (float)(int)(glyph->xadvance + 0.5f); - } - } - dst_font->glyph_count = glyph_count; - glyph_n += dst_font->glyph_count; - } -} - -void -zr_font_bake_custom_data(void *img_memory, int img_width, int img_height, - struct zr_recti img_dst, const char *texture_data_mask, int tex_width, - int tex_height, char white, char black) -{ - zr_byte *pixels; - int y = 0; - int x = 0; - int n = 0; - - ZR_ASSERT(img_memory); - ZR_ASSERT(img_width); - ZR_ASSERT(img_height); - ZR_ASSERT(texture_data_mask); - ZR_UNUSED(tex_height); - if (!img_memory || !img_width || !img_height || !texture_data_mask) - return; - - pixels = (zr_byte*)img_memory; - for (y = 0, n = 0; y < tex_height; ++y) { - for (x = 0; x < tex_width; ++x, ++n) { - const int off0 = ((img_dst.x + x) + (img_dst.y + y) * img_width); - const int off1 = off0 + 1 + tex_width; - pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00; - pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00; - } - } -} - -void -zr_font_bake_convert(void *out_memory, int img_width, int img_height, - const void *in_memory) -{ - int n = 0; - const zr_byte *src; - zr_rune *dst; - - ZR_ASSERT(out_memory); - ZR_ASSERT(in_memory); - ZR_ASSERT(img_width); - ZR_ASSERT(img_height); - if (!out_memory || !in_memory || !img_height || !img_width) return; - - dst = (zr_rune*)out_memory; - src = (const zr_byte*)in_memory; - for (n = (int)(img_width * img_height); n > 0; n--) - *dst++ = ((zr_rune)(*src++) << 24) | 0x00FFFFFF; -} - -/* ------------------------------------------------------------- - * - * FONT - * - * --------------------------------------------------------------*/ -static zr_size -zr_font_text_width(zr_handle handle, float height, const char *text, zr_size len) -{ - zr_rune unicode; - zr_size text_len = 0; - float text_width = 0; - zr_size glyph_len = 0; - float scale = 0; - - struct zr_font *font = (struct zr_font*)handle.ptr; - ZR_ASSERT(font); - ZR_ASSERT(font->glyphs); - if (!font || !text || !len) - return 0; - - scale = height/font->info.height; - glyph_len = text_len = zr_utf_decode(text, &unicode, len); - if (!glyph_len) return 0; - while (text_len <= len && glyph_len) { - const struct zr_font_glyph *g; - if (unicode == ZR_UTF_INVALID) break; - - /* query currently drawn glyph information */ - g = zr_font_find_glyph(font, unicode); - text_width += g->xadvance * scale; - - /* offset next glyph */ - glyph_len = zr_utf_decode(text + text_len, &unicode, len - text_len); - text_len += glyph_len; - } - return (zr_size)text_width; -} - -#if ZR_COMPILE_WITH_VERTEX_BUFFER -static void -zr_font_query_font_glyph(zr_handle handle, float height, - struct zr_user_font_glyph *glyph, zr_rune codepoint, zr_rune next_codepoint) -{ - float scale; - const struct zr_font_glyph *g; - struct zr_font *font; - - ZR_ASSERT(glyph); - ZR_UNUSED(next_codepoint); - - font = (struct zr_font*)handle.ptr; - ZR_ASSERT(font); - ZR_ASSERT(font->glyphs); - if (!font || !glyph) - return; - - scale = height/font->info.height; - g = zr_font_find_glyph(font, codepoint); - glyph->width = (g->x1 - g->x0) * scale; - glyph->height = (g->y1 - g->y0) * scale; - glyph->offset = zr_vec2(g->x0 * scale, g->y0 * scale); - glyph->xadvance = (g->xadvance * scale); - glyph->uv[0] = zr_vec2(g->u0, g->v0); - glyph->uv[1] = zr_vec2(g->u1, g->v1); -} -#endif - -const struct zr_font_glyph* -zr_font_find_glyph(struct zr_font *font, zr_rune unicode) -{ - int i = 0; - int count; - int total_glyphs = 0; - const struct zr_font_glyph *glyph = 0; - ZR_ASSERT(font); - ZR_ASSERT(font->glyphs); - - glyph = font->fallback; - count = zr_range_count(font->info.ranges); - for (i = 0; i < count; ++i) { - int diff; - zr_rune f = font->info.ranges[(i*2)+0]; - zr_rune t = font->info.ranges[(i*2)+1]; - diff = (int)((t - f) + 1); - if (unicode >= f && unicode <= t) - return &font->glyphs[((zr_rune)total_glyphs + (unicode - f))]; - total_glyphs += diff; - } - return glyph; -} - -void -zr_font_init(struct zr_font *font, float pixel_height, - zr_rune fallback_codepoint, struct zr_font_glyph *glyphs, - const struct zr_baked_font *baked_font, zr_handle atlas) -{ - struct zr_baked_font baked; - ZR_ASSERT(font); - ZR_ASSERT(glyphs); - ZR_ASSERT(baked_font); - if (!font || !glyphs || !baked_font) - return; - - baked = *baked_font; - zr_zero(font, sizeof(*font)); - font->info = baked; - font->scale = (float)pixel_height / (float)font->info.height; - font->glyphs = &glyphs[baked_font->glyph_offset]; - font->texture = atlas; - font->fallback_codepoint = fallback_codepoint; - font->fallback = zr_font_find_glyph(font, fallback_codepoint); - - font->handle.height = font->info.height * font->scale; - font->handle.width = zr_font_text_width; - font->handle.userdata.ptr = font; -#if ZR_COMPILE_WITH_VERTEX_BUFFER - font->handle.query = zr_font_query_font_glyph; - font->handle.texture = font->texture; -#endif -} - -/* --------------------------------------------------------------------------- - * - * DEFAULT FONT - * - * ProggyClean.ttf - * Copyright (c) 2004, 2005 Tristan Grimmer - * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) - * Download and more information at http://upperbounds.net - *-----------------------------------------------------------------------------*/ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#pragma clang diagnostic ignored "-Wfloat-equal" -#pragma clang diagnostic ignored "-Wbad-function-cast" -#pragma clang diagnostic ignored "-Wcast-qual" -#pragma clang diagnostic ignored "-Wshadow" -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#pragma clang diagnostic ignored "-Wdeclaration-after-statement" -#pragma clang diagnostic ignored "-Wunused-function" -#pragma clang diagnostic ignored "-Woverlength-strings" -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wbad-function-cast" -#pragma GCC diagnostic ignored "-Wcast-qual" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" -#pragma GCC diagnostic ignored "-Wtype-limits" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wunused-function" -#pragma GCC diagnostic ignored "-Woverlength-strings" -#elif _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4456) -#endif - -#if ZR_COMPILE_WITH_DEFAULT_FONT -static const char zr_proggy_clean_ttf_compressed_data_base85[11980+1] = - "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" - "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" - "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." - "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" - "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" - "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" - "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" - "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" - "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" - "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" - "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" - "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" - "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" - "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" - "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" - "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" - "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" - "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" - "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" - "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" - "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" - ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" - "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" - "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" - "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" - "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" - "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" - "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" - "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" - "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" - "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" - "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" - "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" - "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" - "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" - "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" - "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" - ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" - "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" - "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" - "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" - "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" - "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" - "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" - ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" - "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" - "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" - "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" - "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" - "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; -#endif - -static unsigned int -stb_decompress_length(unsigned char *input) -{ - return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; -} - -static unsigned char *stb__barrier; -static unsigned char *stb__barrier2; -static unsigned char *stb__barrier3; -static unsigned char *stb__barrier4; -static unsigned char *stb__dout; - -static void -stb__match(unsigned char *data, unsigned int length) -{ - /* INVERSE of memmove... write each byte before copying the next...*/ - ZR_ASSERT (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } - while (length--) *stb__dout++ = *data++; -} - -static void -stb__lit(unsigned char *data, unsigned int length) -{ - ZR_ASSERT (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } - zr_memcopy(stb__dout, data, length); - stb__dout += length; -} - -#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) -#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) -#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) - -static unsigned char* -stb_decompress_token(unsigned char *i) -{ - if (*i >= 0x20) { /* use fewer if's for cases that expand small */ - if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; - else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; - else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); - } else { /* more ifs for cases that expand large, since overhead is amortized */ - if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; - else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; - else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); - else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); - else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; - else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; - } - return i; -} - -static unsigned int -stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) -{ - const unsigned long ADLER_MOD = 65521; - unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; - unsigned long blocklen, i; - - blocklen = buflen % 5552; - while (buflen) { - for (i=0; i + 7 < blocklen; i += 8) { - s1 += buffer[0], s2 += s1; - s1 += buffer[1], s2 += s1; - s1 += buffer[2], s2 += s1; - s1 += buffer[3], s2 += s1; - s1 += buffer[4], s2 += s1; - s1 += buffer[5], s2 += s1; - s1 += buffer[6], s2 += s1; - s1 += buffer[7], s2 += s1; - - buffer += 8; - } - - for (; i < blocklen; ++i) - s1 += *buffer++, s2 += s1; - - s1 %= ADLER_MOD, s2 %= ADLER_MOD; - buflen -= blocklen; - blocklen = 5552; - } - return (unsigned int)(s2 << 16) + (unsigned int)s1; -} - -static unsigned int -stb_decompress(unsigned char *output, unsigned char *i, unsigned int length) -{ - unsigned int olen; - if (stb__in4(0) != 0x57bC0000) return 0; - if (stb__in4(4) != 0) return 0; /* error! stream is > 4GB */ - olen = stb_decompress_length(i); - stb__barrier2 = i; - stb__barrier3 = i+length; - stb__barrier = output + olen; - stb__barrier4 = output; - i += 16; - - stb__dout = output; - for (;;) { - unsigned char *old_i = i; - i = stb_decompress_token(i); - if (i == old_i) { - if (*i == 0x05 && i[1] == 0xfa) { - ZR_ASSERT(stb__dout == output + olen); - if (stb__dout != output + olen) return 0; - if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2)) - return 0; - return olen; - } else { - ZR_ASSERT(0); /* NOTREACHED */ - return 0; - } - } - ZR_ASSERT(stb__dout <= output + olen); - if (stb__dout > output + olen) - return 0; - } -} - -static unsigned int -zr_decode_85_byte(char c) -{ return (c >= '\\') ? c-36 : c-35; } - -static void -zr_decode_85(unsigned char* dst, const unsigned char* src) -{ - while (*src) - { - unsigned int tmp = zr_decode_85_byte(src[0]) + - 85*(zr_decode_85_byte(src[1]) + - 85*(zr_decode_85_byte(src[2]) + - 85*(zr_decode_85_byte(src[3]) + - 85*zr_decode_85_byte(src[4])))); - - /* we can't assume little-endianess. */ - dst[0] = ((tmp >> 0) & 0xFF); - dst[1] = ((tmp >> 8) & 0xFF); - dst[2] = ((tmp >> 16) & 0xFF); - dst[3] = ((tmp >> 24) & 0xFF); - - src += 5; - dst += 4; - } -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic pop -#elif _MSC_VER -#pragma warning (pop) -#endif - -/* ------------------------------------------------------------- - * - * FONT ATLAS - * - * --------------------------------------------------------------*/ -struct zr_font_config -zr_font_config(float pixel_height) -{ - struct zr_font_config cfg; - zr_zero_struct(cfg); - cfg.ttf_blob = 0; - cfg.ttf_size = 0; - cfg.ttf_data_owned_by_atlas = 0; - cfg.size = pixel_height; - cfg.oversample_h = 1; - cfg.oversample_v = 1; - cfg.pixel_snap = 0; - cfg.coord_type = ZR_COORD_UV; - cfg.spacing = zr_vec2(0,0); - cfg.range = zr_font_default_glyph_ranges(); - cfg.fallback_glyph = '?'; - cfg.font = 0; - cfg.merge_mode = 0; - return cfg; -} - -#if ZR_COMPILE_WITH_DEFAULT_ALLOCATOR -void -zr_font_atlas_init_default(struct zr_font_atlas *atlas) -{ - ZR_ASSERT(atlas); - if (!atlas) return; - zr_zero_struct(*atlas); - atlas->alloc.userdata.ptr = 0; - atlas->alloc.alloc = zr_malloc; - atlas->alloc.free = zr_mfree; -} -#endif - -void -zr_font_atlas_init(struct zr_font_atlas *atlas, struct zr_allocator *alloc) -{ - ZR_ASSERT(atlas); - ZR_ASSERT(alloc); - if (!atlas || !alloc) return; - zr_zero_struct(*atlas); - atlas->alloc = *alloc; -} - -void -zr_font_atlas_begin(struct zr_font_atlas *atlas) -{ - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc && atlas->alloc.free); - if (!atlas || !atlas->alloc.alloc || !atlas->alloc.free) return; - if (atlas->glyphes) { - atlas->alloc.free(atlas->alloc.userdata, atlas->glyphes); - atlas->glyphes = 0; - } - if (atlas->pixel) { - atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); - atlas->pixel = 0; - } -} - -struct zr_font* -zr_font_atlas_add(struct zr_font_atlas *atlas, const struct zr_font_config *config) -{ - struct zr_font *font = 0; - ZR_ASSERT(atlas); - ZR_ASSERT(config); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - ZR_ASSERT(config->ttf_blob); - ZR_ASSERT(config->ttf_size); - ZR_ASSERT(config->size > 0.0f); - if (!atlas || !config || !config->ttf_blob || !config->ttf_size || config->size <= 0.0f|| - !atlas->alloc.alloc || !atlas->alloc.free) - return 0; - - /* allocate new font */ - if (!config->merge_mode) { - font = (struct zr_font*)atlas->alloc.alloc(atlas->alloc.userdata, sizeof(struct zr_font)); - ZR_ASSERT(font); - if (!font) return 0; - } else { - ZR_ASSERT(atlas->font_num); - font = atlas->fonts[atlas->font_num-1]; - } - - /* make sure enough memory */ - if (!atlas->config || atlas->font_num >= atlas->font_cap) { - void *tmp_font, *tmp_config; - atlas->font_cap = (!atlas->config) ? 32: (int)((float)atlas->font_cap * 2.7f); - tmp_font = atlas->alloc.alloc(atlas->alloc.userdata, ((zr_size)atlas->font_cap * sizeof(struct zr_font*))); - tmp_config = atlas->alloc.alloc(atlas->alloc.userdata, ((zr_size)atlas->font_cap * sizeof(struct zr_font_config))); - if (!atlas->config) { - atlas->fonts = (struct zr_font**)tmp_font; - atlas->config = (struct zr_font_config*)tmp_config; - } else { - /* realloc */ - zr_memcopy(tmp_font, atlas->fonts, sizeof(struct zr_font*) * (zr_size)atlas->font_num); - zr_memcopy(tmp_config, atlas->config, sizeof(struct zr_font_config) * (zr_size)atlas->font_num); - - atlas->alloc.free(atlas->alloc.userdata, atlas->fonts); - atlas->alloc.free(atlas->alloc.userdata, atlas->config); - - atlas->fonts = (struct zr_font**)tmp_font; - atlas->config = (struct zr_font_config*)tmp_config; - } - } - - /* add font and font config into atlas */ - atlas->config[atlas->font_num] = *config; - if (!config->merge_mode) { - atlas->fonts[atlas->font_num] = font; - atlas->config[atlas->font_num].font = &font->info; - } - - /* create own copy of .TTF font blob */ - if (!config->ttf_data_owned_by_atlas) { - struct zr_font_config *c = &atlas->config[atlas->font_num]; - c->ttf_blob = atlas->alloc.alloc(atlas->alloc.userdata, c->ttf_size); - ZR_ASSERT(c->ttf_blob); - if (!c->ttf_blob) { - atlas->font_num++; - return 0; - } - zr_memcopy(c->ttf_blob, config->ttf_blob, c->ttf_size); - c->ttf_data_owned_by_atlas = 1; - } - atlas->font_num++; - return font; -} - -struct zr_font* -zr_font_atlas_add_from_memory(struct zr_font_atlas *atlas, void *memory, - zr_size size, float height, const struct zr_font_config *config) -{ - struct zr_font_config cfg; - ZR_ASSERT(memory); - ZR_ASSERT(size); - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - if (!atlas || !atlas->alloc.alloc || !atlas->alloc.free || !memory || !size) - return 0; - - cfg = (config) ? *config: zr_font_config(height); - cfg.ttf_blob = memory; - cfg.ttf_size = size; - cfg.size = height; - cfg.ttf_data_owned_by_atlas = 0; - return zr_font_atlas_add(atlas, &cfg); -} - -#if ZR_COMPILE_WITH_STANDARD_IO -struct zr_font* -zr_font_atlas_add_from_file(struct zr_font_atlas *atlas, const char *file_path, - float height, const struct zr_font_config *config) -{ - zr_size size; - char *memory; - struct zr_font_config cfg; - - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - if (!atlas || !file_path) return 0; - memory = zr_file_load(file_path, &size, &atlas->alloc); - if (!memory) return 0; - - cfg = (config) ? *config: zr_font_config(height); - cfg.ttf_blob = memory; - cfg.ttf_size = size; - cfg.size = height; - cfg.ttf_data_owned_by_atlas = 1; - return zr_font_atlas_add(atlas, &cfg); -} -#endif - -struct zr_font* -zr_font_atlas_add_compressed(struct zr_font_atlas *atlas, - void *compressed_data, zr_size compressed_size, float height, - const struct zr_font_config *config) -{ - unsigned int decompressed_size; - void *decompressed_data; - struct zr_font_config cfg; - - ZR_ASSERT(compressed_data); - ZR_ASSERT(compressed_size); - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - if (!atlas || !compressed_data || !atlas->alloc.alloc || !atlas->alloc.free) - return 0; - - decompressed_size = stb_decompress_length((unsigned char*)compressed_data); - decompressed_data = atlas->alloc.alloc(atlas->alloc.userdata,decompressed_size); - ZR_ASSERT(decompressed_data); - if (!decompressed_data) return 0; - stb_decompress((unsigned char*)decompressed_data, (unsigned char*)compressed_data, - (unsigned int)compressed_size); - - cfg = (config) ? *config: zr_font_config(height); - cfg.ttf_blob = decompressed_data; - cfg.ttf_size = decompressed_size; - cfg.size = height; - cfg.ttf_data_owned_by_atlas = 1; - return zr_font_atlas_add(atlas, &cfg); -} - -struct zr_font* -zr_font_atlas_add_compressed_base85(struct zr_font_atlas *atlas, - const char *data_base85, float height, const struct zr_font_config *config) -{ - int compressed_size; - void *compressed_data; - struct zr_font *font; - - ZR_ASSERT(data_base85); - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - if (!atlas || !data_base85 || !atlas->alloc.alloc || !atlas->alloc.free) - return 0; - - compressed_size = (((int)zr_strlen(data_base85) + 4) / 5) * 4; - compressed_data = atlas->alloc.alloc(atlas->alloc.userdata, (zr_size)compressed_size); - ZR_ASSERT(compressed_data); - if (!compressed_data) return 0; - zr_decode_85((unsigned char*)compressed_data, (const unsigned char*)data_base85); - font = zr_font_atlas_add_compressed(atlas, compressed_data, - (zr_size)compressed_size, height, config); - atlas->alloc.free(atlas->alloc.userdata, compressed_data); - return font; -} - -#if ZR_COMPILE_WITH_DEFAULT_FONT -struct zr_font* -zr_font_atlas_add_default(struct zr_font_atlas *atlas, - float pixel_height, const struct zr_font_config *config) -{ - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - return zr_font_atlas_add_compressed_base85(atlas, - zr_proggy_clean_ttf_compressed_data_base85, pixel_height, config); -} -#endif - -const void* -zr_font_atlas_bake(struct zr_font_atlas *atlas, int *width, int *height, - enum zr_font_atlas_format fmt) -{ - int i = 0; - void *tmp = 0; - const char *custom_data = "...."; - zr_size tmp_size, img_size; - - ZR_ASSERT(width); - ZR_ASSERT(height); - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - if (!atlas || !width || !height || !atlas->alloc.alloc || !atlas->alloc.free) - return 0; - -#if ZR_COMPILE_WITH_DEFAULT_FONT - /* no font added so just use default font */ - if (!atlas->font_num) - zr_font_atlas_add_default(atlas, 14.0f, 0); -#endif - if (!atlas->font_num) return 0; - - /* allocate temporary memory required for the baking process */ - zr_font_bake_memory(&tmp_size, &atlas->glyph_count, atlas->config, atlas->font_num); - tmp = atlas->alloc.alloc(atlas->alloc.userdata, tmp_size); - ZR_ASSERT(tmp); - if (!tmp) goto failed; - - /* allocate memory glyphes for all fonts */ - atlas->glyphes = (struct zr_font_glyph*) - atlas->alloc.alloc(atlas->alloc.userdata, sizeof(struct zr_font_glyph) * (size_t)atlas->glyph_count); - ZR_ASSERT(atlas->glyphes); - if (!atlas->glyphes) - goto failed; - - /* pack all glyphes into a tight fit space */ - atlas->custom.w = 2; atlas->custom.h = 2; - if (!zr_font_bake_pack(&img_size, width, height, &atlas->custom, tmp, tmp_size, - atlas->config, atlas->font_num)) - goto failed; - - /* allocate memory for the baked image font atlas */ - atlas->pixel = atlas->alloc.alloc(atlas->alloc.userdata, img_size); - ZR_ASSERT(atlas->pixel); - if (!atlas->pixel) - goto failed; - - /* bake glyphes and custom white pixel into image */ - zr_font_bake(atlas->pixel, *width, *height, tmp, tmp_size, atlas->glyphes, - atlas->glyph_count, atlas->config, atlas->font_num); - zr_font_bake_custom_data(atlas->pixel, *width, *height, atlas->custom, - custom_data, 2, 2, '.', 'X'); - - /* convert alpha8 image into rgba32 image */ - if (fmt == ZR_FONT_ATLAS_RGBA32) { - void *img_rgba = atlas->alloc.alloc(atlas->alloc.userdata, (zr_size)(*width * *height * 4)); - ZR_ASSERT(img_rgba); - if (!img_rgba) goto failed; - zr_font_bake_convert(img_rgba, *width, *height, atlas->pixel); - atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); - atlas->pixel = img_rgba; - } - atlas->tex_width = *width; - atlas->tex_height = *height; - - /* initialize each font */ - for (i = 0; i < atlas->font_num; ++i) { - zr_font_init(atlas->fonts[i], atlas->config[i].size, - atlas->config[i].fallback_glyph, atlas->glyphes, - atlas->config[i].font, zr_handle_ptr(0)); - } - - /* free temporary memory */ - atlas->alloc.free(atlas->alloc.userdata, tmp); - return atlas->pixel; - -failed: - /* error so cleanup all memory */ - if (tmp) atlas->alloc.free(atlas->alloc.userdata, tmp); - if (atlas->glyphes) { - atlas->alloc.free(atlas->alloc.userdata, atlas->glyphes); - atlas->glyphes = 0; - } - if (atlas->pixel) { - atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); - atlas->pixel = 0; - } - return 0; -} - -void -zr_font_atlas_end(struct zr_font_atlas *atlas, zr_handle texture, - struct zr_draw_null_texture *null) -{ - int i = 0; - ZR_ASSERT(atlas); - if (!atlas) { - if (!null) return; - null->texture = texture; - null->uv = zr_vec2(0.5f,0.5f); - } - if (null) { - null->texture = texture; - null->uv = zr_vec2((atlas->custom.x + 0.5f)/(float)atlas->tex_width, - (atlas->custom.y + 0.5f)/(float)atlas->tex_height); - } - for (i = 0; i < atlas->font_num; ++i) { - atlas->fonts[i]->texture = texture; - atlas->fonts[i]->handle.texture = texture; - } - - atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); - atlas->pixel = 0; - atlas->tex_width = 0; - atlas->tex_height = 0; - atlas->custom.x = 0; - atlas->custom.y = 0; - atlas->custom.w = 0; - atlas->custom.h = 0; -} - -void -zr_font_atlas_clear(struct zr_font_atlas *atlas) -{ - int i = 0; - ZR_ASSERT(atlas); - ZR_ASSERT(atlas->alloc.alloc); - ZR_ASSERT(atlas->alloc.free); - if (!atlas || !atlas->alloc.alloc || !atlas->alloc.free) - return; - - if (atlas->fonts) { - for (i = 0; i < atlas->font_num; ++i) - atlas->alloc.free(atlas->alloc.userdata, atlas->fonts[i]); - atlas->alloc.free(atlas->alloc.userdata, atlas->fonts); - } - if (atlas->config) { - for (i = 0; i < atlas->font_num; ++i) - atlas->alloc.free(atlas->alloc.userdata, atlas->config[i].ttf_blob); - atlas->alloc.free(atlas->alloc.userdata, atlas->config); - } - if (atlas->glyphes) - atlas->alloc.free(atlas->alloc.userdata, atlas->glyphes); - if (atlas->pixel) - atlas->alloc.free(atlas->alloc.userdata, atlas->pixel); - zr_zero_struct(*atlas); -} -#endif -/* - * ============================================================== - * - * Edit Box - * - * =============================================================== - */ -static void -zr_edit_buffer_append(struct zr_buffer *buffer, const char *str, zr_size len) -{ - char *mem; - ZR_ASSERT(buffer); - ZR_ASSERT(str); - if (!buffer || !str || !len) return; - mem = (char*)zr_buffer_alloc(buffer, ZR_BUFFER_FRONT, len * sizeof(char), 0); - if (!mem) return; - zr_memcopy(mem, str, len * sizeof(char)); -} - -static int -zr_edit_buffer_insert(struct zr_buffer *buffer, zr_size pos, - const char *str, zr_size len) -{ - zr_size i; - void *mem; - char *src; - char *dst; - - zr_size copylen; - ZR_ASSERT(buffer); - if (!buffer || !str || !len || pos > buffer->allocated) return 0; - if ((buffer->allocated + len >= buffer->memory.size) && - (buffer->type == ZR_BUFFER_FIXED)) return 0; - - copylen = buffer->allocated - pos; - if (!copylen) { - zr_edit_buffer_append(buffer, str, len); - return 1; - } - mem = zr_buffer_alloc(buffer, ZR_BUFFER_FRONT, len * sizeof(char), 0); - if (!mem) return 0; - - /* memmove */ - ZR_ASSERT(((int)pos + (int)len + ((int)copylen - 1)) >= 0); - ZR_ASSERT(((int)pos + ((int)copylen - 1)) >= 0); - dst = zr_ptr_add(char, buffer->memory.ptr, pos + len + (copylen - 1)); - src = zr_ptr_add(char, buffer->memory.ptr, pos + (copylen-1)); - for (i = 0; i < copylen; ++i) *dst-- = *src--; - mem = zr_ptr_add(void, buffer->memory.ptr, pos); - zr_memcopy(mem, str, len * sizeof(char)); - return 1; -} - -static void -zr_edit_buffer_remove(struct zr_buffer *buffer, zr_size len) -{ - ZR_ASSERT(buffer); - if (!buffer || len > buffer->allocated) return; - ZR_ASSERT(((int)buffer->allocated - (int)len) >= 0); - buffer->allocated -= len; -} - -static void -zr_edit_buffer_del(struct zr_buffer *buffer, zr_size pos, zr_size len) -{ - ZR_ASSERT(buffer); - if (!buffer || !len || pos > buffer->allocated || - pos + len > buffer->allocated) return; - - if (pos + len < buffer->allocated) { - /* memmove */ - char *dst = zr_ptr_add(char, buffer->memory.ptr, pos); - char *src = zr_ptr_add(char, buffer->memory.ptr, pos + len); - zr_memcopy(dst, src, buffer->allocated - (pos + len)); - ZR_ASSERT(((int)buffer->allocated - (int)len) >= 0); - buffer->allocated -= len; - } else zr_edit_buffer_remove(buffer, len); -} - -static char* -zr_edit_buffer_at_char(struct zr_buffer *buffer, zr_size pos) -{ - ZR_ASSERT(buffer); - if (!buffer || pos > buffer->allocated) return 0; - return zr_ptr_add(char, buffer->memory.ptr, pos); -} - -static char* -zr_edit_buffer_at(struct zr_buffer *buffer, int pos, zr_rune *unicode, - zr_size *len) -{ - int i = 0; - zr_size src_len = 0; - zr_size glyph_len = 0; - char *text; - zr_size text_len; - - ZR_ASSERT(buffer); - ZR_ASSERT(unicode); - ZR_ASSERT(len); - if (!buffer || !unicode || !len) return 0; - if (pos < 0) { - *unicode = 0; - *len = 0; - return 0; - } - - text = (char*)buffer->memory.ptr; - text_len = buffer->allocated; - glyph_len = zr_utf_decode(text, unicode, text_len); - while (glyph_len) { - if (i == pos) { - *len = glyph_len; - break; - } - - i++; - src_len = src_len + glyph_len; - glyph_len = zr_utf_decode(text + src_len, unicode, text_len - src_len); - } - if (i != pos) return 0; - return text + src_len; -} - -static void -zr_edit_box_init_buffer(struct zr_edit_box *eb, struct zr_buffer *buffer, - const struct zr_clipboard *clip, zr_filter f) -{ - ZR_ASSERT(eb); - if (!eb) return; - zr_zero(eb, sizeof(*eb)); - eb->buffer = *buffer; - if (clip) eb->clip = *clip; - if (f) eb->filter = f; - else eb->filter = zr_filter_default; - eb->cursor = 0; - eb->glyphs = 0; -} - -static void -zr_edit_box_init(struct zr_edit_box *eb, void *memory, zr_size size, - const struct zr_clipboard *clip, zr_filter f) -{ - ZR_ASSERT(eb); - if (!eb) return; - zr_zero(eb, sizeof(*eb)); - zr_buffer_init_fixed(&eb->buffer, memory, size); - if (clip) eb->clip = *clip; - if (f) eb->filter = f; - else eb->filter = zr_filter_default; - eb->cursor = 0; - eb->glyphs = 0; -} - -void -zr_edit_box_clear(struct zr_edit_box *box) -{ - ZR_ASSERT(box); - if (!box) return; - zr_buffer_clear(&box->buffer); - box->cursor = box->glyphs = 0; -} - -void -zr_edit_box_add(struct zr_edit_box *eb, const char *str, zr_size len) -{ - int res = 0; - ZR_ASSERT(eb); - if (!eb || !str || !len) return; - - if (eb->cursor != eb->glyphs) { - zr_size l = 0; - zr_rune unicode; - int cursor = (eb->cursor) ? (int)(eb->cursor) : 0; - char *sym = zr_edit_buffer_at(&eb->buffer, cursor, &unicode, &l); - zr_size offset = (zr_size)(sym - (char*)eb->buffer.memory.ptr); - res = zr_edit_buffer_insert(&eb->buffer, offset, str, len); - } else res = zr_edit_buffer_insert(&eb->buffer, eb->buffer.allocated, str, len); - - if (res) { - zr_size l = zr_utf_len(str, len); - eb->glyphs += l; - eb->cursor += l; - eb->text_inserted = 1; - } -} - -static zr_size -zr_edit_box_buffer_input(struct zr_edit_box *box, const struct zr_input *i) -{ - zr_rune unicode; - zr_size src_len = 0; - zr_size text_len = 0; - zr_size glyph_len = 0; - zr_size glyphs = 0; - - ZR_ASSERT(box); - ZR_ASSERT(i); - if (!box || !i) return 0; - - /* add user provided text to buffer until either no input or buffer space left*/ - glyph_len = zr_utf_decode(i->keyboard.text, &unicode, i->keyboard.text_len); - while (glyph_len && ((text_len+glyph_len) <= i->keyboard.text_len)) { - /* filter to make sure the value is correct */ - if (box->filter(box, unicode)) { - zr_edit_box_add(box, &i->keyboard.text[text_len], glyph_len); - text_len += glyph_len; - glyphs++; - } - src_len = src_len + glyph_len; - glyph_len = zr_utf_decode(i->keyboard.text + src_len, &unicode, - i->keyboard.text_len - src_len); - } - return glyphs; -} - -void -zr_edit_box_remove(struct zr_edit_box *box, enum zr_edit_remove_operation op) -{ - char *buf; - zr_size len; - zr_size min; - zr_size maxi; - zr_size diff; - - ZR_ASSERT(box); - if (!box) return; - if (!box->glyphs) return; - - buf = (char*)box->buffer.memory.ptr; - min = ZR_MIN(box->sel.end, box->sel.begin); - maxi = ZR_MAX(box->sel.end, box->sel.begin); - diff = ZR_MAX(1, maxi - min); - - if (diff && box->cursor != box->glyphs) { - zr_size off; - zr_rune unicode; - char *begin; - char *end; - - /* calculate text selection byte position and size */ - begin = zr_edit_buffer_at(&box->buffer, (int)min, &unicode, &len); - end = zr_edit_buffer_at(&box->buffer, (int)maxi, &unicode, &len); - len = ZR_MAX((zr_size)(end - begin), 1); - off = (zr_size)(begin - buf); - - /* delete text selection */ - if (len > 1) { - zr_edit_buffer_del(&box->buffer, off, len); - } else { - if (op == ZR_DELETE) { - zr_edit_buffer_del(&box->buffer, off, len); - } else { - zr_edit_buffer_del(&box->buffer, off-1, len); - box->cursor = (box->cursor) ? box->cursor-1: 0; - box->sel.begin = box->cursor; - box->sel.end = box->cursor; - } - } - box->glyphs = zr_utf_len(buf, box->buffer.allocated); - } else if (op == ZR_REMOVE) { - zr_rune unicode; - int cursor; - char *glyph; - zr_size offset; - - /* remove last glyph */ - cursor = (int)box->cursor - 1; - glyph = zr_edit_buffer_at(&box->buffer, cursor, &unicode, &len); - if (!glyph || !len) return; - - offset = (zr_size)(glyph - (char*)box->buffer.memory.ptr); - zr_edit_buffer_del(&box->buffer, offset, len); - box->glyphs--; - } - if (op == ZR_DELETE) { - if (box->cursor >= box->glyphs) - box->cursor = box->glyphs; - else if (box->cursor > 0) - box->cursor--; - } -} - -int -zr_edit_box_has_selection(const struct zr_edit_box *eb) -{ - ZR_ASSERT(eb); - if (!eb) return 0; - return (int)(eb->sel.end - eb->sel.begin); -} - -const char* -zr_edit_box_get_selection(zr_size *len, struct zr_edit_box *eb) -{ - zr_size l; - const char *begin; - zr_rune unicode; - ZR_ASSERT(eb); - ZR_ASSERT(len); - if (!eb || !len || !(eb->sel.end - eb->sel.begin)) - return 0; - - begin = zr_edit_buffer_at(&eb->buffer, (int)eb->sel.begin, &unicode, &l); - *len = (eb->sel.end - eb->sel.begin) + 1; - return begin; -} - -char* -zr_edit_box_get(struct zr_edit_box *eb) -{ - ZR_ASSERT(eb); - if (!eb) return 0; - return (char*)eb->buffer.memory.ptr; -} - -const char* -zr_edit_box_get_const(const struct zr_edit_box *eb) -{ - ZR_ASSERT(eb); - if (!eb) return 0; - return (char*)eb->buffer.memory.ptr; -} - -char -zr_edit_box_at_char(struct zr_edit_box *eb, zr_size pos) -{ - char *c; - ZR_ASSERT(eb); - if (!eb || pos >= eb->buffer.allocated) return 0; - c = zr_edit_buffer_at_char(&eb->buffer, pos); - return c ? *c : 0; -} - -void -zr_edit_box_at(struct zr_edit_box *eb, zr_size pos, zr_glyph g, zr_size *len) -{ - char *sym; - zr_rune unicode; - - ZR_ASSERT(eb); - ZR_ASSERT(g); - ZR_ASSERT(len); - - if (!eb || !len || !g) return; - if (pos >= eb->glyphs) { - *len = 0; - return; - } - sym = zr_edit_buffer_at(&eb->buffer, (int)pos, &unicode, len); - if (!sym) return; - zr_memcopy(g, sym, *len); -} - -void -zr_edit_box_at_cursor(struct zr_edit_box *eb, zr_glyph g, zr_size *len) -{ - const char *text; - zr_size text_len; - zr_size glyph_len = 0; - - ZR_ASSERT(eb); - ZR_ASSERT(g); - ZR_ASSERT(len); - - if (!eb || !g || !len) return; - if (!eb->cursor) { - *len = 0; - return; - } - - text = (char*)eb->buffer.memory.ptr; - text_len = eb->buffer.allocated; - glyph_len = zr_utf_len(text, text_len); - zr_edit_box_at(eb, glyph_len, g, len); -} - -void -zr_edit_box_set_cursor(struct zr_edit_box *eb, zr_size pos) -{ - ZR_ASSERT(eb); - ZR_ASSERT(eb->glyphs >= pos); - if (!eb || pos > eb->glyphs) return; - eb->cursor = pos; -} - -zr_size -zr_edit_box_get_cursor(struct zr_edit_box *eb) -{ - ZR_ASSERT(eb); - if (!eb) return 0; - return eb->cursor; -} - -zr_size -zr_edit_box_len_char(struct zr_edit_box *eb) -{ - ZR_ASSERT(eb); - return eb->buffer.allocated; -} - -zr_size -zr_edit_box_len(struct zr_edit_box *eb) -{ - ZR_ASSERT(eb); - return eb->glyphs; -} - -/* ============================================================== - * - * INPUT - * - * ===============================================================*/ -void -zr_input_begin(struct zr_context *ctx) -{ - zr_size i; - struct zr_input *in; - ZR_ASSERT(ctx); - if (!ctx) return; - - in = &ctx->input; - for (i = 0; i < ZR_BUTTON_MAX; ++i) - in->mouse.buttons[i].clicked = 0; - in->keyboard.text_len = 0; - in->mouse.scroll_delta = 0; - zr_vec2_mov(in->mouse.prev, in->mouse.pos); - for (i = 0; i < ZR_KEY_MAX; i++) - in->keyboard.keys[i].clicked = 0; -} - -void -zr_input_motion(struct zr_context *ctx, int x, int y) -{ - struct zr_input *in; - ZR_ASSERT(ctx); - if (!ctx) return; - in = &ctx->input; - in->mouse.pos.x = (float)x; - in->mouse.pos.y = (float)y; -} - -void -zr_input_key(struct zr_context *ctx, enum zr_keys key, int down) -{ - struct zr_input *in; - ZR_ASSERT(ctx); - if (!ctx) return; - in = &ctx->input; - if (in->keyboard.keys[key].down == down) return; - in->keyboard.keys[key].down = down; - in->keyboard.keys[key].clicked++; -} - -void -zr_input_button(struct zr_context *ctx, enum zr_buttons id, int x, int y, int down) -{ - struct zr_mouse_button *btn; - struct zr_input *in; - ZR_ASSERT(ctx); - if (!ctx) return; - in = &ctx->input; - if (in->mouse.buttons[id].down == down) return; - - btn = &in->mouse.buttons[id]; - btn->clicked_pos.x = (float)x; - btn->clicked_pos.y = (float)y; - btn->down = down; - btn->clicked++; -} - -void -zr_input_scroll(struct zr_context *ctx, float y) -{ - ZR_ASSERT(ctx); - if (!ctx) return; - ctx->input.mouse.scroll_delta += y; -} - -void -zr_input_glyph(struct zr_context *ctx, const zr_glyph glyph) -{ - zr_size len = 0; - zr_rune unicode; - - struct zr_input *in; - ZR_ASSERT(ctx); - if (!ctx) return; - in = &ctx->input; - - len = zr_utf_decode(glyph, &unicode, ZR_UTF_SIZE); - if (len && ((in->keyboard.text_len + len) < ZR_INPUT_MAX)) { - zr_utf_encode(unicode, &in->keyboard.text[in->keyboard.text_len], - ZR_INPUT_MAX - in->keyboard.text_len); - in->keyboard.text_len += len; - } -} - -void -zr_input_char(struct zr_context *ctx, char c) -{ - zr_glyph glyph; - ZR_ASSERT(ctx); - if (!ctx) return; - glyph[0] = c; - zr_input_glyph(ctx, glyph); -} - -void -zr_input_unicode(struct zr_context *ctx, zr_rune unicode) -{ - zr_glyph rune; - ZR_ASSERT(ctx); - if (!ctx) return; - zr_utf_encode(unicode, rune, ZR_UTF_SIZE); - zr_input_glyph(ctx, rune); -} - -void -zr_input_end(struct zr_context *ctx) -{ - struct zr_input *in; - ZR_ASSERT(ctx); - if (!ctx) return; - in = &ctx->input; - in->mouse.delta = zr_vec2_sub(in->mouse.pos, in->mouse.prev); -} - -int -zr_input_has_mouse_click_in_rect(const struct zr_input *i, enum zr_buttons id, - struct zr_rect b) -{ - const struct zr_mouse_button *btn; - if (!i) return zr_false; - btn = &i->mouse.buttons[id]; - if (!ZR_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h)) - return zr_false; - return zr_true; -} - -int -zr_input_has_mouse_click_down_in_rect(const struct zr_input *i, enum zr_buttons id, - struct zr_rect b, int down) -{ - const struct zr_mouse_button *btn; - if (!i) return zr_false; - btn = &i->mouse.buttons[id]; - return zr_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down); -} - -int -zr_input_is_mouse_click_in_rect(const struct zr_input *i, enum zr_buttons id, - struct zr_rect b) -{ - const struct zr_mouse_button *btn; - if (!i) return zr_false; - btn = &i->mouse.buttons[id]; - return (zr_input_has_mouse_click_down_in_rect(i, id, b, zr_false) && - btn->clicked) ? zr_true : zr_false; -} - -int -zr_input_is_mouse_click_down_in_rect(const struct zr_input *i, enum zr_buttons id, - struct zr_rect b, int down) -{ - const struct zr_mouse_button *btn; - if (!i) return zr_false; - btn = &i->mouse.buttons[id]; - return (zr_input_has_mouse_click_down_in_rect(i, id, b, down) && - btn->clicked) ? zr_true : zr_false; -} - -int -zr_input_any_mouse_click_in_rect(const struct zr_input *in, struct zr_rect b) -{ - int i, down = 0; - for (i = 0; i < ZR_BUTTON_MAX; ++i) - down = down || zr_input_is_mouse_click_in_rect(in, (enum zr_buttons)i, b); - return down; -} - -int -zr_input_is_mouse_hovering_rect(const struct zr_input *i, struct zr_rect rect) -{ - if (!i) return zr_false; - return ZR_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h); -} - -int -zr_input_is_mouse_prev_hovering_rect(const struct zr_input *i, struct zr_rect rect) -{ - if (!i) return zr_false; - return ZR_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h); -} - -int -zr_input_mouse_clicked(const struct zr_input *i, enum zr_buttons id, struct zr_rect rect) -{ - if (!i) return zr_false; - if (!zr_input_is_mouse_hovering_rect(i, rect)) return zr_false; - return zr_input_is_mouse_click_in_rect(i, id, rect); -} - -int -zr_input_is_mouse_down(const struct zr_input *i, enum zr_buttons id) -{ - if (!i) return zr_false; - return i->mouse.buttons[id].down; -} - -int -zr_input_is_mouse_pressed(const struct zr_input *i, enum zr_buttons id) -{ - const struct zr_mouse_button *b; - if (!i) return zr_false; - b = &i->mouse.buttons[id]; - if (b->down && b->clicked) - return zr_true; - return zr_false; -} - -int -zr_input_is_mouse_released(const struct zr_input *i, enum zr_buttons id) -{ - if (!i) return zr_false; - return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked); -} - -int -zr_input_is_key_pressed(const struct zr_input *i, enum zr_keys key) -{ - const struct zr_key *k; - if (!i) return zr_false; - k = &i->keyboard.keys[key]; - if (k->down && k->clicked) - return zr_true; - return zr_false; -} - -int -zr_input_is_key_released(const struct zr_input *i, enum zr_keys key) -{ - const struct zr_key *k; - if (!i) return zr_false; - k = &i->keyboard.keys[key]; - if (!k->down && k->clicked) - return zr_true; - return zr_false; -} - -int -zr_input_is_key_down(const struct zr_input *i, enum zr_keys key) -{ - const struct zr_key *k; - if (!i) return zr_false; - k = &i->keyboard.keys[key]; - if (k->down) return zr_true; - return zr_false; -} - -/* =============================================================== - * - * TEXT - * - * ===============================================================*/ -struct zr_text { - struct zr_vec2 padding; - struct zr_color background; - struct zr_color text; -}; - -static void -zr_widget_text(struct zr_command_buffer *o, struct zr_rect b, - const char *string, zr_size len, const struct zr_text *t, - zr_flags a, const struct zr_user_font *f) -{ - struct zr_rect label; - zr_size text_width; - - ZR_ASSERT(o); - ZR_ASSERT(t); - if (!o || !t) return; - - b.h = ZR_MAX(b.h, 2 * t->padding.y); - label.x = 0; label.w = 0; - label.y = b.y + t->padding.y; - label.h = b.h - 2 * t->padding.y; - - text_width = f->width(f->userdata, f->height, (const char*)string, len); - text_width += (zr_size)(2 * t->padding.x); - - /* align in x-axis */ - if (a & ZR_TEXT_ALIGN_LEFT) { - label.x = b.x + t->padding.x; - label.w = ZR_MAX(0, b.w - 2 * t->padding.x); - } else if (a & ZR_TEXT_ALIGN_CENTERED) { - label.w = ZR_MAX(1, 2 * t->padding.x + (float)text_width); - label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); - label.x = ZR_MAX(b.x + t->padding.x, label.x); - label.w = ZR_MIN(b.x + b.w, label.x + label.w); - if (label.w >= label.x) label.w -= label.x; - } else if (a & ZR_TEXT_ALIGN_RIGHT) { - label.x = ZR_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); - label.w = (float)text_width + 2 * t->padding.x; - } else return; - - /* align in y-axis */ - if (a & ZR_TEXT_ALIGN_MIDDLE) { - label.y = b.y + b.h/2.0f - (float)f->height/2.0f; - label.h = b.h - (b.h/2.0f + f->height/2.0f); - } else if (a & ZR_TEXT_ALIGN_BOTTOM) { - label.y = b.y + b.h - f->height; - label.h = f->height; - } - zr_draw_text(o, label, (const char*)string, - len, f, t->background, t->text); -} - -static void -zr_widget_text_wrap(struct zr_command_buffer *o, struct zr_rect b, - const char *string, zr_size len, const struct zr_text *t, - const struct zr_user_font *f) -{ - float width; - zr_size glyphs = 0; - zr_size fitting = 0; - zr_size done = 0; - struct zr_rect line; - struct zr_text text; - - ZR_ASSERT(o); - ZR_ASSERT(t); - if (!o || !t) return; - - text.padding = zr_vec2(0,0); - text.background = t->background; - text.text = t->text; - - b.w = ZR_MAX(b.w, 2 * t->padding.x); - b.h = ZR_MAX(b.h, 2 * t->padding.y); - b.h = b.h - 2 * t->padding.y; - - line.x = b.x + t->padding.x; - line.y = b.y + t->padding.y; - line.w = b.w - 2 * t->padding.x; - line.h = 2 * t->padding.y + f->height; - - fitting = zr_use_font_glyph_clamp(f, string, len, line.w, &glyphs, &width); - while (done < len) { - if (!fitting || line.y + line.h >= (b.y + b.h)) break; - zr_widget_text(o, line, &string[done], fitting, &text, ZR_TEXT_LEFT, f); - done += fitting; - line.y += f->height + 2 * t->padding.y; - fitting = zr_use_font_glyph_clamp(f, &string[done], len - done, - line.w, &glyphs, &width); - } -} - -/* =============================================================== - * - * BUTTON - * - * ===============================================================*/ -static void -zr_draw_symbol(struct zr_command_buffer *out, enum zr_symbol_type type, - struct zr_rect content, struct zr_color background, struct zr_color foreground, - float border_width, const struct zr_user_font *font) -{ - switch (type) { - case ZR_SYMBOL_X: - case ZR_SYMBOL_UNDERSCORE: - case ZR_SYMBOL_PLUS: - case ZR_SYMBOL_MINUS: { - /* single character text symbol */ - const char *X = (type == ZR_SYMBOL_X) ? "x": - (type == ZR_SYMBOL_UNDERSCORE) ? "_": - (type == ZR_SYMBOL_PLUS) ? "+": "-"; - struct zr_text text; - text.padding = zr_vec2(0,0); - text.background = background; - text.text = foreground; - zr_widget_text(out, content, X, 1, &text, ZR_TEXT_CENTERED, font); - } break; - case ZR_SYMBOL_CIRCLE: - case ZR_SYMBOL_CIRCLE_FILLED: - case ZR_SYMBOL_RECT: - case ZR_SYMBOL_RECT_FILLED: { - /* simple empty/filled shapes */ - if (type == ZR_SYMBOL_RECT || type == ZR_SYMBOL_RECT_FILLED) { - zr_fill_rect(out, content, 0, foreground); - if (type == ZR_SYMBOL_RECT_FILLED) - zr_fill_rect(out, zr_shrink_rect(content, border_width), 0, background); - } else { - zr_fill_circle(out, content, foreground); - if (type == ZR_SYMBOL_CIRCLE_FILLED) - zr_fill_circle(out, zr_shrink_rect(content, 1), background); - } - } break; - case ZR_SYMBOL_TRIANGLE_UP: - case ZR_SYMBOL_TRIANGLE_DOWN: - case ZR_SYMBOL_TRIANGLE_LEFT: - case ZR_SYMBOL_TRIANGLE_RIGHT: { - enum zr_heading heading; - struct zr_vec2 points[3]; - heading = (type == ZR_SYMBOL_TRIANGLE_RIGHT) ? ZR_RIGHT : - (type == ZR_SYMBOL_TRIANGLE_LEFT) ? ZR_LEFT: - (type == ZR_SYMBOL_TRIANGLE_UP) ? ZR_UP: ZR_DOWN; - zr_triangle_from_direction(points, content, 0, 0, heading); - zr_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y, - points[2].x, points[2].y, foreground); - } break; - default: - case ZR_SYMBOL_NONE: - case ZR_SYMBOL_MAX: break; - } -} - -static int -zr_button_behavior(zr_flags *state, struct zr_rect r, - const struct zr_input *i, enum zr_button_behavior behavior) -{ - int ret = 0; - *state = ZR_WIDGET_STATE_INACTIVE; - if (!i) return 0; - if (zr_input_is_mouse_hovering_rect(i, r)) { - *state = ZR_WIDGET_STATE_HOVERED; - if (zr_input_is_mouse_down(i, ZR_BUTTON_LEFT)) - *state = ZR_WIDGET_STATE_ACTIVE; - if (zr_input_has_mouse_click_in_rect(i, ZR_BUTTON_LEFT, r)) { - ret = (behavior != ZR_BUTTON_DEFAULT) ? - zr_input_is_mouse_down(i, ZR_BUTTON_LEFT): - zr_input_is_mouse_released(i, ZR_BUTTON_LEFT); - } - } - if (*state == ZR_WIDGET_STATE_HOVERED && !zr_input_is_mouse_prev_hovering_rect(i, r)) - *state |= ZR_WIDGET_STATE_ENTERED; - else if (zr_input_is_mouse_prev_hovering_rect(i, r)) - *state |= ZR_WIDGET_STATE_LEFT; - return ret; -} - -static const struct zr_style_item* -zr_draw_button(struct zr_command_buffer *out, - const struct zr_rect *bounds, zr_flags state, - const struct zr_style_button *style) -{ - const struct zr_style_item *background; - if (state & ZR_WIDGET_STATE_HOVERED) - background = &style->hover; - else if (state & ZR_WIDGET_STATE_ACTIVE) - background = &style->active; - else background = &style->normal; - - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, *bounds, &background->data.image); - } else { - zr_fill_rect(out, *bounds, style->rounding, style->border_color); - zr_fill_rect(out, zr_shrink_rect(*bounds, style->border), style->rounding, - background->data.color); - } - return background; -} - -static int -zr_do_button(zr_flags *state, struct zr_command_buffer *out, struct zr_rect r, - const struct zr_style_button *style, const struct zr_input *in, - enum zr_button_behavior behavior, struct zr_rect *content) -{ - struct zr_vec2 pad; - struct zr_rect bounds; - - ZR_ASSERT(style); - ZR_ASSERT(state); - ZR_ASSERT(out); - if (!out || !style) - return zr_false; - - /* calculate button content space */ - pad.x = style->padding.x + style->border; - pad.y = style->padding.y + style->border; - - content->x = r.x + style->padding.x; - content->y = r.y + style->padding.y; - content->w = r.w - 2 * style->padding.x; - content->h = r.h - 2 * style->padding.y; - - /* execute button behavior */ - bounds.x = r.x - style->touch_padding.x; - bounds.y = r.y - style->touch_padding.y; - bounds.w = r.w + 2 * style->touch_padding.x; - bounds.h = r.h + 2 * style->touch_padding.y; - return zr_button_behavior(state, bounds, in, behavior); -} - -static void -zr_draw_button_text(struct zr_command_buffer *out, - const struct zr_rect *bounds, const struct zr_rect *content, zr_flags state, - const struct zr_style_button *style, const char *txt, zr_size len, - zr_flags text_alignment, const struct zr_user_font *font) -{ - struct zr_text text; - const struct zr_style_item *background; - background = zr_draw_button(out, bounds, state, style); - - if (background->type == ZR_STYLE_ITEM_COLOR) - text.background = background->data.color; - else text.background = style->text_background; - if (state & ZR_WIDGET_STATE_HOVERED) - text.text = style->text_hover; - else if (state & ZR_WIDGET_STATE_ACTIVE) - text.text = style->text_active; - else text.text = style->text_normal; - text.padding = zr_vec2(0,0); - zr_widget_text(out, *content, txt, len, &text, text_alignment, font); -} - -static int -zr_do_button_text(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect bounds, - const char *string, zr_size len, zr_flags align, enum zr_button_behavior behavior, - const struct zr_style_button *style, const struct zr_input *in, - const struct zr_user_font *font) -{ - struct zr_rect content; - int ret = zr_false; - - ZR_ASSERT(state); - ZR_ASSERT(style); - ZR_ASSERT(out); - ZR_ASSERT(string); - ZR_ASSERT(font); - if (!out || !style || !font || !string) - return zr_false; - - ret = zr_do_button(state, out, bounds, style, in, behavior, &content); - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw.button_text) - style->draw.button_text(out, &bounds, &content, *state, style, - string, len, align, font); - else zr_draw_button_text(out, &bounds, &content, *state, style, - string, len, align, font); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return ret; -} - -static void -zr_draw_button_symbol(struct zr_command_buffer *out, - const struct zr_rect *bounds, const struct zr_rect *content, - zr_flags state, const struct zr_style_button *style, - enum zr_symbol_type type, const struct zr_user_font *font) -{ - struct zr_color sym, bg; - const struct zr_style_item *background; - background = zr_draw_button(out, bounds, state, style); - if (background->type == ZR_STYLE_ITEM_COLOR) - bg = background->data.color; - else bg = style->text_background; - - if (state & ZR_WIDGET_STATE_HOVERED) - sym = style->text_hover; - else if (state & ZR_WIDGET_STATE_ACTIVE) - sym = style->text_active; - else sym = style->text_normal; - zr_draw_symbol(out, type, *content, bg, sym, 1, font); -} - -static int -zr_do_button_symbol(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect bounds, - enum zr_symbol_type symbol, enum zr_button_behavior behavior, - const struct zr_style_button *style, const struct zr_input *in, - const struct zr_user_font *font) -{ - int ret; - struct zr_rect content; - - ZR_ASSERT(state); - ZR_ASSERT(style); - ZR_ASSERT(font); - ZR_ASSERT(out); - if (!out || !style || !font || !state) - return zr_false; - - ret = zr_do_button(state, out, bounds, style, in, behavior, &content); - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw.button_symbol) - style->draw.button_symbol(out, &bounds, &content, *state, style, symbol, font); - else zr_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return ret; -} - -static void -zr_draw_button_image(struct zr_command_buffer *out, - const struct zr_rect *bounds, const struct zr_rect *content, - zr_flags state, const struct zr_style_button *style, const struct zr_image *img) -{ - zr_draw_button(out, bounds, state, style); - zr_draw_image(out, *content, img); -} - -static int -zr_do_button_image(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect bounds, - struct zr_image img, enum zr_button_behavior b, - const struct zr_style_button *style, const struct zr_input *in) -{ - int ret; - struct zr_rect content; - - ZR_ASSERT(state); - ZR_ASSERT(style); - ZR_ASSERT(out); - if (!out || !style || !state) - return zr_false; - - ret = zr_do_button(state, out, bounds, style, in, b, &content); - content.x += style->image_padding.x; - content.y += style->image_padding.y; - content.w -= 2 * style->image_padding.x; - content.h -= 2 * style->image_padding.y; - - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw.button_image) - style->draw.button_image(out, &bounds, &content, *state, style, &img); - else zr_draw_button_image(out, &bounds, &content, *state, style, &img); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return ret; -} - -static void -zr_draw_button_text_symbol(struct zr_command_buffer *out, - const struct zr_rect *bounds, const struct zr_rect *label, - const struct zr_rect *symbol, zr_flags state, const struct zr_style_button *style, - const char *str, zr_size len, enum zr_symbol_type type, - const struct zr_user_font *font) -{ - struct zr_color sym, bg; - struct zr_text text; - const struct zr_style_item *background; - background = zr_draw_button(out, bounds, state, style); - if (background->type == ZR_STYLE_ITEM_COLOR) { - text.background = background->data.color; - bg = background->data.color; - } else { - text.background = style->text_background; - bg = style->text_background; - } - - if (state & ZR_WIDGET_STATE_HOVERED) { - sym = style->text_hover; - text.text = style->text_hover; - } else if (state & ZR_WIDGET_STATE_ACTIVE) { - sym = style->text_active; - text.text = style->text_active; - } else { - sym = style->text_normal; - text.text = style->text_normal; - } - text.padding = zr_vec2(0,0); - zr_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); - zr_widget_text(out, *label, str, len, &text, ZR_TEXT_CENTERED, font); -} - -static int -zr_do_button_text_symbol(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect bounds, - enum zr_symbol_type symbol, const char *str, zr_size len, zr_flags align, - enum zr_button_behavior behavior, const struct zr_style_button *style, - const struct zr_user_font *font, const struct zr_input *in) -{ - int ret; - struct zr_rect tri = {0,0,0,0}; - struct zr_rect content; - - ZR_ASSERT(style); - ZR_ASSERT(out); - ZR_ASSERT(font); - if (!out || !style || !font) - return zr_false; - - ret = zr_do_button(state, out, bounds, style, in, behavior, &content); - tri.y = content.y + (content.h/2) - font->height/2; - tri.w = font->height; tri.h = font->height; - if (align & ZR_TEXT_ALIGN_LEFT) { - tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); - tri.x = ZR_MAX(tri.x, 0); - } else tri.x = content.x + 2 * style->padding.x; - - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw.button_text_symbol) - zr_draw_button_text_symbol(out, &bounds, &content, &tri, - *state, style, str, len, symbol, font); - else zr_draw_button_text_symbol(out, &bounds, &content, &tri, - *state, style, str, len, symbol, font); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return ret; -} - -static void -zr_draw_button_text_image(struct zr_command_buffer *out, - const struct zr_rect *bounds, const struct zr_rect *label, - const struct zr_rect *image, zr_flags state, const struct zr_style_button *style, - const char *str, zr_size len, const struct zr_user_font *font, - const struct zr_image *img) -{ - struct zr_text text; - const struct zr_style_item *background; - background = zr_draw_button(out, bounds, state, style); - - if (background->type == ZR_STYLE_ITEM_COLOR) - text.background = background->data.color; - else text.background = style->text_background; - if (state & ZR_WIDGET_STATE_HOVERED) - text.text = style->text_hover; - else if (state & ZR_WIDGET_STATE_ACTIVE) - text.text = style->text_active; - else text.text = style->text_normal; - - text.padding = zr_vec2(0,0); - zr_widget_text(out, *label, str, len, &text, ZR_TEXT_CENTERED, font); - zr_draw_image(out, *image, img); -} - -static int -zr_do_button_text_image(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect bounds, - struct zr_image img, const char* str, zr_size len, zr_flags align, - enum zr_button_behavior behavior, const struct zr_style_button *style, - const struct zr_user_font *font, const struct zr_input *in) -{ - int ret; - struct zr_rect icon; - struct zr_rect content; - - ZR_ASSERT(style); - ZR_ASSERT(state); - ZR_ASSERT(font); - ZR_ASSERT(out); - if (!out || !font || !style || !str) - return zr_false; - - ret = zr_do_button(state, out, bounds, style, in, behavior, &content); - icon.y = bounds.y + style->padding.y; - icon.w = icon.h = bounds.h - 2 * style->padding.y; - if (align & ZR_TEXT_ALIGN_LEFT) { - icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); - icon.x = ZR_MAX(icon.x, 0); - } else icon.x = bounds.x + 2 * style->padding.x; - - icon.x += style->image_padding.x; - icon.y += style->image_padding.y; - icon.w -= 2 * style->image_padding.x; - icon.h -= 2 * style->image_padding.y; - - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw.button_text_image) - zr_draw_button_text_image(out, &bounds, &content, &icon, - *state, style, str, len, font, &img); - else zr_draw_button_text_image(out, &bounds, &content, &icon, - *state, style, str, len, font, &img); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return ret; -} - -/* =============================================================== - * - * TOGGLE - * - * ===============================================================*/ -enum zr_toggle_type { - ZR_TOGGLE_CHECK, - ZR_TOGGLE_OPTION -}; - -static int -zr_toggle_behavior(const struct zr_input *in, struct zr_rect select, - zr_flags *state, int active) -{ - *state = ZR_WIDGET_STATE_INACTIVE; - if (in && zr_input_is_mouse_hovering_rect(in, select)) - *state = ZR_WIDGET_STATE_HOVERED; - if (zr_input_mouse_clicked(in, ZR_BUTTON_LEFT, select)) { - *state = ZR_WIDGET_STATE_ACTIVE; - active = !active; - } - if (*state == ZR_WIDGET_STATE_HOVERED && !zr_input_is_mouse_prev_hovering_rect(in, select)) - *state |= ZR_WIDGET_STATE_ENTERED; - else if (zr_input_is_mouse_prev_hovering_rect(in, select)) - *state |= ZR_WIDGET_STATE_LEFT; - return active; -} - -static void -zr_draw_checkbox(struct zr_command_buffer *out, - zr_flags state, const struct zr_style_toggle *style, int active, - const struct zr_rect *label, const struct zr_rect *selector, - const struct zr_rect *cursors, const char *string, zr_size len, - const struct zr_user_font *font) -{ - const struct zr_style_item *background; - const struct zr_style_item *cursor; - struct zr_text text; - - /* select correct colors */ - if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_hover; - } else if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_active; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - text.text = style->text_normal; - } - - /* draw background and cursor */ - if (background->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, *selector, &background->data.image); - else zr_fill_rect(out, *selector, 0, background->data.color); - if (active) { - if (cursor->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, *cursors, &cursor->data.image); - else zr_fill_rect(out, *cursors, 0, cursor->data.color); - } - - text.padding.x = 0; - text.padding.y = 0; - text.background = style->text_background; - zr_widget_text(out, *label, string, len, &text, ZR_TEXT_LEFT, font); -} - -static void -zr_draw_option(struct zr_command_buffer *out, - zr_flags state, const struct zr_style_toggle *style, int active, - const struct zr_rect *label, const struct zr_rect *selector, - const struct zr_rect *cursors, const char *string, zr_size len, - const struct zr_user_font *font) -{ - const struct zr_style_item *background; - const struct zr_style_item *cursor; - struct zr_text text; - - /* select correct colors */ - if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_hover; - } else if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_active; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - text.text = style->text_normal; - } - - /* draw background and cursor */ - if (background->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, *selector, &background->data.image); - else zr_fill_circle(out, *selector, background->data.color); - if (active) { - if (cursor->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, *cursors, &cursor->data.image); - else zr_fill_circle(out, *cursors, cursor->data.color); - } - - text.padding.x = 0; - text.padding.y = 0; - text.background = style->text_background; - zr_widget_text(out, *label, string, len, &text, ZR_TEXT_LEFT, font); -} - -static int -zr_do_toggle(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect r, - int *active, const char *str, zr_size len, enum zr_toggle_type type, - const struct zr_style_toggle *style, const struct zr_input *in, - const struct zr_user_font *font) -{ - int was_active; - struct zr_rect bounds; - struct zr_rect select; - struct zr_rect cursor; - struct zr_rect label; - float cursor_pad; - - ZR_ASSERT(style); - ZR_ASSERT(out); - ZR_ASSERT(font); - if (!out || !style || !font || !active) - return 0; - - r.w = ZR_MAX(r.w, font->height + 2 * style->padding.x); - r.h = ZR_MAX(r.h, font->height + 2 * style->padding.y); - - /* add additional touch padding for touch screen devices */ - bounds.x = r.x - style->touch_padding.x; - bounds.y = r.y - style->touch_padding.y; - bounds.w = r.w + 2 * style->touch_padding.x; - bounds.h = r.h + 2 * style->touch_padding.y; - - /* calculate the selector space */ - select.w = ZR_MIN(r.h, font->height + style->padding.y); - select.h = select.w; - select.x = r.x + style->padding.x; - select.y = (r.y + style->padding.y + (select.w / 2)) - (font->height / 2); - cursor_pad = (type == ZR_TOGGLE_OPTION) ? - (float)(int)(select.w / 4): - (float)(int)(select.h / 6); - - /* calculate the bounds of the cursor inside the selector */ - select.h = ZR_MAX(select.w, cursor_pad * 2); - cursor.h = select.h - cursor_pad * 2; - cursor.w = cursor.h; - cursor.x = select.x + cursor_pad; - cursor.y = select.y + cursor_pad; - - /* label behind the selector */ - label.x = r.x + select.w + style->padding.x * 2; - label.y = select.y; - label.w = ZR_MAX(r.x + r.w, label.x + style->padding.x); - label.w -= (label.x + style->padding.x); - label.h = select.w; - - was_active = *active; - *active = zr_toggle_behavior(in, bounds, state, *active); - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (type == ZR_TOGGLE_CHECK) { - if (style->draw.checkbox) - style->draw.checkbox(out, *state, - style, *active, &label, &select, &cursor, str, len, font); - else zr_draw_checkbox(out, *state, style, *active, &label, - &select, &cursor, str, len, font); - } else { - if (style->draw.radio) - style->draw.radio(out, *state, style, - *active, &label, &select, &cursor, str, len, font); - else zr_draw_option(out, *state, style, *active, &label, - &select, &cursor, str, len, font); - } - if (style->draw_end) - style->draw_begin(out, style->userdata); - return (was_active != *active); -} - -/* =============================================================== - * - * SELECTABLE - * - * ===============================================================*/ -static void -zr_draw_selectable(struct zr_command_buffer *out, - zr_flags state, const struct zr_style_selectable *style, int active, - const struct zr_rect *bounds, const char *string, zr_size len, - zr_flags align, const struct zr_user_font *font) -{ - const struct zr_style_item *background; - struct zr_text text; - text.padding = style->padding; - - if (!active) { - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->pressed; - text.text = style->text_pressed; - } else if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - text.text = style->text_hover; - } else { - background = &style->normal; - text.text = style->text_normal; - } - } else { - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->pressed_active; - text.text = style->text_pressed_active; - } else if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover_active; - text.text = style->text_hover_active; - } else { - background = &style->normal_active; - text.text = style->text_normal_active; - } - } - - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, *bounds, &background->data.image); - text.background = zr_rgba(0,0,0,0); - } else { - zr_fill_rect(out, *bounds, style->rounding, background->data.color); - text.background = background->data.color; - } - zr_widget_text(out, *bounds, string, len, &text, align, font); -} - -static int -zr_do_selectable(zr_flags *state, struct zr_command_buffer *out, - struct zr_rect bounds, const char *str, zr_size len, zr_flags align, int *value, - const struct zr_style_selectable *style, const struct zr_input *in, - const struct zr_user_font *font) -{ - int old_value; - struct zr_rect touch; - - ZR_ASSERT(state); - ZR_ASSERT(out); - ZR_ASSERT(str); - ZR_ASSERT(len); - ZR_ASSERT(value); - ZR_ASSERT(style); - ZR_ASSERT(font); - - if (!state || !out || !str || !len || !value || !style || !font) return 0; - old_value = *value; - - touch.x = bounds.x - style->touch_padding.x; - touch.y = bounds.y - style->touch_padding.y; - touch.w = bounds.w + style->touch_padding.x * 2; - touch.h = bounds.h + style->touch_padding.y * 2; - if (zr_button_behavior(state, touch, in, ZR_BUTTON_DEFAULT)) - *value = !(*value); - - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw) - style->draw(out, *state, style, *value, &bounds, - str, len, align, font); - else zr_draw_selectable(out, *state, style, *value, &bounds, - str, len, align, font); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return old_value != *value; -} - -/* =============================================================== - * - * SLIDER - * - * ===============================================================*/ -static float -zr_slider_behavior(zr_flags *state, struct zr_rect *cursor, - const struct zr_input *in, const struct zr_style_slider *style, - struct zr_rect bounds, float slider_min, float slider_max, float slider_value, - float slider_step, float slider_steps) -{ - int inslider = in && zr_input_is_mouse_hovering_rect(in, bounds); - int incursor = in && zr_input_has_mouse_click_down_in_rect(in, ZR_BUTTON_LEFT, bounds, zr_true); - - *state = (inslider) ? ZR_WIDGET_STATE_HOVERED: ZR_WIDGET_STATE_INACTIVE; - if (in && inslider && incursor) - { - const float d = in->mouse.pos.x - (cursor->x + cursor->w / 2.0f); - const float pxstep = (bounds.w - (2 * style->padding.x)) / slider_steps; - - /* only update value if the next slider step is reached */ - *state = ZR_WIDGET_STATE_ACTIVE; - if (ZR_ABS(d) >= pxstep) { - float ratio = 0; - const float steps = (float)((int)(ZR_ABS(d) / pxstep)); - slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); - slider_value = ZR_CLAMP(slider_min, slider_value, slider_max); - ratio = (slider_value - slider_min)/slider_step; - cursor->x = bounds.x + (cursor->w * ratio); - } - } - if (*state == ZR_WIDGET_STATE_HOVERED && !zr_input_is_mouse_prev_hovering_rect(in, bounds)) - *state |= ZR_WIDGET_STATE_ENTERED; - else if (zr_input_is_mouse_prev_hovering_rect(in, bounds)) - *state |= ZR_WIDGET_STATE_LEFT; - return slider_value; -} - -static void -zr_draw_slider(struct zr_command_buffer *out, zr_flags state, - const struct zr_style_slider *style, const struct zr_rect *bounds, - const struct zr_rect *virtual_cursor, float min, float value, float max) -{ - struct zr_rect fill; - struct zr_rect bar; - struct zr_rect scursor; - const struct zr_style_item *background; - - struct zr_color bar_color; - const struct zr_style_item *cursor; - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->active; - bar_color = style->bar_active; - cursor = &style->cursor_active; - } else if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - bar_color = style->bar_hover; - cursor = &style->cursor_hover; - } else { - background = &style->normal; - bar_color = style->bar_normal; - cursor = &style->cursor_normal; - } - - bar.x = bounds->x; - bar.y = (bounds->y + virtual_cursor->h/2) - virtual_cursor->h/8; - bar.w = bounds->w; - bar.h = bounds->h/6; - - scursor.h = style->cursor_size.y; - scursor.w = style->cursor_size.x; - scursor.y = (bar.y + bar.h/2.0f) - scursor.h/2.0f; - scursor.x = (value <= min) ? virtual_cursor->x: (value >= max) ? - ((bar.x + bar.w) - virtual_cursor->w): - virtual_cursor->x - (virtual_cursor->w/2); - - fill.w = (scursor.x + (scursor.w/2.0f)) - bar.x; - fill.x = bar.x; - fill.y = bar.y; - fill.h = bar.h; - - /* draw background */ - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, *bounds, &background->data.image); - } else { - zr_fill_rect(out, *bounds, style->rounding, style->border_color); - zr_fill_rect(out, zr_shrink_rect(*bounds, style->border), style->rounding, - background->data.color); - } - - /* draw slider bar */ - zr_fill_rect(out, bar, style->rounding, bar_color); - zr_fill_rect(out, fill, style->rounding, style->bar_filled); - - /* draw cursor */ - if (cursor->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, scursor, &cursor->data.image); - else zr_fill_circle(out, scursor, cursor->data.color); -} - -static float -zr_do_slider(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect bounds, - float min, float val, float max, float step, - const struct zr_style_slider *style, const struct zr_input *in, - const struct zr_user_font *font) -{ - float slider_range; - float slider_min; - float slider_max; - float slider_value; - float slider_steps; - float cursor_offset; - struct zr_rect cursor; - - ZR_ASSERT(style); - ZR_ASSERT(out); - if (!out || !style) - return 0; - - /* remove padding from slider bounds */ - bounds.x = bounds.x + style->padding.x; - bounds.y = bounds.y + style->padding.y; - bounds.h = ZR_MAX(bounds.h, 2 * style->padding.y); - bounds.w = ZR_MAX(bounds.w, 1 + bounds.h + 2 * style->padding.x); - bounds.h -= 2 * style->padding.y; - bounds.w -= 2 * style->padding.y; - - /* optional buttons */ - if (style->show_buttons) { - zr_flags ws; - struct zr_rect button; - button.y = bounds.y; - button.w = bounds.h; - button.h = bounds.h; - - /* decrement button */ - button.x = bounds.x; - if (zr_do_button_symbol(&ws, out, button, style->dec_symbol, ZR_BUTTON_DEFAULT, - &style->dec_button, in, font)) - val -= step; - - /* increment button */ - button.x = (bounds.x + bounds.w) - button.w; - if (zr_do_button_symbol(&ws, out, button, style->inc_symbol, ZR_BUTTON_DEFAULT, - &style->inc_button, in, font)) - val += step; - - bounds.x = bounds.x + button.w + style->spacing.x; - bounds.w = bounds.w - (2 * button.w + 2 * style->spacing.x); - } - - /* make sure the provided values are correct */ - slider_max = ZR_MAX(min, max); - slider_min = ZR_MIN(min, max); - slider_value = ZR_CLAMP(slider_min, val, slider_max); - slider_range = slider_max - slider_min; - slider_steps = slider_range / step; - - /* calculate slider virtual cursor bounds */ - cursor_offset = (slider_value - slider_min) / step; - cursor.h = bounds.h; - cursor.w = bounds.w / (slider_steps + 1); - cursor.x = bounds.x + (cursor.w * cursor_offset); - cursor.y = bounds.y; - slider_value = zr_slider_behavior(state, &cursor, in, style, bounds, - slider_min, slider_max, slider_value, step, slider_steps); - - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw) - style->draw(out, *state, style, &bounds, &cursor, - slider_min, slider_value, slider_max); - else zr_draw_slider(out, *state, style, &bounds, &cursor, - slider_min, slider_value, slider_max); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return slider_value; -} - -/* =============================================================== - * - * PROGRESSBAR - * - * ===============================================================*/ -static zr_size -zr_progress_behavior(zr_flags *state, const struct zr_input *in, - struct zr_rect r, zr_size max, zr_size value, int modifiable) -{ - *state = ZR_WIDGET_STATE_INACTIVE; - if (in && modifiable && zr_input_is_mouse_hovering_rect(in, r)) { - if (zr_input_is_mouse_down(in, ZR_BUTTON_LEFT)) { - float ratio = ZR_MAX(0, (float)(in->mouse.pos.x - r.x)) / (float)r.w; - value = (zr_size)ZR_MAX(0,((float)max * ratio)); - *state = ZR_WIDGET_STATE_ACTIVE; - } else *state = ZR_WIDGET_STATE_HOVERED; - } - - if (*state == ZR_WIDGET_STATE_HOVERED && !zr_input_is_mouse_prev_hovering_rect(in, r)) - *state |= ZR_WIDGET_STATE_ENTERED; - else if (zr_input_is_mouse_prev_hovering_rect(in, r)) - *state |= ZR_WIDGET_STATE_LEFT; - - if (!max) return value; - value = ZR_MIN(value, max); - return value; -} - -static void -zr_draw_progress(struct zr_command_buffer *out, zr_flags state, - const struct zr_style_progress *style, const struct zr_rect *bounds, - const struct zr_rect *scursor, zr_size value, zr_size max) -{ - const struct zr_style_item *background; - const struct zr_style_item *cursor; - - ZR_UNUSED(max); - ZR_UNUSED(value); - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->active; - cursor = &style->cursor_active; - } else if (state & ZR_WIDGET_STATE_HOVERED){ - background = &style->hover; - cursor = &style->cursor_hover; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - } - - if (background->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, *bounds, &background->data.image); - else zr_fill_rect(out, *bounds, style->rounding, background->data.color); - - if (cursor->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, *scursor, &cursor->data.image); - else zr_fill_rect(out, *scursor, style->rounding, cursor->data.color); -} - -static zr_size -zr_do_progress(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect bounds, - zr_size value, zr_size max, int modifiable, - const struct zr_style_progress *style, const struct zr_input *in) -{ - zr_size prog_value; - float prog_scale; - struct zr_rect cursor; - - ZR_ASSERT(style); - ZR_ASSERT(out); - if (!out || !style) return 0; - - cursor.w = ZR_MAX(bounds.w, 2 * style->padding.x); - cursor.h = ZR_MAX(bounds.h, 2 * style->padding.y); - cursor = zr_pad_rect(bounds, zr_vec2(style->padding.x, style->padding.y)); - - prog_scale = (float)value / (float)max; - cursor.w = (bounds.w - 2) * prog_scale; - - prog_value = ZR_MIN(value, max); - prog_value = zr_progress_behavior(state, in, bounds, max, prog_value, modifiable); - - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw) - style->draw(out, *state, style, &bounds, &cursor, value, max); - else zr_draw_progress(out, *state, style, &bounds, &cursor, value, max); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return prog_value; -} - -/* =============================================================== - * - * SCROLLBAR - * - * ===============================================================*/ -static float -zr_scrollbar_behavior(zr_flags *state, struct zr_input *in, - int has_scrolling, struct zr_rect scroll, - struct zr_rect cursor, float scroll_offset, - float target, float scroll_step, enum zr_orientation o) -{ - int left_mouse_down; - int left_mouse_click_in_cursor; - if (!in) return scroll_offset; - - *state = ZR_WIDGET_STATE_INACTIVE; - left_mouse_down = in->mouse.buttons[ZR_BUTTON_LEFT].down; - left_mouse_click_in_cursor = zr_input_has_mouse_click_down_in_rect(in, - ZR_BUTTON_LEFT, cursor, zr_true); - if (zr_input_is_mouse_hovering_rect(in, scroll)) - *state = ZR_WIDGET_STATE_HOVERED; - - if (left_mouse_down && left_mouse_click_in_cursor) { - /* update cursor by mouse dragging */ - float pixel, delta; - *state = ZR_WIDGET_STATE_ACTIVE; - if (o == ZR_VERTICAL) { - pixel = in->mouse.delta.y; - delta = (pixel / scroll.h) * target; - scroll_offset = ZR_CLAMP(0, scroll_offset + delta, target - scroll.h); - /* This is probably one of my most disgusting hacks I have ever done. - * This basically changes the mouse clicked position with the moving - * cursor. This allows for better scroll behavior but resulted into me - * having to remove const correctness for input. But in the end I believe - * it is worth it. */ - in->mouse.buttons[ZR_BUTTON_LEFT].clicked_pos.y += in->mouse.delta.y; - } else { - pixel = in->mouse.delta.x; - delta = (pixel / scroll.w) * target; - scroll_offset = ZR_CLAMP(0, scroll_offset + delta, target - scroll.w); - in->mouse.buttons[ZR_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x; - } - } else if (has_scrolling && ((in->mouse.scroll_delta<0) || - (in->mouse.scroll_delta>0))) { - /* update cursor by mouse scrolling */ - scroll_offset = scroll_offset + scroll_step * (-in->mouse.scroll_delta); - if (o == ZR_VERTICAL) - scroll_offset = ZR_CLAMP(0, scroll_offset, target - scroll.h); - else scroll_offset = ZR_CLAMP(0, scroll_offset, target - scroll.w); - } - if (*state == ZR_WIDGET_STATE_HOVERED && !zr_input_is_mouse_prev_hovering_rect(in, scroll)) - *state |= ZR_WIDGET_STATE_ENTERED; - else if (zr_input_is_mouse_prev_hovering_rect(in, scroll)) - *state |= ZR_WIDGET_STATE_LEFT; - return scroll_offset; -} - -static void -zr_draw_scrollbar(struct zr_command_buffer *out, zr_flags state, - const struct zr_style_scrollbar *style, const struct zr_rect *bounds, - const struct zr_rect *scroll) -{ - const struct zr_style_item *background; - const struct zr_style_item *cursor; - - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->active; - cursor = &style->cursor_active; - } else if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - cursor = &style->cursor_hover; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - } - - /* draw background */ - if (background->type == ZR_STYLE_ITEM_COLOR) { - zr_fill_rect(out, *bounds, style->rounding, style->border_color); - zr_fill_rect(out, zr_shrink_rect(*bounds,style->border), style->rounding, background->data.color); - } else { - zr_draw_image(out, *bounds, &background->data.image); - } - - /* draw cursor */ - if (cursor->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, *scroll, &cursor->data.image); - else zr_fill_rect(out, *scroll, style->rounding, cursor->data.color); -} - -static float -zr_do_scrollbarv(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect scroll, int has_scrolling, - float offset, float target, float step, float button_pixel_inc, - const struct zr_style_scrollbar *style, struct zr_input *in, - const struct zr_user_font *font) -{ - struct zr_rect cursor; - float scroll_step; - float scroll_offset; - float scroll_off; - float scroll_ratio; - - ZR_ASSERT(out); - ZR_ASSERT(style); - ZR_ASSERT(state); - if (!out || !style) return 0; - - scroll.w = ZR_MAX(scroll.w, 1); - scroll.h = ZR_MAX(scroll.h, 2 * scroll.w); - if (target <= scroll.h) return 0; - - /* optional scrollbar buttons */ - if (style->show_buttons) { - zr_flags ws; - float scroll_h; - struct zr_rect button; - button.x = scroll.x; - button.w = scroll.w; - button.h = scroll.w; - - scroll_h = scroll.h - 2 * button.h; - scroll_step = ZR_MIN(step, button_pixel_inc); - - /* decrement button */ - button.y = scroll.y; - if (zr_do_button_symbol(&ws, out, button, style->dec_symbol, - ZR_BUTTON_REPEATER, &style->dec_button, in, font)) - offset = offset - scroll_step; - - /* increment button */ - button.y = scroll.y + scroll.h - button.h; - if (zr_do_button_symbol(&ws, out, button, style->inc_symbol, - ZR_BUTTON_REPEATER, &style->inc_button, in, font)) - offset = offset + scroll_step; - - scroll.y = scroll.y + button.h; - scroll.h = scroll_h; - } - - /* calculate scrollbar constants */ - scroll_step = ZR_MIN(step, scroll.h); - scroll_offset = ZR_CLAMP(0, offset, target - scroll.h); - scroll_ratio = scroll.h / target; - scroll_off = scroll_offset / target; - - /* calculate scrollbar cursor bounds */ - cursor.h = (scroll_ratio * scroll.h - 2); - cursor.y = scroll.y + (scroll_off * scroll.h) + 1; - cursor.w = scroll.w - 2; - cursor.x = scroll.x + 1; - - /* update scrollbar */ - scroll_offset = zr_scrollbar_behavior(state, in, has_scrolling, scroll, cursor, - scroll_offset, target, scroll_step, ZR_VERTICAL); - scroll_off = scroll_offset / target; - cursor.y = scroll.y + (scroll_off * scroll.h); - - /* draw scrollbar */ - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw) - style->draw(out, *state, style, &scroll, &cursor); - else zr_draw_scrollbar(out, *state, style, &scroll, &cursor); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return scroll_offset; -} - -static float -zr_do_scrollbarh(zr_flags *state, - struct zr_command_buffer *out, struct zr_rect scroll, int has_scrolling, - float offset, float target, float step, float button_pixel_inc, - const struct zr_style_scrollbar *style, struct zr_input *in, - const struct zr_user_font *font) -{ - struct zr_rect cursor; - float scroll_step; - float scroll_offset; - float scroll_off; - float scroll_ratio; - - ZR_ASSERT(out); - ZR_ASSERT(style); - if (!out || !style) return 0; - - /* scrollbar background */ - scroll.h = ZR_MAX(scroll.h, 1); - scroll.w = ZR_MAX(scroll.w, 2 * scroll.h); - if (target <= scroll.w) return 0; - - /* optional scrollbar buttons */ - if (style->show_buttons) { - zr_flags ws; - float scroll_w; - struct zr_rect button; - button.y = scroll.y; - button.w = scroll.h; - button.h = scroll.h; - - scroll_w = scroll.w - 2 * button.w; - scroll_step = ZR_MIN(step, button_pixel_inc); - - /* decrement button */ - button.x = scroll.x; - if (zr_do_button_symbol(&ws, out, button, style->dec_symbol, - ZR_BUTTON_REPEATER, &style->dec_button, in, font)) - offset = offset - scroll_step; - - /* increment button */ - button.x = scroll.x + scroll.w - button.w; - if (zr_do_button_symbol(&ws, out, button, style->inc_symbol, - ZR_BUTTON_REPEATER, &style->inc_button, in, font)) - offset = offset + scroll_step; - - scroll.x = scroll.x + button.w; - scroll.w = scroll_w; - } - - /* calculate scrollbar constants */ - scroll_step = ZR_MIN(step, scroll.w); - scroll_offset = ZR_CLAMP(0, offset, target - scroll.w); - scroll_ratio = scroll.w / target; - scroll_off = scroll_offset / target; - - /* calculate cursor bounds */ - cursor.w = scroll_ratio * scroll.w - 2; - cursor.x = scroll.x + (scroll_off * scroll.w) + 1; - cursor.h = scroll.h - 2; - cursor.y = scroll.y + 1; - - /* update scrollbar */ - scroll_offset = zr_scrollbar_behavior(state, in, has_scrolling, scroll, cursor, - scroll_offset, target, scroll_step, ZR_HORIZONTAL); - scroll_off = scroll_offset / target; - cursor.x = scroll.x + (scroll_off * scroll.w); - - /* draw scrollbar */ - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw) - style->draw(out, *state, style, &scroll, &cursor); - else zr_draw_scrollbar(out, *state, style, &scroll, &cursor); - if (style->draw_end) - style->draw_begin(out, style->userdata); - return scroll_offset; -} - -/* =============================================================== - * - * FILTER - * - * ===============================================================*/ -int zr_filter_default(const struct zr_edit_box *box, zr_rune unicode) -{(void)unicode;ZR_UNUSED(box);return zr_true;} - -int -zr_filter_ascii(const struct zr_edit_box *box, zr_rune unicode) -{ - ZR_UNUSED(box); - if (unicode > 128) return zr_false; - else return zr_true; -} - -int -zr_filter_float(const struct zr_edit_box *box, zr_rune unicode) -{ - ZR_UNUSED(box); - if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') - return zr_false; - else return zr_true; -} - -int -zr_filter_decimal(const struct zr_edit_box *box, zr_rune unicode) -{ - ZR_UNUSED(box); - if ((unicode < '0' || unicode > '9') && unicode != '-') - return zr_false; - else return zr_true; -} - -int -zr_filter_hex(const struct zr_edit_box *box, zr_rune unicode) -{ - ZR_UNUSED(box); - if ((unicode < '0' || unicode > '9') && - (unicode < 'a' || unicode > 'f') && - (unicode < 'A' || unicode > 'F')) - return zr_false; - else return zr_true; -} - -int -zr_filter_oct(const struct zr_edit_box *box, zr_rune unicode) -{ - ZR_UNUSED(box); - if (unicode < '0' || unicode > '7') - return zr_false; - else return zr_true; -} - -int -zr_filter_binary(const struct zr_edit_box *box, zr_rune unicode) -{ - ZR_UNUSED(box); - if (unicode != '0' && unicode != '1') - return zr_false; - else return zr_true; -} - -/* =============================================================== - * - * EDIT - * - * ===============================================================*/ -static void -zr_edit_input_behavior(struct zr_edit_box *box, const struct zr_input *in, int has_special) -{ - char *buffer = zr_edit_box_get(box); - zr_size len = zr_edit_box_len_char(box); - zr_size min = ZR_MIN(box->sel.end, box->sel.begin); - zr_size maxi = ZR_MAX(box->sel.end, box->sel.begin); - zr_size diff = maxi - min; - int enter, tab; - - /* text manipulation */ - if (zr_input_is_key_pressed(in,ZR_KEY_DEL)) - zr_edit_box_remove(box, ZR_DELETE); - else if (zr_input_is_key_pressed(in,ZR_KEY_BACKSPACE)) - zr_edit_box_remove(box, ZR_REMOVE); - - enter = has_special && zr_input_is_key_pressed(in, ZR_KEY_ENTER); - tab = has_special && zr_input_is_key_pressed(in, ZR_KEY_TAB); - if (in->keyboard.text_len || enter || tab) { - if (diff && box->cursor != box->glyphs) { - /* replace text selection */ - zr_edit_box_remove(box, ZR_DELETE); - box->cursor = min; - } - if (enter) zr_edit_box_add(box, "\n", 1); - else if (tab) zr_edit_box_add(box, " ", 4); - else zr_edit_box_buffer_input(box, in); - box->sel.begin = box->cursor; - box->sel.end = box->cursor; - } - - /* cursor key movement */ - if (zr_input_is_key_pressed(in, ZR_KEY_LEFT)) { - box->cursor = (zr_size)(ZR_MAX(0, (int)box->cursor - 1)); - box->sel.begin = box->cursor; - box->sel.end = box->cursor; - } - if (zr_input_is_key_pressed(in, ZR_KEY_RIGHT) && box->cursor < box->glyphs) { - box->cursor = ZR_MIN((!box->glyphs) ? 0 : box->glyphs, box->cursor + 1); - box->sel.begin = box->cursor; - box->sel.end = box->cursor; - } - - /* copy & cut & paste functionlity */ - if (zr_input_is_key_pressed(in, ZR_KEY_PASTE) && box->clip.paste) - box->clip.paste(box->clip.userdata, box); - - if ((zr_input_is_key_pressed(in, ZR_KEY_COPY) && box->clip.copy) || - (zr_input_is_key_pressed(in, ZR_KEY_CUT) && box->clip.copy)) { - if (diff && box->cursor != box->glyphs) { - /* copy or cut text selection */ - zr_size l; - zr_rune unicode; - char *begin, *end; - begin = zr_edit_buffer_at(&box->buffer, (int)min, &unicode, &l); - end = zr_edit_buffer_at(&box->buffer, (int)maxi, &unicode, &l); - box->clip.copy(box->clip.userdata, begin, (zr_size)(end - begin)); - if (zr_input_is_key_pressed(in, ZR_KEY_CUT)) - zr_edit_box_remove(box, ZR_DELETE); - } else { - /* copy or cut complete buffer */ - box->clip.copy(box->clip.userdata, buffer, len); - if (zr_input_is_key_pressed(in, ZR_KEY_CUT)) - zr_edit_box_clear(box); - } - } -} - -static void -zr_edit_click_behavior(zr_flags *state, const struct zr_style_edit *style, - struct zr_rect *bounds, int show_cursor, struct zr_edit_box *box, - const struct zr_input *in, const struct zr_user_font *font, const char *text, - /* these three here describe the current text frame */ - zr_size text_len, zr_size glyph_off, zr_size glyph_cnt) -{ - /* check if the editbox is activated/deactivated */ - if (!in) return; - *state = ZR_WIDGET_STATE_INACTIVE; - if (in && in->mouse.buttons[ZR_BUTTON_LEFT].clicked && - in->mouse.buttons[ZR_BUTTON_LEFT].down) { - box->active = ZR_INBOX(in->mouse.pos.x,in->mouse.pos.y,bounds->x,bounds->y,bounds->w,bounds->h); - if (box->active) *state = ZR_WIDGET_STATE_ACTIVE; - } - if (ZR_INBOX(in->mouse.pos.x, in->mouse.pos.y, bounds->x, bounds->y, bounds->w, bounds->h)) - *state |= ZR_WIDGET_STATE_HOVERED; - - /* set cursor by mouse click and handle text selection */ - if (in && show_cursor && in->mouse.buttons[ZR_BUTTON_LEFT].down && box->active) { - const char *visible = text; - float xoff = in->mouse.pos.x - (bounds->x + style->padding.x + style->border); - if (*state & ZR_WIDGET_STATE_HOVERED) - { - /* text selection in current text frame */ - zr_size glyph_index; - zr_size glyph_pos=zr_user_font_glyph_index_at_pos(font,visible,text_len,xoff); - if (glyph_cnt + glyph_off >= box->glyphs) - glyph_index = glyph_off + ZR_MIN(glyph_pos, glyph_cnt); - else glyph_index = glyph_off + ZR_MIN(glyph_pos, glyph_cnt-1); - - if (text_len) - zr_edit_box_set_cursor(box, glyph_index); - if (!box->sel.active) { - box->sel.active = zr_true; - box->sel.begin = glyph_index; - box->sel.end = box->sel.begin; - } else { - if (box->sel.begin > glyph_index) { - box->sel.end = glyph_index; - box->sel.active = zr_true; - } - } - } else if (!ZR_INBOX(in->mouse.pos.x,in->mouse.pos.y,bounds->x,bounds->y,bounds->w,bounds->h) && - ZR_INBOX(in->mouse.buttons[ZR_BUTTON_LEFT].clicked_pos.x, - in->mouse.buttons[ZR_BUTTON_LEFT].clicked_pos.y,bounds->x,bounds->y,bounds->w,bounds->h) - && box->cursor != box->glyphs && box->cursor > 0) - { - /* text selection out of the current text frame */ - zr_size glyph = ((in->mouse.pos.x > bounds->x) && - box->cursor+1 < box->glyphs) ? - box->cursor+1: box->cursor-1; - zr_edit_box_set_cursor(box, glyph); - if (box->sel.active) { - box->sel.end = glyph; - box->sel.active = zr_true; - } - } else box->sel.active = zr_false; - } else box->sel.active = zr_false; -} - -static void -zr_draw_edit(struct zr_command_buffer *out, zr_flags state, - const struct zr_style_edit *style, const struct zr_rect *bounds, - const struct zr_rect *label, const struct zr_rect *selection, - int show_cursor, const char *unselected_text, zr_size unselected_len, - const char *selected_text, zr_size selected_len, - const struct zr_edit_box *box, const struct zr_user_font *font) -{ - const struct zr_style_item *background; - const struct zr_style_item *cursor; - struct zr_color selected; - struct zr_color sel_text; - struct zr_color text; - - /* select correct colors */ - struct zr_text txt; - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->active; - cursor = &style->cursor_active; - text = style->text_active; - selected = style->selected_normal; - sel_text = style->selected_text_normal; - } else if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - cursor = &style->cursor_hover; - text = style->text_hover; - selected = style->selected_hover; - sel_text = style->selected_text_hover; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - text = style->text_normal; - selected = style->selected_normal; - sel_text = style->selected_text_normal; - } - - /* draw background color/image */ - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, *bounds, &background->data.image); - txt.background = zr_rgba(0,0,0,0); - } else { - txt.background = background->data.color; - zr_fill_rect(out, *bounds, style->rounding, style->border_color); - zr_fill_rect(out, zr_shrink_rect(*bounds, style->border), - style->rounding, style->normal.data.color); - } - - /* draw unselected text */ - txt.padding = zr_vec2(0,0); - txt.text = text; - zr_widget_text(out, *label, unselected_text, unselected_len, - &txt, ZR_TEXT_LEFT, font); - - if (box->active && show_cursor) { - if (box->cursor == box->glyphs) { - /* draw cursor at the end of the string */ - float text_width; - zr_size s = font->width(font->userdata, font->height, - unselected_text, unselected_len); - text_width = (float)s; - if (cursor->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, zr_rect(label->x+(float)text_width, - label->y, style->cursor_size, label->h), &cursor->data.image); - else zr_fill_rect(out, zr_rect(label->x+(float)text_width, - label->y, style->cursor_size, label->h), 0, cursor->data.color); - } else { - /* draw text selection */ - struct zr_rect clip = out->clip; - zr_push_scissor(out, clip); - zr_fill_rect(out, *selection, 0, selected); - - txt.padding = zr_vec2(0,0); - txt.background = selected; - txt.text = sel_text; - zr_widget_text(out, *selection, selected_text, selected_len, - &txt, ZR_TEXT_LEFT, font); - zr_push_scissor(out, clip); - } - } -} - -static void -zr_do_edit_buffer(zr_flags *state, struct zr_command_buffer *out, - struct zr_rect bounds, struct zr_edit_box *box, const struct zr_style_edit *style, - int show_cursor, const struct zr_input *in, const struct zr_user_font *font) -{ - char *buffer; - zr_size len; - - /* text frame */ - zr_size text_len = 0; - zr_size glyph_off = 0; - zr_size glyph_cnt = 0; - zr_size offset = 0; - float text_width = 0; - - /* selection text */ - char *selection_begin = 0, *selection_end = 0; - zr_size off_begin = 0, off_end = 0, off_max = 0; - - struct zr_rect label; - struct zr_rect selection = zr_rect(0,0,0,0); - - ZR_ASSERT(out); - ZR_ASSERT(font); - ZR_ASSERT(style); - ZR_ASSERT(state); - if (!out || !box || !style) - return; - - buffer = zr_edit_box_get(box); - text_len = len = zr_edit_box_len_char(box); - bounds.w = ZR_MAX(bounds.w, 2 * style->padding.x + 2 * style->border); - bounds.h = ZR_MAX(bounds.h, font->height + (2 * style->padding.y + 2 * style->border)); - - /* calculate visible text frame */ - label.w = ZR_MAX(bounds.w, - 2 * style->padding.x - 2 * style->border); - label.w -= 2 * style->padding.x - 2 * style->border; - { - zr_size frames = 0; - zr_size glyphs = 0; - zr_size frame_len = 0; - zr_size row_len = 0; - zr_size cursor_w = (zr_size)style->cursor_size; - float space = ZR_MAX(label.w, (float)cursor_w); - space -= (float)cursor_w; - - while (text_len) { - frames++; - offset += frame_len; - frame_len = zr_user_font_glyphs_fitting_in_space(font, - &buffer[offset], text_len, space, &row_len, &glyphs, &text_width, 0); - glyph_off += glyphs; - if (glyph_off > box->cursor || !frame_len) break; - text_len -= frame_len; - } - - text_len = frame_len; - glyph_cnt = glyphs; - glyph_off = (frames <= 1) ? 0 : (glyph_off - glyphs); - offset = (frames <= 1) ? 0 : offset; - } - - /* update edit state */ - if (box->active && in) - zr_edit_input_behavior(box, in, 0); - zr_edit_click_behavior(state, style, &bounds, show_cursor, box, in, font, - &buffer[offset], text_len, glyph_off, glyph_cnt); - - /* calculate unselected text bounds */ - label.x = bounds.x + style->padding.x + style->border; - label.y = bounds.y + style->padding.y + style->border; - label.h = bounds.h - (2 * style->padding.y + 2 * style->border); - - /* calculate selected text */ - if (box->active && show_cursor && box->cursor < box->glyphs) { - zr_size s; - zr_rune unicode; - - /* calculate selection text range */ - zr_size min = ZR_MIN(box->sel.end, box->sel.begin); - zr_size maxi = ZR_MAX(box->sel.end, box->sel.begin); - selection_begin = zr_edit_buffer_at(&box->buffer, (int)min, &unicode, &off_max); - selection_end = zr_edit_buffer_at(&box->buffer, (int)maxi, &unicode, &off_max); - off_begin = (zr_size)(selection_begin - (char*)box->buffer.memory.ptr); - off_end = (zr_size)(selection_end - (char*)box->buffer.memory.ptr); - - /* calculate selected text bounds */ - selection = label; - s = font->width(font->userdata, font->height, buffer + offset, off_begin - offset); - selection.x += (float)s; - s = font->width(font->userdata, font->height, selection_begin, ZR_MAX(off_max, off_end - off_begin)); - selection.w = (float)s; - } - - /* draw edit */ - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw) - style->draw(out, *state, style, &bounds, &label, &selection, - show_cursor, &buffer[offset], text_len, selection_begin, - ZR_MAX(off_max, off_end - off_begin), box, font); - else zr_draw_edit(out, *state, style, &bounds, &label, &selection, - show_cursor, &buffer[offset], text_len, selection_begin, - ZR_MAX(off_max, off_end - off_begin), box, font); - if (style->draw_end) - style->draw_begin(out, style->userdata); -} - -static zr_size -zr_do_edit_string(zr_flags *state, struct zr_command_buffer *out, struct zr_rect r, - char *buffer, zr_size len, zr_size max, int *active, - zr_size *cursor, int show_cursor, const struct zr_style_edit *style, - zr_filter filter, const struct zr_input *in, const struct zr_user_font *font) -{ - struct zr_edit_box box; - zr_edit_box_init(&box, buffer, max, 0, filter); - box.buffer.allocated = len; - box.active = *active; - box.glyphs = zr_utf_len(buffer, len); - if (!cursor) { - box.cursor = box.glyphs; - } else{ - box.cursor = ZR_MIN(*cursor, box.glyphs); - box.sel.begin = box.cursor; - box.sel.end = box.cursor; - } - zr_do_edit_buffer(state, out, r, &box, style, show_cursor, in, font); - - *active = box.active; - if (cursor) *cursor = box.cursor; - return zr_edit_box_len_char(&box); -} -/* =============================================================== - * - * EDIT FIELD - * - * ===============================================================*/ -static void -zr_draw_edit_field(struct zr_command_buffer *out, zr_flags state, - const struct zr_style_edit *style, const struct zr_rect *bounds, - int show_cursor, const char *buffer, zr_size len, float total_width, - const struct zr_edit_box *box, const struct zr_user_font *font) -{ - zr_size text_len = len; - zr_size offset = 0; - zr_size row_off = 0; - zr_size row_len = 0; - zr_size glyphs = 0; - zr_size glyph_off = 0; - float text_width = 0; - struct zr_rect scissor; - struct zr_rect clip; - - struct zr_rect label; - struct zr_rect old_clip = out->clip; - - const struct zr_style_item *background; - const struct zr_style_item *cursor; - struct zr_color selected; - struct zr_color sel_text; - struct zr_color text; - struct zr_color text_background; - - /* select correct colors */ - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->active; - cursor = &style->cursor_active; - text = style->text_active; - selected = style->selected_normal; - sel_text = style->selected_text_normal; - } else if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - cursor = &style->cursor_hover; - text = style->text_hover; - selected = style->selected_hover; - sel_text = style->selected_text_hover; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - text = style->text_normal; - selected = style->selected_normal; - sel_text = style->selected_text_normal; - } - - /* draw background color/image */ - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, *bounds, &background->data.image); - text_background = zr_rgba(0,0,0,0); - } else { - text_background = background->data.color; - zr_fill_rect(out, *bounds, style->rounding, style->border_color); - zr_fill_rect(out, zr_shrink_rect(*bounds, style->border), - style->rounding, style->normal.data.color); - } - - /* calculate clipping rect for scrollbar */ - clip = zr_shrink_rect(*bounds, style->border); - clip.x += style->padding.x; - clip.y += style->padding.y; - clip.w -= 2 * style->padding.x; - clip.h -= 2 * style->padding.y; - zr_unify(&scissor, &out->clip, clip.x, clip.y, clip.x + clip.w, clip.y + clip.h); - - /* calculate row text space */ - zr_push_scissor(out, scissor); - label.x = bounds->x + style->padding.x + style->border; - label.y = (bounds->y + style->padding.y + style->border) - box->scrollbar; - label.h = font->height + style->padding.y; - - /* draw each text row */ - while (text_len) { - /* selection bounds */ - struct zr_text txt; - zr_size begin = ZR_MIN(box->sel.end, box->sel.begin); - zr_size end = ZR_MAX(box->sel.end, box->sel.begin); - - offset += row_off; - row_off = zr_user_font_glyphs_fitting_in_space(font, - &buffer[offset], text_len, total_width, &row_len, - &glyphs, &text_width, 1); - label.w = text_width; - if (!row_off || !row_len) break; - - /* draw either unselected or selected row */ - if (glyph_off <= begin && glyph_off + glyphs > begin && - glyph_off + glyphs <= end && box->active) - { - /* 1.) selection beginning in current row */ - zr_size l = 0, sel_begin, sel_len; - zr_size unselected_text_width; - zr_rune unicode; - - /* calculate selection beginning string position */ - const char *from; - from = zr_utf_at(&buffer[offset], row_len, - (int)(begin - glyph_off), &unicode, &l); - sel_begin = (zr_size)(from - (char*)box->buffer.memory.ptr); - sel_begin = sel_begin - offset; - sel_len = row_len - sel_begin; - - /* draw unselected text part */ - unselected_text_width = - font->width(font->userdata, font->height, &buffer[offset], - (row_len >= sel_len) ? row_len - sel_len: 0); - - txt.padding = zr_vec2(0,0); - txt.background = text_background; - txt.text = text; - zr_widget_text(out, label, &buffer[offset], - (row_len >= sel_len) ? row_len - sel_len: 0, - &txt, ZR_TEXT_LEFT, font); - - /* draw selected text part */ - label.x += (float)(unselected_text_width); - label.w -= (float)(unselected_text_width); - txt.background = selected; - txt.text = sel_text; - zr_fill_rect(out, label, 0, selected); - zr_widget_text(out, label, &buffer[offset+sel_begin], - sel_len, &txt, ZR_TEXT_LEFT, font); - - label.x -= (float)unselected_text_width; - label.w += (float)(unselected_text_width); - } else if (glyph_off > begin && glyph_off + glyphs < end && box->active) { - /* 2.) selection spanning over current row */ - txt.padding = zr_vec2(0,0); - txt.background = selected; - txt.text = sel_text; - zr_fill_rect(out, label, 0, selected); - zr_widget_text(out, label, &buffer[offset], row_len, - &txt, ZR_TEXT_LEFT, font); - } else if (glyph_off > begin && glyph_off + glyphs >= end && - box->active && end >= glyph_off && end <= glyph_off + glyphs) { - /* 3.) selection ending in current row */ - zr_size l = 0, sel_end, sel_len; - zr_size selected_text_width; - zr_rune unicode; - - /* calculate selection beginning string position */ - const char *to = zr_utf_at(&buffer[offset], row_len, - (int)(end - glyph_off), &unicode, &l); - sel_end = (zr_size)(to - (char*)box->buffer.memory.ptr); - sel_len = (sel_end - offset); - sel_end = sel_end - offset; - - /* draw selected text part */ - selected_text_width = font->width(font->userdata, font->height, - &buffer[offset], sel_len); - txt.padding = zr_vec2(0,0); - txt.background = selected; - txt.text = sel_text; - zr_fill_rect(out, label, 0, selected); - zr_widget_text(out, label, &buffer[offset], sel_len, - &txt, ZR_TEXT_LEFT, font); - - /* draw unselected text part */ - label.x += (float)selected_text_width; - label.w -= (float)(selected_text_width); - txt.background = text_background; - txt.text = text; - zr_widget_text(out, label, &buffer[offset+sel_end], - (row_len >= sel_len) ? row_len - sel_len: 0, - &txt, ZR_TEXT_LEFT, font); - - label.x -= (float)selected_text_width; - label.w += (float)(selected_text_width); - } - else if (glyph_off <= begin && glyph_off + glyphs >= begin && - box->active && glyph_off <= end && glyph_off + glyphs > end) - { - /* 4.) selection beginning and ending in current row */ - zr_size l = 0; - zr_size cur_text_width; - zr_size cur_len; - zr_size sel_begin, sel_end, sel_len; - zr_rune unicode; - float label_x = label.x; - float label_w = label.w; - float tmp; - - const char *from = zr_utf_at(&buffer[offset], row_len, - (int)(begin - glyph_off), &unicode, &l); - const char *to = zr_utf_at(&buffer[offset], row_len, - (int)(end - glyph_off), &unicode, &l); - - /* calculate selection bounds and length */ - sel_begin = (zr_size)(from - (char*)box->buffer.memory.ptr); - sel_begin = sel_begin - offset; - sel_end = (zr_size)(to - (char*)box->buffer.memory.ptr); - sel_end = sel_end - offset; - sel_len = (sel_end - sel_begin); - if (!sel_len) { - sel_len = zr_utf_decode(&buffer[offset+sel_begin], - &unicode, row_len); - sel_end += zr_utf_decode(&buffer[offset+sel_end], - &unicode, row_len); - } - - /* draw beginning unselected text part */ - cur_text_width = font->width(font->userdata, font->height, - &buffer[offset], sel_begin); - txt.padding = zr_vec2(0,0); - txt.background = text_background; - txt.text = text; - zr_widget_text(out, label, &buffer[offset], sel_begin, - &txt, ZR_TEXT_LEFT, font); - - /* draw selected text part */ - label.x += (float)cur_text_width; - label.w -= (float)(cur_text_width); - tmp = label.w; - - txt.background = selected; - txt.text = sel_text; - cur_len = font->width(font->userdata, font->height, - &buffer[offset+sel_begin], sel_len); - label.w = (float)cur_len; - zr_fill_rect(out, label, 0, selected); - zr_widget_text(out, label, &buffer[offset+sel_begin], sel_len, - &txt, ZR_TEXT_LEFT, font); - cur_text_width = font->width(font->userdata, font->height, - &buffer[offset+sel_begin], sel_len); - label.w = tmp; - - /* draw ending unselected text part */ - label.x += (float)cur_text_width; - label.w -= (float)(cur_text_width); - txt.background = text_background; - txt.text = text; - zr_widget_text(out, label, &buffer[offset+sel_end], - row_len - (sel_len + sel_begin), - &txt, ZR_TEXT_LEFT, font); - - label.x = (float)label_x; - label.w = (float)label_w; - } else { - /* 5.) no selection */ - label.w = text_width; - txt.background = text_background; - txt.text = text; - zr_widget_text(out, label, &buffer[offset], - row_len, &txt, ZR_TEXT_LEFT, font); - } - - glyph_off += glyphs; - text_len -= row_off; - label.y += font->height + style->padding.y; - } - - /* draw the cursor at the end of the string */ - if (box->active && show_cursor) { - if (box->cursor == box->glyphs) { - struct zr_rect cursors; - if (len) label.y -= (font->height + style->padding.y); - - cursors.x = label.x + (float)text_width; - cursors.y = label.y; - cursors.w = style->cursor_size; - cursors.h = label.h; - if (cursor->type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, cursors, &cursor->data.image); - else zr_fill_rect(out, cursors, 0, cursor->data.color); - } - } - zr_push_scissor(out, old_clip); -} - -static void -zr_do_edit_field(zr_flags *state, struct zr_command_buffer *out, struct zr_rect r, - struct zr_edit_box *box, const struct zr_style_edit *style, - int show_cursor, int modifiable, struct zr_input *in, - const struct zr_user_font *font) -{ - char *buffer; - zr_size len; - - int prev_state; - zr_size visible_rows = 0; - zr_size total_rows = 0; - - float total_width = 0; - float total_height = 0; - zr_size row_height = 0; - - ZR_ASSERT(out); - ZR_ASSERT(state); - ZR_ASSERT(font); - ZR_ASSERT(style); - if (!out || !box || !style || !state) - return; - - /* calculate usable field space */ - r.w = ZR_MAX(r.w, 2 * style->padding.x + 2 * style->border); - r.h = ZR_MAX(r.h, font->height + (2 * style->padding.y + 2 * style->border)); - - total_width = r.w - (2 * style->padding.x + 2 * style->border); - total_width -= style->scrollbar_size; - row_height = (zr_size)(font->height + style->padding.y); - - /* check if edit box is big enough to show even a single row */ - visible_rows = (zr_size)(r.h - (2 * style->border+ 2 * style->padding.y)); - visible_rows = (zr_size)((float)visible_rows / (font->height + style->padding.y)); - if (!visible_rows) return; - - /* check if editbox is activated/deactivated */ - prev_state = box->active; - if (in && in->mouse.buttons[ZR_BUTTON_LEFT].clicked && - in->mouse.buttons[ZR_BUTTON_LEFT].down) - box->active = ZR_INBOX(in->mouse.pos.x,in->mouse.pos.y,r.x,r.y,r.w,r.h); - - /* text input handling */ - if (box->active && in && modifiable) - zr_edit_input_behavior(box, in, 1); - - *state = 0, - buffer = zr_edit_box_get(box); - len = zr_edit_box_len_char(box); - { - /* calulate total number of needed rows */ - zr_size glyphs = 0; - zr_size row_off = 0; - zr_size text_len = len; - zr_size offset = 0; - zr_size row_len; - - float space = total_width; - float text_width = 0; - - while (text_len) { - total_rows++; - offset += row_off; - row_off = zr_user_font_glyphs_fitting_in_space(font, - &buffer[offset], text_len, space, &row_len, &glyphs, &text_width, 1); - if (!row_off){ - text_len = 0; - } else text_len -= row_off; - } - total_height = (float)total_rows * (float)row_height; - } - - if (!box->active || (!prev_state && box->active)) { - /* make sure edit box points to the end of the buffer if not active */ - if (total_rows > visible_rows) - box->scrollbar = (float)((total_rows - visible_rows) * row_height); - if (!prev_state && box->active) { - box->cursor = zr_utf_len(buffer, len); - box->sel.begin = box->cursor; - box->sel.end = box->cursor; - } - } - - if ((in && in->keyboard.text_len && total_rows >= visible_rows && box->active) || - box->sel.active || (box->text_inserted && total_rows >= visible_rows)) - { - /* make sure cursor is always in current visible field while writing */ - box->text_inserted = 0; - if (box->cursor == box->glyphs && !box->sel.active) { - /* cursor is at end of text and out of visible frame */ - float row_offset = (float)(total_rows - visible_rows); - box->scrollbar = (font->height + style->padding.x) * row_offset; - } else { - /* cursor is inside text and out of visible frame */ - float text_width; - zr_size cur_row = 0; - zr_size glyphs = 0; - zr_size row_off = 0; - zr_size row_len = 0; - zr_size text_len = len; - zr_size offset = 0, glyph_off = 0; - zr_size cursor = ZR_MIN(box->sel.end, box->sel.begin); - zr_size scroll_offset = (zr_size)(box->scrollbar / (float)row_height); - - /* find cursor row */ - while (text_len) { - offset += row_off; - row_off = zr_user_font_glyphs_fitting_in_space(font, - &buffer[offset], text_len, total_width, &row_len, &glyphs, &text_width, 1); - if ((cursor >= glyph_off && cursor < glyph_off + glyphs) || !row_off) - break; - - glyph_off += glyphs; - text_len -= row_off; - cur_row++; - } - - if (cur_row >= visible_rows && !box->sel.active) { - /* set visible frame to include cursor while writing */ - zr_size row_offset = (cur_row + 1) - visible_rows; - box->scrollbar = (font->height + style->padding.x) * (float)row_offset; - } else if (box->sel.active && scroll_offset > cur_row) { - /* set visible frame to include cursor while selecting */ - zr_size row_offset = (scroll_offset > 0) ? scroll_offset-1: scroll_offset; - box->scrollbar = (font->height + style->padding.x) * (float)row_offset; - } - } - } - if (box->text_inserted) { - /* @NOTE: zr_editbox_add handler: ugly but works */ - box->sel.begin = box->cursor; - box->sel.end = box->cursor; - box->text_inserted = 0; - } - - if (in && show_cursor && in->mouse.buttons[ZR_BUTTON_LEFT].down && box->active) - { - /* TEXT SELECTION */ - const char *visible = buffer; - float xoff = in->mouse.pos.x - (r.x + style->padding.x + style->border); - float yoff = in->mouse.pos.y - (r.y + style->padding.y + style->border); - - int in_space = (xoff >= 0 && xoff < total_width); - int in_region = (box->sel.active && yoff < 0) || - (yoff >= 0 && yoff < total_height); - - if (ZR_INBOX(in->mouse.pos.x, in->mouse.pos.y, r.x, r.y, r.w, r.h) && - in_space && in_region) - { - zr_size row; - zr_size glyph_index = 0, glyph_pos = 0; - zr_size cur_row = 0; - zr_size glyphs = 0; - zr_size row_off = box->glyphs; - zr_size row_len = 0; - zr_size text_len = len; - zr_size offset = 0, glyph_off = 0; - float text_width = 0; - - /* selection beyond the current visible text rows */ - if (yoff < 0 && box->sel.active) { - int off = ((int)yoff + (int)box->scrollbar - (int)row_height); - int next_row = off / (int)row_height; - row = (next_row < 0) ? 0 : (zr_size)next_row; - } else row = (zr_size)((yoff + box->scrollbar)/ - (font->height + style->padding.y)); - - /* find selected row */ - if (text_len) { - while (text_len && cur_row <= row) { - row_off = zr_user_font_glyphs_fitting_in_space(font, - &buffer[offset], text_len, total_width, &row_len, - &glyphs, &text_width, 1); - if (!row_off) break; - - glyph_off += glyphs; - text_len -= row_off; - visible += row_off; - offset += row_off; - cur_row++; - } - glyph_off -= glyphs; - visible -= row_off; - } - - /* find selected glyphs in row */ - if ((text_width + r.x + style->padding.y + style->border) > xoff) { - glyph_pos = zr_user_font_glyph_index_at_pos(font, visible, row_len, xoff); - if (glyph_pos + glyph_off >= box->glyphs) - glyph_index = box->glyphs; - else glyph_index = glyph_off + ZR_MIN(glyph_pos, glyphs-1); - - zr_edit_box_set_cursor(box, glyph_index); - if (!box->sel.active) { - box->sel.active = zr_true; - box->sel.begin = glyph_index; - box->sel.end = glyph_index; - } else { - if (box->sel.begin > glyph_index) { - box->sel.end = glyph_index; - box->sel.active = zr_true; - } - } - } - } else box->sel.active = zr_false; - } else box->sel.active = zr_false; - - { - /* SCROLLBAR */ - struct zr_rect bounds; - float scroll_target; - float scroll_offset; - float scroll_step; - float scroll_inc; - zr_flags ws; - - bounds.x = (r.x + r.w) - (style->scrollbar_size + style->border); - bounds.y = r.y + style->border + style->padding.y; - bounds.w = style->scrollbar_size; - bounds.h = r.h - (2 * style->border + 2 * style->padding.y); - - scroll_offset = box->scrollbar; - scroll_step = total_height * 0.10f; - scroll_inc = total_height * 0.01f; - scroll_target = total_height; - box->scrollbar = zr_do_scrollbarv(&ws, out, bounds, - box->active, scroll_offset, scroll_target, scroll_step, - scroll_inc, &style->scrollbar, in, font); - } - zr_draw_edit_field(out, *state, style, &r, show_cursor, buffer, len, total_width, box, font); -} - -/* =============================================================== - * - * PROPERTY - * - * ===============================================================*/ -enum zr_property_status { - ZR_PROPERTY_DEFAULT, - ZR_PROPERTY_EDIT, - ZR_PROPERTY_DRAG -}; - -enum zr_property_filter { - ZR_FILTER_INT, - ZR_FILTER_FLOAT -}; - -static float -zr_drag_behavior(zr_flags *state, const struct zr_input *in, - struct zr_rect drag, float min, float val, float max, float inc_per_pixel) -{ - int left_mouse_down = in && in->mouse.buttons[ZR_BUTTON_LEFT].down; - int left_mouse_click_in_cursor = in && - zr_input_has_mouse_click_down_in_rect(in, ZR_BUTTON_LEFT, drag, zr_true); - - *state = ZR_WIDGET_STATE_INACTIVE; - if (zr_input_is_mouse_hovering_rect(in, drag)) - *state = ZR_WIDGET_STATE_HOVERED; - - if (left_mouse_down && left_mouse_click_in_cursor) { - float delta, pixels; - pixels = in->mouse.delta.x; - delta = pixels * inc_per_pixel; - val += delta; - val = ZR_CLAMP(min, val, max); - *state = ZR_WIDGET_STATE_ACTIVE; - } - if (*state == ZR_WIDGET_STATE_HOVERED && !zr_input_is_mouse_prev_hovering_rect(in, drag)) - *state |= ZR_WIDGET_STATE_ENTERED; - else if (zr_input_is_mouse_prev_hovering_rect(in, drag)) - *state |= ZR_WIDGET_STATE_LEFT; - return val; -} - -static float -zr_property_behavior(zr_flags *ws, const struct zr_input *in, - struct zr_rect property, struct zr_rect label, struct zr_rect edit, - struct zr_rect empty, int *state, float min, float value, float max, - float step, float inc_per_pixel) -{ - ZR_UNUSED(step); - if (in && *state == ZR_PROPERTY_DEFAULT) { - if (zr_button_behavior(ws, edit, in, ZR_BUTTON_DEFAULT)) - *state = ZR_PROPERTY_EDIT; - else if (zr_input_is_mouse_click_down_in_rect(in, ZR_BUTTON_LEFT, label, zr_true)) - *state = ZR_PROPERTY_DRAG; - else if (zr_input_is_mouse_click_down_in_rect(in, ZR_BUTTON_LEFT, empty, zr_true)) - *state = ZR_PROPERTY_DRAG; - } - if (*state == ZR_PROPERTY_DRAG) { - value = zr_drag_behavior(ws, in, property, min, value, max, inc_per_pixel); - if (!(*ws & ZR_WIDGET_STATE_ACTIVE)) *state = ZR_PROPERTY_DEFAULT; - } - return value; -} - -static void -zr_draw_property(struct zr_command_buffer *out, const struct zr_style_property *style, - const struct zr_rect *bounds, const struct zr_rect *label, zr_flags state, - const char *name, zr_size len, const struct zr_user_font *font) -{ - struct zr_text text; - const struct zr_style_item *background; - - /* select correct background and text color */ - if (state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->active; - text.text = style->label_active; - } else if (state & ZR_WIDGET_STATE_HOVERED) { - background = &style->hover; - text.text = style->label_hover; - } else { - background = &style->normal; - text.text = style->label_normal; - } - - /* draw background */ - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, *bounds, &background->data.image); - text.background = zr_rgba(0,0,0,0); - } else { - text.background = background->data.color; - zr_fill_rect(out, *bounds, style->rounding, style->border_color); - zr_fill_rect(out, zr_shrink_rect(*bounds,style->border), style->rounding, background->data.color); - } - - /* draw label */ - text.padding = zr_vec2(0,0); - zr_widget_text(out, *label, name, len, &text, ZR_TEXT_CENTERED, font); -} - -static float -zr_do_property(zr_flags *ws, - struct zr_command_buffer *out, struct zr_rect property, - const char *name, float min, float val, float max, - float step, float inc_per_pixel, char *buffer, zr_size *len, - int *state, zr_size *cursor, const struct zr_style_property *style, - enum zr_property_filter filter, const struct zr_input *in, - const struct zr_user_font *font) -{ - const zr_filter filters[] = { - zr_filter_decimal, - zr_filter_float - }; - int active, old; - zr_size num_len, name_len; - char string[ZR_MAX_NUMBER_BUFFER]; - zr_size size; - - float property_min; - float property_max; - float property_value; - - char *dst = 0; - zr_size *length; - - struct zr_rect left; - struct zr_rect right; - struct zr_rect label; - struct zr_rect edit; - struct zr_rect empty; - - /* make sure the provided values are correct */ - property_max = ZR_MAX(min, max); - property_min = ZR_MIN(min, max); - property_value = ZR_CLAMP(property_min, val, property_max); - - /* left decrement button */ - left.h = font->height/2; - left.w = left.h; - left.x = property.x + style->border + style->padding.x; - left.y = property.y + style->border + property.h/2.0f - left.h/2; - - /* text label */ - name_len = zr_strlen(name); - size = font->width(font->userdata, font->height, name, name_len); - label.x = left.x + left.w + style->padding.x; - label.w = (float)size + 2 * style->padding.x; - label.y = property.y + style->border; - label.h = property.h - 2 * style->border; - - /* right increment button */ - right.y = left.y; - right.w = left.w; - right.h = left.h; - right.x = property.x + property.w - (right.w + style->padding.x); - - /* edit */ - if (*state == ZR_PROPERTY_EDIT) { - size = font->width(font->userdata, font->height, buffer, *len); - length = len; - dst = buffer; - } else { - zr_ftos(string, property_value); - num_len = zr_string_float_limit(string, ZR_MAX_FLOAT_PRECISION); - size = font->width(font->userdata, font->height, string, num_len); - dst = string; - length = &num_len; - } - edit.w = (float)size + 2 * style->padding.x; - edit.x = right.x - (edit.w + style->padding.x); - edit.y = property.y + style->border + 1; - edit.h = property.h - (2 * style->border + 2); - - /* empty left space activator */ - empty.w = edit.x - (label.x + label.w); - empty.x = label.x + label.w; - empty.y = property.y; - empty.h = property.h; - - /* update property */ - old = (*state == ZR_PROPERTY_EDIT); - property_value = zr_property_behavior(ws, in, property, label, edit, empty, - state, property_min, property_value, property_max, - step, inc_per_pixel); - - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (style->draw) - style->draw(out, style, &property, &label, *ws, name, name_len, font); - else zr_draw_property(out, style, &property, &label, *ws, name, name_len, font); - if (style->draw_end) - style->draw_begin(out, style->userdata); - - /* execute right and left button */ - if (zr_do_button_symbol(ws, out, left, style->sym_left, ZR_BUTTON_DEFAULT, - &style->dec_button, in, font)) - property_value = ZR_CLAMP(min, property_value - step, max); - if (zr_do_button_symbol(ws, out, right, style->sym_right, ZR_BUTTON_DEFAULT, - &style->inc_button, in, font)) - property_value = ZR_CLAMP(min, property_value + step, max); - - active = (*state == ZR_PROPERTY_EDIT); - if (old != ZR_PROPERTY_EDIT && active) { - /* property has been activated so setup buffer */ - zr_memcopy(buffer, dst, *length); - *cursor = zr_utf_len(buffer, *length); - *len = *length; - length = len; - dst = buffer; - } - - *length = zr_do_edit_string(ws, out, edit, dst, *length, ZR_MAX_NUMBER_BUFFER, - &active, cursor, 1, &style->edit, filters[filter], - (*state == ZR_PROPERTY_EDIT) ? in: 0, font); - if (active && zr_input_is_key_pressed(in, ZR_KEY_ENTER)) - active = !active; - - if (old && !active) { - /* property is now not active so convert edit text to value*/ - *state = ZR_PROPERTY_DEFAULT; - buffer[*len] = '\0'; - zr_string_float_limit(buffer, ZR_MAX_FLOAT_PRECISION); - zr_strtof(&property_value, buffer); - property_value = ZR_CLAMP(min, property_value, max); - } - return property_value; -} - -/* =============================================================== - * - * COLOR PICKER - * - * ===============================================================*/ -static int -zr_color_picker_behavior(zr_flags *state, - const struct zr_rect *bounds, const struct zr_rect *matrix, - const struct zr_rect *hue_bar, const struct zr_rect *alpha_bar, - struct zr_color *color, const struct zr_input *in) -{ - float hsva[4]; - int value_changed = 0; - int hsv_changed = 0; - - ZR_ASSERT(state); - ZR_ASSERT(matrix); - ZR_ASSERT(hue_bar); - ZR_ASSERT(color); - - /* color matrix */ - zr_color_hsva_fv(hsva, *color); - if (zr_button_behavior(state, *matrix, in, ZR_BUTTON_REPEATER)) { - hsva[1] = ZR_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); - hsva[2] = 1.0f - ZR_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); - value_changed = hsv_changed = 1; - } - - /* hue bar */ - if (zr_button_behavior(state, *hue_bar, in, ZR_BUTTON_REPEATER)) { - hsva[0] = ZR_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); - value_changed = hsv_changed = 1; - } - /* alpha bar */ - if (alpha_bar) { - if (zr_button_behavior(state, *alpha_bar, in, ZR_BUTTON_REPEATER)) { - hsva[3] = 1.0f - ZR_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); - value_changed = 1; - } - } - - *state = ZR_WIDGET_STATE_INACTIVE; - if (hsv_changed) { - *color = zr_hsva_fv(hsva); - *state = ZR_WIDGET_STATE_ACTIVE; - } - if (value_changed) { - color->a = (zr_byte)(hsva[3] * 255.0f); - *state = ZR_WIDGET_STATE_ACTIVE; - } - if (zr_input_is_mouse_hovering_rect(in, *bounds)) - *state = ZR_WIDGET_STATE_HOVERED; - if (*state == ZR_WIDGET_STATE_HOVERED && !zr_input_is_mouse_prev_hovering_rect(in, *bounds)) - *state |= ZR_WIDGET_STATE_ENTERED; - else if (zr_input_is_mouse_prev_hovering_rect(in, *bounds)) - *state |= ZR_WIDGET_STATE_LEFT; - return value_changed; -} - -static void -zr_draw_color_picker(struct zr_command_buffer *o, const struct zr_rect *matrix, - const struct zr_rect *hue_bar, const struct zr_rect *alpha_bar, - struct zr_color color) -{ - static const struct zr_color black = {0,0,0,255}; - static const struct zr_color white = {255, 255, 255, 255}; - static const struct zr_color black_trans = {0,0,0,0}; - - const float crosshair_size = 7.0f; - struct zr_color temp; - float hsva[4]; - float line_y; - int i; - - ZR_ASSERT(o); - ZR_ASSERT(matrix); - ZR_ASSERT(hue_bar); - ZR_ASSERT(alpha_bar); - - /* draw hue bar */ - zr_color_hsv_fv(hsva, color); - for (i = 0; i < 6; ++i) { - static const struct zr_color hue_colors[] = { - {255, 0, 0, 255}, {255,255,0,255}, {0,255,0,255}, {0, 255,255,255}, - {0,0,255,255}, {255, 0, 255, 255}, {255, 0, 0, 255}}; - zr_fill_rect_multi_color(o, - zr_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, - hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], - hue_colors[i+1], hue_colors[i+1]); - } - line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); - zr_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, - line_y, 1, zr_rgb(255,255,255)); - - /* draw alpha bar */ - if (alpha_bar) { - float alpha = ZR_SATURATE((float)color.a/255.0f); - line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); - - zr_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); - zr_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, - line_y, 1, zr_rgb(255,255,255)); - } - - /* draw color matrix */ - temp = zr_hsv_f(hsva[0], 1.0f, 1.0f); - zr_fill_rect_multi_color(o, *matrix, white, temp, temp, white); - zr_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); - - /* draw cross-hair */ - {struct zr_vec2 p; float S = hsva[1]; float V = hsva[2]; - p.x = (float)(int)(matrix->x + S * matrix->w + 0.5f); - p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h + 0.5f); - zr_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); - zr_stroke_line(o, p.x + crosshair_size, p.y, p.x+2, p.y, 1.0f, white); - zr_stroke_line(o, p.x, p.y + crosshair_size, p.x, p.y+2, 1.0f, zr_rgb(255,255,255)); - zr_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, zr_rgb(255,255,255));} -} - -static int -zr_do_color_picker(zr_flags *state, - struct zr_command_buffer *out, struct zr_color *color, - enum zr_color_picker_format fmt, struct zr_rect bounds, - struct zr_vec2 padding, const struct zr_input *in, - const struct zr_user_font *font) -{ - int ret = 0; - struct zr_rect matrix; - struct zr_rect hue_bar; - struct zr_rect alpha_bar; - float bar_w; - - ZR_ASSERT(out); - ZR_ASSERT(color); - ZR_ASSERT(state); - ZR_ASSERT(font); - if (!out || !color || !state || !font) - return ret; - - bar_w = font->height; - bounds.x += padding.x; - bounds.y += padding.x; - bounds.w -= 2 * padding.x; - bounds.h -= 2 * padding.y; - - matrix.x = bounds.x; - matrix.y = bounds.y; - matrix.h = bounds.h; - matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); - - hue_bar.w = bar_w; - hue_bar.y = bounds.y; - hue_bar.h = matrix.h; - hue_bar.x = matrix.x + matrix.w + padding.x; - - alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; - alpha_bar.y = bounds.y; - alpha_bar.w = bar_w; - alpha_bar.h = matrix.h; - - ret = zr_color_picker_behavior(state, &bounds, &matrix, &hue_bar, - (fmt == ZR_RGBA) ? &alpha_bar:0, color, in); - zr_draw_color_picker(out, &matrix, &hue_bar, (fmt == ZR_RGBA) ? &alpha_bar:0, *color); - return ret; -} - -/* ============================================================== - * - * STYLE - * - * ===============================================================*/ -void zr_style_default(struct zr_context *ctx){zr_style_from_table(ctx, 0);} -#define ZR_COLOR_MAP(ZR_COLOR)\ - ZR_COLOR(ZR_COLOR_TEXT, 175,175,175,255) \ - ZR_COLOR(ZR_COLOR_WINDOW, 45, 45, 45,255) \ - ZR_COLOR(ZR_COLOR_HEADER, 40, 40, 40,255) \ - ZR_COLOR(ZR_COLOR_BORDER, 65, 65, 65,255) \ - ZR_COLOR(ZR_COLOR_BUTTON, 50, 50, 50,255) \ - ZR_COLOR(ZR_COLOR_BUTTON_HOVER, 40, 40, 40,255) \ - ZR_COLOR(ZR_COLOR_BUTTON_ACTIVE, 35, 35, 35,255) \ - ZR_COLOR(ZR_COLOR_TOGGLE, 100,100,100,255) \ - ZR_COLOR(ZR_COLOR_TOGGLE_HOVER, 120,120,120,255) \ - ZR_COLOR(ZR_COLOR_TOGGLE_CURSOR, 45, 45, 45,255) \ - ZR_COLOR(ZR_COLOR_SELECTABLE, 45, 45, 45,255) \ - ZR_COLOR(ZR_COLOR_SELECTABLE_HOVER, 45, 45, 45,255) \ - ZR_COLOR(ZR_COLOR_SELECTABLE_TEXT, 175,175,175,255) \ - ZR_COLOR(ZR_COLOR_SLIDER, 38, 38, 38,255) \ - ZR_COLOR(ZR_COLOR_SLIDER_CURSOR, 100,100,100,255) \ - ZR_COLOR(ZR_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ - ZR_COLOR(ZR_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ - ZR_COLOR(ZR_COLOR_PROPERTY, 38, 38, 38,255) \ - ZR_COLOR(ZR_COLOR_EDIT, 38, 38, 38,255) \ - ZR_COLOR(ZR_COLOR_EDIT_CURSOR, 175,175,175,255) \ - ZR_COLOR(ZR_COLOR_COMBO, 45, 45, 45,255) \ - ZR_COLOR(ZR_COLOR_CHART, 120,120,120,255) \ - ZR_COLOR(ZR_COLOR_CHART_COLOR, 45,45,45,255) \ - ZR_COLOR(ZR_COLOR_CHART_COLOR_HIGHLIGHT,255,0,0,255) \ - ZR_COLOR(ZR_COLOR_SCROLLBAR, 40,40,40,255) \ - ZR_COLOR(ZR_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ - ZR_COLOR(ZR_COLOR_SCROLLBAR_CURSOR_HOVER,120,120,120,255) \ - ZR_COLOR(ZR_COLOR_SCROLLBAR_CURSOR_ACTIVE, 150,150,150,255) \ - ZR_COLOR(ZR_COLOR_TAB_HEADER, 40, 40, 40,255) - -static const struct zr_color -zr_default_color_style[ZR_COLOR_COUNT] = { -#define ZR_COLOR(a,b,c,d,e) {b,c,d,e}, -ZR_COLOR_MAP(ZR_COLOR) -#undef ZR_COLOR -}; - -static const char *zr_color_names[ZR_COLOR_COUNT] = { -#define ZR_COLOR(a,b,c,d,e) #a, -ZR_COLOR_MAP(ZR_COLOR) -#undef ZR_COLOR -}; - -const char *zr_style_color_name(enum zr_style_colors c) -{return zr_color_names[c];} - -struct zr_style_item zr_style_item_image(struct zr_image img) -{struct zr_style_item i; i.type = ZR_STYLE_ITEM_IMAGE; i.data.image = img; return i;} - -struct zr_style_item zr_style_item_color(struct zr_color col) -{struct zr_style_item i; i.type = ZR_STYLE_ITEM_COLOR; i.data.color = col; return i;} - -struct zr_style_item zr_style_item_hide(void) -{struct zr_style_item i; i.type = ZR_STYLE_ITEM_COLOR; i.data.color = zr_rgba(0,0,0,0); return i;} - -void -zr_style_from_table(struct zr_context *ctx, const struct zr_color *table) -{ - struct zr_style *style; - struct zr_style_text *text; - struct zr_style_button *button; - struct zr_style_toggle *toggle; - struct zr_style_selectable *select; - struct zr_style_slider *slider; - struct zr_style_progress *prog; - struct zr_style_scrollbar *scroll; - struct zr_style_edit *edit; - struct zr_style_property *property; - struct zr_style_combo *combo; - struct zr_style_chart *chart; - struct zr_style_tab *tab; - struct zr_style_window *win; - - ZR_ASSERT(ctx); - if (!ctx) return; - style = &ctx->style; - table = (!table) ? zr_default_color_style: table; - - /* default text */ - text = &style->text; - text->color = table[ZR_COLOR_TEXT]; - text->padding = zr_vec2(4,4); - - /* default button */ - button = &style->button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_BUTTON]); - button->hover = zr_style_item_color(table[ZR_COLOR_BUTTON_HOVER]); - button->active = zr_style_item_color(table[ZR_COLOR_BUTTON_ACTIVE]); - button->border_color = table[ZR_COLOR_BORDER]; - button->text_background = table[ZR_COLOR_BUTTON]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(4.0f,4.0f); - button->image_padding = zr_vec2(0.0f,0.0f); - button->touch_padding = zr_vec2(0.0f, 0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 1.0f; - button->rounding = 4.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* contextual button */ - button = &style->contextual_button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_WINDOW]); - button->hover = zr_style_item_color(table[ZR_COLOR_BUTTON_HOVER]); - button->active = zr_style_item_color(table[ZR_COLOR_BUTTON_ACTIVE]); - button->border_color = table[ZR_COLOR_WINDOW]; - button->text_background = table[ZR_COLOR_WINDOW]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(4.0f,4.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* menu button */ - button = &style->menu_button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_WINDOW]); - button->hover = zr_style_item_color(table[ZR_COLOR_WINDOW]); - button->active = zr_style_item_color(table[ZR_COLOR_WINDOW]); - button->border_color = table[ZR_COLOR_WINDOW]; - button->text_background = table[ZR_COLOR_WINDOW]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(4.0f,4.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 1.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* checkbox toggle */ - toggle = &style->checkbox; - zr_zero_struct(*toggle); - toggle->normal = zr_style_item_color(table[ZR_COLOR_TOGGLE]); - toggle->hover = zr_style_item_color(table[ZR_COLOR_TOGGLE_HOVER]); - toggle->active = zr_style_item_color(table[ZR_COLOR_TOGGLE_HOVER]); - toggle->cursor_normal = zr_style_item_color(table[ZR_COLOR_TOGGLE_CURSOR]); - toggle->cursor_hover = zr_style_item_color(table[ZR_COLOR_TOGGLE_CURSOR]); - toggle->userdata = zr_handle_ptr(0); - toggle->text_background = table[ZR_COLOR_WINDOW]; - toggle->text_normal = table[ZR_COLOR_TEXT]; - toggle->text_hover = table[ZR_COLOR_TEXT]; - toggle->text_active = table[ZR_COLOR_TEXT]; - toggle->padding = zr_vec2(4.0f, 4.0f); - toggle->touch_padding = zr_vec2(0,0); - - /* option toggle */ - toggle = &style->option; - zr_zero_struct(*toggle); - toggle->normal = zr_style_item_color(table[ZR_COLOR_TOGGLE]); - toggle->hover = zr_style_item_color(table[ZR_COLOR_TOGGLE_HOVER]); - toggle->active = zr_style_item_color(table[ZR_COLOR_TOGGLE_HOVER]); - toggle->cursor_normal = zr_style_item_color(table[ZR_COLOR_TOGGLE_CURSOR]); - toggle->cursor_hover = zr_style_item_color(table[ZR_COLOR_TOGGLE_CURSOR]); - toggle->userdata = zr_handle_ptr(0); - toggle->text_background = table[ZR_COLOR_WINDOW]; - toggle->text_normal = table[ZR_COLOR_TEXT]; - toggle->text_hover = table[ZR_COLOR_TEXT]; - toggle->text_active = table[ZR_COLOR_TEXT]; - toggle->padding = zr_vec2(4.0f, 4.0f); - toggle->touch_padding = zr_vec2(0,0); - - /* selectable */ - select = &style->selectable; - zr_zero_struct(*select); - select->normal = zr_style_item_color(table[ZR_COLOR_SELECTABLE]); - select->hover = zr_style_item_color(table[ZR_COLOR_SELECTABLE_HOVER]); - select->pressed = zr_style_item_color(table[ZR_COLOR_SELECTABLE_HOVER]); - select->normal_active = zr_style_item_color(table[ZR_COLOR_TEXT]); - select->hover_active = zr_style_item_color(table[ZR_COLOR_TEXT]); - select->pressed_active = zr_style_item_color(table[ZR_COLOR_TEXT]); - select->text_normal = table[ZR_COLOR_TEXT]; - select->text_hover = table[ZR_COLOR_TEXT]; - select->text_pressed = table[ZR_COLOR_TEXT]; - select->text_normal_active = table[ZR_COLOR_SELECTABLE]; - select->text_hover_active = table[ZR_COLOR_SELECTABLE]; - select->text_pressed_active = table[ZR_COLOR_SELECTABLE]; - select->padding = zr_vec2(4.0f,4.0f); - select->touch_padding = zr_vec2(0,0); - select->userdata = zr_handle_ptr(0); - select->rounding = 0.0f; - select->draw_begin = 0; - select->draw = 0; - select->draw_end = 0; - - /* slider */ - slider = &style->slider; - zr_zero_struct(*slider); - slider->normal = zr_style_item_hide(); - slider->hover = zr_style_item_hide(); - slider->active = zr_style_item_hide(); - slider->bar_normal = table[ZR_COLOR_SLIDER]; - slider->bar_hover = table[ZR_COLOR_SLIDER]; - slider->bar_active = table[ZR_COLOR_SLIDER]; - slider->bar_filled = table[ZR_COLOR_SLIDER_CURSOR]; - slider->cursor_normal = zr_style_item_color(table[ZR_COLOR_SLIDER_CURSOR]); - slider->cursor_hover = zr_style_item_color(table[ZR_COLOR_SLIDER_CURSOR_HOVER]); - slider->cursor_active = zr_style_item_color(table[ZR_COLOR_SLIDER_CURSOR_ACTIVE]); - slider->inc_symbol = ZR_SYMBOL_TRIANGLE_RIGHT; - slider->dec_symbol = ZR_SYMBOL_TRIANGLE_LEFT; - slider->cursor_size = zr_vec2(16,16); - slider->padding = zr_vec2(4,4); - slider->spacing = zr_vec2(4,4); - slider->userdata = zr_handle_ptr(0); - slider->show_buttons = zr_false; - slider->bar_height = 8; - slider->rounding = 0; - slider->draw_begin = 0; - slider->draw = 0; - slider->draw_end = 0; - - /* slider buttons */ - button = &style->slider.inc_button; - button->normal = zr_style_item_color(zr_rgb(40,40,40)); - button->hover = zr_style_item_color(zr_rgb(42,42,42)); - button->active = zr_style_item_color(zr_rgb(44,44,44)); - button->border_color = zr_rgb(65,65,65); - button->text_background = zr_rgb(40,40,40); - button->text_normal = zr_rgb(175,175,175); - button->text_hover = zr_rgb(175,175,175); - button->text_active = zr_rgb(175,175,175); - button->padding = zr_vec2(8.0f,8.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 1.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - style->slider.dec_button = style->slider.inc_button; - - /* progressbar */ - prog = &style->progress; - zr_zero_struct(*prog); - prog->normal = zr_style_item_color(table[ZR_COLOR_SLIDER]); - prog->hover = zr_style_item_color(table[ZR_COLOR_SLIDER]); - prog->active = zr_style_item_color(table[ZR_COLOR_SLIDER]); - prog->cursor_normal = zr_style_item_color(table[ZR_COLOR_SLIDER_CURSOR]); - prog->cursor_hover = zr_style_item_color(table[ZR_COLOR_SLIDER_CURSOR_HOVER]); - prog->cursor_active = zr_style_item_color(table[ZR_COLOR_SLIDER_CURSOR_ACTIVE]); - prog->userdata = zr_handle_ptr(0); - prog->padding = zr_vec2(4,4); - prog->rounding = 0; - prog->draw_begin = 0; - prog->draw = 0; - prog->draw_end = 0; - - /* scrollbars */ - scroll = &style->scrollh; - zr_zero_struct(*scroll); - scroll->normal = zr_style_item_color(table[ZR_COLOR_SCROLLBAR]); - scroll->hover = zr_style_item_color(table[ZR_COLOR_SCROLLBAR]); - scroll->active = zr_style_item_color(table[ZR_COLOR_SCROLLBAR]); - scroll->cursor_normal = zr_style_item_color(table[ZR_COLOR_SCROLLBAR_CURSOR]); - scroll->cursor_hover = zr_style_item_color(table[ZR_COLOR_SCROLLBAR_CURSOR_HOVER]); - scroll->cursor_active = zr_style_item_color(table[ZR_COLOR_SCROLLBAR_CURSOR_ACTIVE]); - scroll->dec_symbol = ZR_SYMBOL_CIRCLE_FILLED; - scroll->inc_symbol = ZR_SYMBOL_CIRCLE_FILLED; - scroll->userdata = zr_handle_ptr(0); - scroll->border_color = zr_rgb(65,65,65); - scroll->padding = zr_vec2(4,4); - scroll->show_buttons = zr_false; - scroll->border = 0; - scroll->rounding = 0; - scroll->draw_begin = 0; - scroll->draw = 0; - scroll->draw_end = 0; - style->scrollv = style->scrollh; - - /* scrollbars buttons */ - button = &style->scrollh.inc_button; - button->normal = zr_style_item_color(zr_rgb(40,40,40)); - button->hover = zr_style_item_color(zr_rgb(42,42,42)); - button->active = zr_style_item_color(zr_rgb(44,44,44)); - button->border_color = zr_rgb(65,65,65); - button->text_background = zr_rgb(40,40,40); - button->text_normal = zr_rgb(175,175,175); - button->text_hover = zr_rgb(175,175,175); - button->text_active = zr_rgb(175,175,175); - button->padding = zr_vec2(4.0f,4.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 1.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - style->scrollh.dec_button = style->scrollh.inc_button; - style->scrollv.inc_button = style->scrollh.inc_button; - style->scrollv.dec_button = style->scrollh.inc_button; - - /* edit */ - edit = &style->edit; - zr_zero_struct(*edit); - edit->normal = zr_style_item_color(table[ZR_COLOR_EDIT]); - edit->hover = zr_style_item_color(table[ZR_COLOR_EDIT]); - edit->active = zr_style_item_color(table[ZR_COLOR_EDIT]); - edit->cursor_normal = zr_style_item_color(table[ZR_COLOR_EDIT_CURSOR]); - edit->cursor_hover = zr_style_item_color(table[ZR_COLOR_EDIT_CURSOR]); - edit->cursor_active = zr_style_item_color(table[ZR_COLOR_EDIT_CURSOR]); - edit->border_color = table[ZR_COLOR_BORDER]; - edit->text_normal = table[ZR_COLOR_TEXT]; - edit->text_hover = table[ZR_COLOR_TEXT]; - edit->text_active = table[ZR_COLOR_TEXT]; - edit->selected_normal = table[ZR_COLOR_TEXT]; - edit->selected_hover = table[ZR_COLOR_TEXT]; - edit->selected_text_normal = table[ZR_COLOR_EDIT]; - edit->selected_text_hover = table[ZR_COLOR_EDIT]; - edit->userdata = zr_handle_ptr(0); - edit->padding = zr_vec2(4,4); - edit->cursor_size = 4; - edit->border = 1; - edit->rounding = 0; - edit->draw_begin = 0; - edit->draw = 0; - edit->draw_end = 0; - - /* property */ - property = &style->property; - zr_zero_struct(*property); - property->normal = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - property->hover = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - property->active = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - property->border_color = table[ZR_COLOR_BORDER]; - property->label_normal = table[ZR_COLOR_TEXT]; - property->label_hover = table[ZR_COLOR_TEXT]; - property->label_active = table[ZR_COLOR_TEXT]; - property->sym_left = ZR_SYMBOL_TRIANGLE_LEFT; - property->sym_right = ZR_SYMBOL_TRIANGLE_RIGHT; - property->userdata = zr_handle_ptr(0); - property->padding = zr_vec2(4,4); - property->border = 1; - property->rounding = 10; - property->draw_begin = 0; - property->draw = 0; - property->draw_end = 0; - - /* property buttons */ - button = &style->property.dec_button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - button->hover = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - button->active = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - button->border_color = zr_rgba(0,0,0,0); - button->text_background = table[ZR_COLOR_PROPERTY]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(0.0f,0.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - style->property.inc_button = style->property.dec_button; - - /* property edit */ - edit = &style->property.edit; - zr_zero_struct(*edit); - edit->normal = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - edit->hover = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - edit->active = zr_style_item_color(table[ZR_COLOR_PROPERTY]); - edit->cursor_normal = zr_style_item_color(table[ZR_COLOR_EDIT_CURSOR]); - edit->cursor_hover = zr_style_item_color(table[ZR_COLOR_EDIT_CURSOR]); - edit->cursor_active = zr_style_item_color(table[ZR_COLOR_EDIT_CURSOR]); - edit->border_color = zr_rgba(0,0,0,0); - edit->text_normal = table[ZR_COLOR_TEXT]; - edit->text_hover = table[ZR_COLOR_TEXT]; - edit->text_active = table[ZR_COLOR_TEXT]; - edit->selected_normal = table[ZR_COLOR_TEXT]; - edit->selected_hover = table[ZR_COLOR_TEXT]; - edit->selected_text_normal = table[ZR_COLOR_EDIT]; - edit->selected_text_hover = table[ZR_COLOR_EDIT]; - edit->userdata = zr_handle_ptr(0); - edit->userdata = zr_handle_ptr(0); - edit->padding = zr_vec2(0,0); - edit->cursor_size = 8; - edit->border = 0; - edit->rounding = 0; - edit->draw_begin = 0; - edit->draw = 0; - edit->draw_end = 0; - - /* chart */ - chart = &style->line_chart; - zr_zero_struct(*chart); - chart->background = zr_style_item_color(table[ZR_COLOR_CHART]); - chart->border_color = table[ZR_COLOR_BORDER]; - chart->selected_color = table[ZR_COLOR_CHART_COLOR_HIGHLIGHT]; - chart->color = table[ZR_COLOR_CHART_COLOR]; - chart->border = 0; - chart->rounding = 0; - chart->padding = zr_vec2(4,4); - style->column_chart = *chart; - - /* combo */ - combo = &style->combo; - combo->normal = zr_style_item_color(table[ZR_COLOR_COMBO]); - combo->hover = zr_style_item_color(table[ZR_COLOR_COMBO]); - combo->active = zr_style_item_color(table[ZR_COLOR_COMBO]); - combo->border_color = table[ZR_COLOR_BORDER]; - combo->label_normal = table[ZR_COLOR_TEXT]; - combo->label_hover = table[ZR_COLOR_TEXT]; - combo->label_active = table[ZR_COLOR_TEXT]; - combo->sym_normal = ZR_SYMBOL_TRIANGLE_DOWN; - combo->sym_hover = ZR_SYMBOL_TRIANGLE_DOWN; - combo->sym_active =ZR_SYMBOL_TRIANGLE_DOWN; - combo->content_padding = zr_vec2(4,4); - combo->button_padding = zr_vec2(0,4); - combo->spacing = zr_vec2(4,0); - combo->border = 1; - combo->rounding = 0; - - /* combo button */ - button = &style->combo.button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_COMBO]); - button->hover = zr_style_item_color(table[ZR_COLOR_COMBO]); - button->active = zr_style_item_color(table[ZR_COLOR_COMBO]); - button->border_color = zr_rgba(0,0,0,0); - button->text_background = table[ZR_COLOR_COMBO]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(2.0f,2.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* tab */ - tab = &style->tab; - tab->background = zr_style_item_color(table[ZR_COLOR_TAB_HEADER]); - tab->border_color = table[ZR_COLOR_BORDER]; - tab->text = table[ZR_COLOR_TEXT]; - tab->sym_minimize = ZR_SYMBOL_TRIANGLE_DOWN; - tab->sym_maximize = ZR_SYMBOL_TRIANGLE_RIGHT; - tab->border = 1; - tab->rounding = 0; - tab->padding = zr_vec2(4,4); - tab->spacing = zr_vec2(4,4); - - /* tab button */ - button = &style->tab.tab_button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_TAB_HEADER]); - button->hover = zr_style_item_color(table[ZR_COLOR_TAB_HEADER]); - button->active = zr_style_item_color(table[ZR_COLOR_TAB_HEADER]); - button->border_color = zr_rgba(0,0,0,0); - button->text_background = table[ZR_COLOR_TAB_HEADER]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(2.0f,2.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* node button */ - button = &style->tab.node_button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_WINDOW]); - button->hover = zr_style_item_color(table[ZR_COLOR_WINDOW]); - button->active = zr_style_item_color(table[ZR_COLOR_WINDOW]); - button->border_color = zr_rgba(0,0,0,0); - button->text_background = table[ZR_COLOR_TAB_HEADER]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(2.0f,2.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* window header */ - win = &style->window; - win->header.align = ZR_HEADER_RIGHT; - win->header.close_symbol = ZR_SYMBOL_X; - win->header.minimize_symbol = ZR_SYMBOL_MINUS; - win->header.maximize_symbol = ZR_SYMBOL_PLUS; - win->header.normal = zr_style_item_color(table[ZR_COLOR_HEADER]); - win->header.hover = zr_style_item_color(table[ZR_COLOR_HEADER]); - win->header.active = zr_style_item_color(table[ZR_COLOR_HEADER]); - win->header.label_normal = table[ZR_COLOR_TEXT]; - win->header.label_hover = table[ZR_COLOR_TEXT]; - win->header.label_active = table[ZR_COLOR_TEXT]; - win->header.label_padding = zr_vec2(4,4); - win->header.padding = zr_vec2(4,4); - win->header.spacing = zr_vec2(0,0); - - /* window header close button */ - button = &style->window.header.close_button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_HEADER]); - button->hover = zr_style_item_color(table[ZR_COLOR_HEADER]); - button->active = zr_style_item_color(table[ZR_COLOR_HEADER]); - button->border_color = zr_rgba(0,0,0,0); - button->text_background = table[ZR_COLOR_HEADER]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(0.0f,0.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* window header minimize button */ - button = &style->window.header.minimize_button; - zr_zero_struct(*button); - button->normal = zr_style_item_color(table[ZR_COLOR_HEADER]); - button->hover = zr_style_item_color(table[ZR_COLOR_HEADER]); - button->active = zr_style_item_color(table[ZR_COLOR_HEADER]); - button->border_color = zr_rgba(0,0,0,0); - button->text_background = table[ZR_COLOR_HEADER]; - button->text_normal = table[ZR_COLOR_TEXT]; - button->text_hover = table[ZR_COLOR_TEXT]; - button->text_active = table[ZR_COLOR_TEXT]; - button->padding = zr_vec2(0.0f,0.0f); - button->touch_padding = zr_vec2(0.0f,0.0f); - button->userdata = zr_handle_ptr(0); - button->text_alignment = ZR_TEXT_CENTERED; - button->border = 0.0f; - button->rounding = 0.0f; - button->draw_begin = 0; - button->draw_end = 0; - - /* window */ - win->background = table[ZR_COLOR_WINDOW]; - win->fixed_background = zr_style_item_color(table[ZR_COLOR_WINDOW]); - win->border_color = table[ZR_COLOR_BORDER]; - win->combo_border_color = table[ZR_COLOR_BORDER]; - win->contextual_border_color = table[ZR_COLOR_BORDER]; - win->menu_border_color = table[ZR_COLOR_BORDER]; - win->group_border_color = table[ZR_COLOR_BORDER]; - win->tooltip_border_color = table[ZR_COLOR_BORDER]; - win->scaler = zr_style_item_color(table[ZR_COLOR_TEXT]); - win->footer_padding = zr_vec2(4,4); - win->rounding = 0.0f; - win->scaler_size = zr_vec2(16,16); - win->padding = zr_vec2(8,8); - win->spacing = zr_vec2(4,4); - win->scrollbar_size = zr_vec2(10,10); - win->min_size = zr_vec2(64,64); - win->combo_border = 1.0f; - win->contextual_border = 1.0f; - win->menu_border = 1.0f; - win->group_border = 1.0f; - win->tooltip_border = 1.0f; - win->border = 2.0f; -} - -void -zr_style_set_font(struct zr_context *ctx, const struct zr_user_font *font) -{ - struct zr_style *style; - ZR_ASSERT(ctx); - if (!ctx) return; - style = &ctx->style; - style->font = *font; -} - -/* =============================================================== - * - * POOL - * - * ===============================================================*/ -struct zr_table { - unsigned int seq; - zr_hash keys[ZR_VALUE_PAGE_CAPACITY]; - zr_uint values[ZR_VALUE_PAGE_CAPACITY]; - struct zr_table *next, *prev; -}; - -union zr_page_data { - struct zr_table tbl; - struct zr_window win; -}; - -struct zr_window_page { - unsigned size; - struct zr_window_page *next; - union zr_page_data win[1]; -}; - -struct zr_pool { - struct zr_allocator alloc; - enum zr_allocation_type type; - unsigned int page_count; - struct zr_window_page *pages; - unsigned capacity; - zr_size size; - zr_size cap; -}; - -static void -zr_pool_init(struct zr_pool *pool, struct zr_allocator *alloc, - unsigned int capacity) -{ - zr_zero(pool, sizeof(*pool)); - pool->alloc = *alloc; - pool->capacity = capacity; - pool->pages = 0; - pool->type = ZR_BUFFER_DYNAMIC; -} - -static void -zr_pool_free(struct zr_pool *pool) -{ - struct zr_window_page *next; - struct zr_window_page *iter = pool->pages; - if (!pool) return; - if (pool->type == ZR_BUFFER_FIXED) return; - while (iter) { - next = iter->next; - pool->alloc.free(pool->alloc.userdata, iter); - iter = next; - } - pool->alloc.free(pool->alloc.userdata, pool); -} - -static void -zr_pool_init_fixed(struct zr_pool *pool, void *memory, zr_size size) -{ - zr_zero(pool, sizeof(*pool)); - /* make sure pages have correct granularity to at least fit one page into memory */ - if (size < sizeof(struct zr_window_page) + ZR_POOL_DEFAULT_CAPACITY * sizeof(struct zr_window)) - pool->capacity = (unsigned)(size - sizeof(struct zr_window_page)) / sizeof(struct zr_window); - else pool->capacity = ZR_POOL_DEFAULT_CAPACITY; - pool->pages = (struct zr_window_page*)memory; - pool->type = ZR_BUFFER_FIXED; - pool->size = size; -} - -static void* -zr_pool_alloc(struct zr_pool *pool) -{ - if (!pool->pages || pool->pages->size >= pool->capacity) { - /* allocate new page */ - struct zr_window_page *page; - if (pool->type == ZR_BUFFER_FIXED) { - if (!pool->pages) { - ZR_ASSERT(pool->pages); - return 0; - } - ZR_ASSERT(pool->pages->size < pool->capacity); - return 0; - } else { - zr_size size = sizeof(struct zr_window_page); - size += ZR_POOL_DEFAULT_CAPACITY * sizeof(union zr_page_data); - page = (struct zr_window_page*)pool->alloc.alloc(pool->alloc.userdata, size); - page->size = 0; - page->next = pool->pages; - pool->pages = page; - } - } - return &pool->pages->win[pool->pages->size++]; -} - -/* =============================================================== - * - * CONTEXT - * - * ===============================================================*/ -static void zr_remove_window(struct zr_context*, struct zr_window*); -static void* zr_create_window(struct zr_context *ctx); -static void zr_free_window(struct zr_context *ctx, struct zr_window *win); -static void zr_free_table(struct zr_context *ctx, struct zr_table *tbl); -static void zr_remove_table(struct zr_window *win, struct zr_table *tbl); - -static void -zr_setup(struct zr_context *ctx, const struct zr_user_font *font) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(font); - if (!ctx || !font) return; - zr_zero_struct(*ctx); - zr_style_default(ctx); - ctx->style.font = *font; -#if ZR_COMPILE_WITH_VERTEX_BUFFER - zr_draw_list_init(&ctx->draw_list); -#endif -} - -#if ZR_COMPILE_WITH_DEFAULT_ALLOCATOR -int -zr_init_default(struct zr_context *ctx, const struct zr_user_font *font) -{ - struct zr_allocator alloc; - alloc.userdata.ptr = 0; - alloc.alloc = zr_malloc; - alloc.free = zr_mfree; - return zr_init(ctx, &alloc, font); -} -#endif - -int -zr_init_fixed(struct zr_context *ctx, void *memory, zr_size size, - const struct zr_user_font *font) -{ - ZR_ASSERT(memory); - if (!memory) return 0; - zr_setup(ctx, font); - zr_buffer_init_fixed(&ctx->memory, memory, size); - ctx->pool = 0; - return 1; -} - -int -zr_init_custom(struct zr_context *ctx, struct zr_buffer *cmds, - struct zr_buffer *pool, const struct zr_user_font *font) -{ - ZR_ASSERT(cmds); - ZR_ASSERT(pool); - if (!cmds || !pool) return 0; - zr_setup(ctx, font); - ctx->memory = *cmds; - if (pool->type == ZR_BUFFER_FIXED) { - /* take memory from buffer and alloc fixed pool */ - void *memory = pool->memory.ptr; - zr_size size = pool->memory.size; - ctx->pool = memory; - ZR_ASSERT(size > sizeof(struct zr_pool)); - size -= sizeof(struct zr_pool); - zr_pool_init_fixed((struct zr_pool*)ctx->pool, - (void*)((zr_byte*)ctx->pool+sizeof(struct zr_pool)), size); - } else { - /* create dynamic pool from buffer allocator */ - struct zr_allocator *alloc = &pool->pool; - ctx->pool = alloc->alloc(alloc->userdata, sizeof(struct zr_pool)); - zr_pool_init((struct zr_pool*)ctx->pool, alloc, ZR_POOL_DEFAULT_CAPACITY); - } - return 1; -} - -int -zr_init(struct zr_context *ctx, struct zr_allocator *alloc, - const struct zr_user_font *font) -{ - ZR_ASSERT(alloc); - if (!alloc) return 0; - zr_setup(ctx, font); - zr_buffer_init(&ctx->memory, alloc, ZR_DEFAULT_COMMAND_BUFFER_SIZE); - ctx->pool = alloc->alloc(alloc->userdata, sizeof(struct zr_pool)); - zr_pool_init((struct zr_pool*)ctx->pool, alloc, ZR_POOL_DEFAULT_CAPACITY); - return 1; -} - -#if ZR_COMPILE_WITH_COMMAND_USERDATA -void -zr_set_user_data(struct zr_context *ctx, zr_handle handle) -{ - if (!ctx) return; - ctx->userdata = handle; - if (ctx->current) - ctx->current->buffer.userdata = handle; -} -#endif - -void -zr_free(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - if (!ctx) return; - zr_buffer_free(&ctx->memory); - if (ctx->pool) zr_pool_free((struct zr_pool*)ctx->pool); - - zr_zero(&ctx->input, sizeof(ctx->input)); - zr_zero(&ctx->style, sizeof(ctx->style)); - zr_zero(&ctx->memory, sizeof(ctx->memory)); - - ctx->seq = 0; - ctx->pool = 0; - ctx->build = 0; - ctx->begin = 0; - ctx->end = 0; - ctx->active = 0; - ctx->current = 0; - ctx->freelist = 0; - ctx->count = 0; -} - -void -zr_clear(struct zr_context *ctx) -{ - struct zr_window *iter; - struct zr_window *next; - ZR_ASSERT(ctx); - - if (!ctx) return; - if (ctx->pool) - zr_buffer_clear(&ctx->memory); - else zr_buffer_reset(&ctx->memory, ZR_BUFFER_FRONT); - - ctx->build = 0; - ctx->memory.calls = 0; -#if ZR_COMPILE_WITH_VERTEX_BUFFER - zr_draw_list_clear(&ctx->draw_list); -#endif - - /* garbage collector */ - iter = ctx->begin; - while (iter) { - /* make sure minimized windows do not get removed */ - if (iter->flags & ZR_WINDOW_MINIMIZED) { - iter = iter->next; - continue; - } - - /* free unused popup windows */ - if (iter->popup.win && iter->popup.win->seq != ctx->seq) { - zr_free_window(ctx, iter->popup.win); - iter->popup.win = 0; - } - - {struct zr_table *n, *it = iter->tables; - while (it) { - /* remove unused window state tables */ - n = it->next; - if (it->seq != ctx->seq) { - zr_remove_table(iter, it); - zr_zero(it, sizeof(union zr_page_data)); - zr_free_table(ctx, it); - if (it == iter->tables) - iter->tables = n; - } - it = n; - }} - - /* window itself is not used anymore so free */ - if (iter->seq != ctx->seq || iter->flags & ZR_WINDOW_HIDDEN) { - next = iter->next; - zr_remove_window(ctx, iter); - zr_free_window(ctx, iter); - iter = next; - } else iter = iter->next; - } - ctx->seq++; -} - -/* ---------------------------------------------------------------- - * - * BUFFERING - * - * ---------------------------------------------------------------*/ -static void -zr_start(struct zr_context *ctx, struct zr_window *win) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(win); - if (!ctx || !win) return; - win->buffer.begin = ctx->memory.allocated; - win->buffer.end = win->buffer.begin; - win->buffer.last = win->buffer.begin; - win->buffer.clip = zr_null_rect; -} - -static void -zr_start_popup(struct zr_context *ctx, struct zr_window *win) -{ - struct zr_popup_buffer *buf; - struct zr_panel *iter; - ZR_ASSERT(ctx); - ZR_ASSERT(win); - if (!ctx || !win) return; - - /* make sure to use the correct popup buffer*/ - iter = win->layout; - while (iter->parent) - iter = iter->parent; - - /* save buffer fill state for popup */ - buf = &iter->popup_buffer; - buf->begin = win->buffer.end; - buf->end = win->buffer.end; - buf->parent = win->buffer.last; - buf->last = buf->begin; - buf->active = zr_true; -} - -static void -zr_finish_popup(struct zr_context *ctx, struct zr_window *win) -{ - struct zr_popup_buffer *buf; - struct zr_panel *iter; - ZR_ASSERT(ctx); - ZR_ASSERT(win); - if (!ctx || !win) return; - - /* make sure to use the correct popup buffer*/ - iter = win->layout; - while (iter->parent) - iter = iter->parent; - - buf = &iter->popup_buffer; - buf->last = win->buffer.last; - buf->end = win->buffer.end; -} - -static void -zr_finish(struct zr_context *ctx, struct zr_window *win) -{ - struct zr_popup_buffer *buf; - struct zr_command *parent_last; - struct zr_command *sublast; - struct zr_command *last; - void *memory; - - ZR_ASSERT(ctx); - ZR_ASSERT(win); - if (!ctx || !win) return; - win->buffer.end = ctx->memory.allocated; - if (!win->layout->popup_buffer.active) return; - - /* frome here this case is for popup windows */ - buf = &win->layout->popup_buffer; - memory = ctx->memory.memory.ptr; - - /* redirect the sub-window buffer to the end of the window command buffer */ - parent_last = zr_ptr_add(struct zr_command, memory, buf->parent); - sublast = zr_ptr_add(struct zr_command, memory, buf->last); - last = zr_ptr_add(struct zr_command, memory, win->buffer.last); - - parent_last->next = buf->end; - sublast->next = last->next; - last->next = buf->begin; - win->buffer.last = buf->last; - win->buffer.end = buf->end; - buf->active = zr_false; -} - -static void -zr_build(struct zr_context *ctx) -{ - struct zr_window *iter; - struct zr_window *next; - struct zr_command *cmd; - zr_byte *buffer; - - iter = ctx->begin; - buffer = (zr_byte*)ctx->memory.memory.ptr; - while (iter != 0) { - next = iter->next; - if (iter->buffer.last == iter->buffer.begin || (iter->flags & ZR_WINDOW_HIDDEN)) { - iter = next; - continue; - } - cmd = zr_ptr_add(struct zr_command, buffer, iter->buffer.last); - while (next && ((next->buffer.last == next->buffer.begin) || - (next->flags & ZR_WINDOW_HIDDEN))) - next = next->next; /* skip empty command buffers */ - - if (next) { - cmd->next = next->buffer.begin; - } else cmd->next = ctx->memory.allocated; - iter = next; - } -} - -const struct zr_command* -zr__begin(struct zr_context *ctx) -{ - struct zr_window *iter; - zr_byte *buffer; - ZR_ASSERT(ctx); - if (!ctx) return 0; - if (!ctx->count) return 0; - - /* build one command list out of all windows */ - buffer = (zr_byte*)ctx->memory.memory.ptr; - if (!ctx->build) { - zr_build(ctx); - ctx->build = zr_true; - } - - iter = ctx->begin; - while (iter && ((iter->buffer.begin == iter->buffer.end) || (iter->flags & ZR_WINDOW_HIDDEN))) - iter = iter->next; - if (!iter) return 0; - return zr_ptr_add_const(struct zr_command, buffer, iter->buffer.begin); -} - -const struct zr_command* -zr__next(struct zr_context *ctx, const struct zr_command *cmd) -{ - zr_byte *buffer; - const struct zr_command *next; - ZR_ASSERT(ctx); - if (!ctx || !cmd || !ctx->count) return 0; - if (cmd->next >= ctx->memory.allocated) return 0; - buffer = (zr_byte*)ctx->memory.memory.ptr; - next = zr_ptr_add_const(struct zr_command, buffer, cmd->next); - return next; -} - -/* ---------------------------------------------------------------- - * - * TABLES - * - * ---------------------------------------------------------------*/ -static struct zr_table* -zr_create_table(struct zr_context *ctx) -{ - void *tbl = (void*)zr_create_window(ctx); - zr_zero(tbl, sizeof(struct zr_table)); - return (struct zr_table*)tbl; -} - -static void -zr_free_table(struct zr_context *ctx, struct zr_table *tbl) -{zr_free_window(ctx, (struct zr_window*)tbl);} - -static void -zr_push_table(struct zr_window *win, struct zr_table *tbl) -{ - if (!win->tables) { - win->tables = tbl; - tbl->next = 0; - tbl->prev = 0; - win->table_count = 1; - win->table_size = 1; - return; - } - win->tables->prev = tbl; - tbl->next = win->tables; - tbl->prev = 0; - win->tables = tbl; - win->table_count++; - win->table_size = 0; -} - -static void -zr_remove_table(struct zr_window *win, struct zr_table *tbl) -{ - if (win->tables == tbl) - win->tables = tbl->next; - if (tbl->next) - tbl->next->prev = tbl->prev; - if (tbl->prev) - tbl->prev->next = tbl->next; - tbl->next = 0; - tbl->prev = 0; -} - -static zr_uint* -zr_add_value(struct zr_context *ctx, struct zr_window *win, - zr_hash name, zr_uint value) -{ - if (!win->tables || win->table_size == ZR_VALUE_PAGE_CAPACITY) { - struct zr_table *tbl = zr_create_table(ctx); - zr_push_table(win, tbl); - } - win->tables->seq = win->seq; - win->tables->keys[win->table_size] = name; - win->tables->values[win->table_size] = value; - return &win->tables->values[win->table_size++]; -} - -static zr_uint* -zr_find_value(struct zr_window *win, zr_hash name) -{ - unsigned short size = win->table_size; - struct zr_table *iter = win->tables; - while (iter) { - unsigned short i = 0; - for (i = 0; i < size; ++i) { - if (iter->keys[i] == name) { - iter->seq = win->seq; - return &iter->values[i]; - } - } - size = ZR_VALUE_PAGE_CAPACITY; - iter = iter->next; - } - return 0; -} - -/* ---------------------------------------------------------------- - * - * WINDOW - * - * ---------------------------------------------------------------*/ -static int zr_panel_begin(struct zr_context *ctx, const char *title); -static void zr_panel_end(struct zr_context *ctx); - -static void* -zr_create_window(struct zr_context *ctx) -{ - struct zr_window *win = 0; - if (ctx->freelist) { - /* unlink window from free list */ - win = ctx->freelist; - ctx->freelist = win->next; - } else if (ctx->pool) { - /* allocate window from memory pool */ - win = (struct zr_window*) zr_pool_alloc((struct zr_pool*)ctx->pool); - ZR_ASSERT(win); - if (!win) return 0; - } else { - /* allocate new window from the back of the fixed size memory buffer */ - static const zr_size size = sizeof(union zr_page_data); - static const zr_size align = ZR_ALIGNOF(union zr_page_data); - win = (struct zr_window*)zr_buffer_alloc(&ctx->memory, ZR_BUFFER_BACK, size, align); - ZR_ASSERT(win); - if (!win) return 0; - } - zr_zero(win, sizeof(*win)); - win->next = 0; - win->prev = 0; - win->seq = ctx->seq; - return win; -} - -static void -zr_free_window(struct zr_context *ctx, struct zr_window *win) -{ - /* unlink windows from list */ - struct zr_table *n, *it = win->tables; - if (win->popup.win) { - zr_free_window(ctx, win->popup.win); - win->popup.win = 0; - } - - win->next = 0; - win->prev = 0; - - while (it) { - /*free window state tables */ - n = it->next; - if (it->seq != ctx->seq) { - zr_remove_table(win, it); - zr_free_table(ctx, it); - if (it == win->tables) - win->tables = n; - } - it = n; - } - - /* link windows into freelist */ - if (!ctx->freelist) { - ctx->freelist = win; - } else { - win->next = ctx->freelist; - ctx->freelist = win; - } -} - -static struct zr_window* -zr_find_window(struct zr_context *ctx, zr_hash hash) -{ - struct zr_window *iter; - iter = ctx->begin; - while (iter) { - ZR_ASSERT(iter != iter->next); - if (iter->name == hash) - return iter; - iter = iter->next; - } - return 0; -} - -static void -zr_insert_window(struct zr_context *ctx, struct zr_window *win) -{ - const struct zr_window *iter; - struct zr_window *end; - ZR_ASSERT(ctx); - ZR_ASSERT(win); - if (!win || !ctx) return; - - iter = ctx->begin; - while (iter) { - ZR_ASSERT(iter != iter->next); - ZR_ASSERT(iter != win); - if (iter == win) return; - iter = iter->next; - } - - if (!ctx->begin) { - win->next = 0; - win->prev = 0; - ctx->begin = win; - ctx->end = win; - ctx->count = 1; - return; - } - - end = ctx->end; - end->flags |= ZR_WINDOW_ROM; - end->next = win; - win->prev = ctx->end; - win->next = 0; - ctx->end = win; - ctx->count++; - - ctx->active = ctx->end; - ctx->end->flags &= ~(zr_flags)ZR_WINDOW_ROM; -} - -static void -zr_remove_window(struct zr_context *ctx, struct zr_window *win) -{ - if (win == ctx->begin || win == ctx->end) { - if (win == ctx->begin) { - ctx->begin = win->next; - if (win->next) - win->next->prev = 0; - } - if (win == ctx->end) { - ctx->end = win->prev; - if (win->prev) - win->prev->next = 0; - } - } else { - if (win->next) - win->next->prev = win->prev; - if (win->prev) - win->prev->next = win->next; - } - if (win == ctx->active || !ctx->active) { - ctx->active = ctx->end; - if (ctx->end) - ctx->end->flags &= ~(zr_flags)ZR_WINDOW_ROM; - } - win->next = 0; - win->prev = 0; - ctx->count--; -} - -int -zr_begin(struct zr_context *ctx, struct zr_panel *layout, const char *title, - struct zr_rect bounds, zr_flags flags) -{ - struct zr_window *win; - struct zr_style *style; - zr_hash title_hash; - int title_len; - int ret = 0; - - ZR_ASSERT(ctx); - ZR_ASSERT(!ctx->current && "if this triggers you missed a `zr_end` call"); - if (!ctx || ctx->current || !title) - return 0; - - /* find or create window */ - style = &ctx->style; - title_len = (int)zr_strlen(title); - title_hash = zr_murmur_hash(title, (int)title_len, ZR_WINDOW_TITLE); - win = zr_find_window(ctx, title_hash); - if (!win) { - win = (struct zr_window*)zr_create_window(ctx); - zr_insert_window(ctx, win); - zr_command_buffer_init(&win->buffer, &ctx->memory, ZR_CLIPPING_ON); - ZR_ASSERT(win); - if (!win) return 0; - - win->flags = flags; - win->bounds = bounds; - win->name = title_hash; - win->popup.win = 0; - - } else { - /* update public window flags */ - win->flags &= ~(zr_flags)(ZR_WINDOW_PRIVATE-1); - win->flags |= flags; - win->seq++; - } - if (win->flags & ZR_WINDOW_HIDDEN) { - ctx->current = win; - return 0; - } - - /* overlapping window */ - if (!(win->flags & ZR_WINDOW_SUB) && !(win->flags & ZR_WINDOW_HIDDEN)) - { - int inpanel, ishovered; - const struct zr_window *iter = win; - - /* This is so terrible but neccessary for minimized windows. The difference - * lies in the size of the window. But it is not possible to get the size - * without cheating because you do not have the information at this point. - * Even worse this is wrong since windows could have different window heights. - * I leave it in for now since I otherwise loose my mind. */ - float h = ctx->style.font.height + 2 * style->window.header.padding.y; - - /* activate window if hovered and no other window is overlapping this window */ - zr_start(ctx, win); - inpanel = zr_input_mouse_clicked(&ctx->input, ZR_BUTTON_LEFT, win->bounds); - ishovered = zr_input_is_mouse_hovering_rect(&ctx->input, win->bounds); - if ((win != ctx->active) && ishovered) { - iter = win->next; - while (iter) { - if (!(iter->flags & ZR_WINDOW_MINIMIZED)) { - if (ZR_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, - iter->bounds.x, iter->bounds.y, iter->bounds.w, iter->bounds.h) && - !(iter->flags & ZR_WINDOW_HIDDEN)) - break; - } else { - if (ZR_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, - iter->bounds.x, iter->bounds.y, iter->bounds.w, h) && - !(iter->flags & ZR_WINDOW_HIDDEN)) - break; - } - if (iter->popup.win && iter->popup.active && !(iter->flags & ZR_WINDOW_HIDDEN) && - ZR_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, - iter->popup.win->bounds.x, iter->popup.win->bounds.y, - iter->popup.win->bounds.w, iter->popup.win->bounds.h)) - break; - iter = iter->next; - } - } - - /* activate window if clicked */ - if (iter && inpanel && (win != ctx->end)) { - iter = win->next; - while (iter) { - /* try to find a panel with higher priorty in the same position */ - if (!(iter->flags & ZR_WINDOW_MINIMIZED)) { - if (ZR_INBOX(ctx->input.mouse.prev.x, ctx->input.mouse.prev.y, iter->bounds.x, - iter->bounds.y, iter->bounds.w, iter->bounds.h) && - !(iter->flags & ZR_WINDOW_HIDDEN)) - break; - } else { - if (ZR_INBOX(ctx->input.mouse.prev.x, ctx->input.mouse.prev.y, iter->bounds.x, - iter->bounds.y, iter->bounds.w, h) && - !(iter->flags & ZR_WINDOW_HIDDEN)) - break; - } - if (iter->popup.win && iter->popup.active && !(iter->flags & ZR_WINDOW_HIDDEN) && - ZR_INTERSECT(win->bounds.x, win->bounds.y, win->bounds.w, win->bounds.h, - iter->popup.win->bounds.x, iter->popup.win->bounds.y, - iter->popup.win->bounds.w, iter->popup.win->bounds.h)) - break; - iter = iter->next; - } - } - - if (!iter && ctx->end != win) { - /* current window is active in that position so transfer to top - * at the highest priority in stack */ - zr_remove_window(ctx, win); - zr_insert_window(ctx, win); - - win->flags &= ~(zr_flags)ZR_WINDOW_ROM; - ctx->active = win; - } - if (ctx->end != win) - win->flags |= ZR_WINDOW_ROM; - } - - win->layout = layout; - ctx->current = win; - ret = zr_panel_begin(ctx, title); - layout->offset = &win->scrollbar; - return ret; -} - -void -zr_end(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current) return; - if (ctx->current->flags & ZR_WINDOW_HIDDEN) { - ctx->current = 0; - return; - } - zr_panel_end(ctx); - ctx->current = 0; - ctx->last_widget_state = 0; -} - -struct zr_rect -zr_window_get_bounds(const struct zr_context *ctx) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return zr_rect(0,0,0,0); - return ctx->current->bounds; -} -struct zr_vec2 -zr_window_get_position(const struct zr_context *ctx) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return zr_vec2(0,0); - return zr_vec2(ctx->current->bounds.x, ctx->current->bounds.y); -} - -struct zr_vec2 -zr_window_get_size(const struct zr_context *ctx) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return zr_vec2(0,0); - return zr_vec2(ctx->current->bounds.w, ctx->current->bounds.h); -} - -float -zr_window_get_width(const struct zr_context *ctx) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return 0; - return ctx->current->bounds.w; -} - -float -zr_window_get_height(const struct zr_context *ctx) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return 0; - return ctx->current->bounds.h; -} - -struct zr_rect -zr_window_get_content_region(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return zr_rect(0,0,0,0); - return ctx->current->layout->clip; -} - -struct zr_vec2 -zr_window_get_content_region_min(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current) return zr_vec2(0,0); - return zr_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y); -} - -struct zr_vec2 -zr_window_get_content_region_max(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current) return zr_vec2(0,0); - return zr_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w, - ctx->current->layout->clip.y + ctx->current->layout->clip.h); -} - -struct zr_vec2 -zr_window_get_content_region_size(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current) return zr_vec2(0,0); - return zr_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h); -} - -struct zr_command_buffer* -zr_window_get_canvas(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current) return 0; - return &ctx->current->buffer; -} - -struct zr_panel* -zr_window_get_panel(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return 0; - return ctx->current->layout; -} - -int -zr_window_has_focus(const struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current) return 0; - return ctx->current == ctx->active; -} - -int -zr_window_is_hovered(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return 0; - return zr_input_is_mouse_hovering_rect(&ctx->input, ctx->current->bounds); -} - -int -zr_window_is_any_hovered(struct zr_context *ctx) -{ - struct zr_window *iter; - ZR_ASSERT(ctx); - if (!ctx) return 0; - iter = ctx->begin; - while (iter) { - if (zr_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) - return 1; - iter = iter->next; - } - return 0; -} - -int -zr_window_is_collapsed(struct zr_context *ctx, const char *name) -{ - int title_len; - zr_hash title_hash; - struct zr_window *win; - ZR_ASSERT(ctx); - if (!ctx) return 0; - - title_len = (int)zr_strlen(name); - title_hash = zr_murmur_hash(name, (int)title_len, ZR_WINDOW_TITLE); - win = zr_find_window(ctx, title_hash); - if (!win) return 0; - return win->flags & ZR_WINDOW_MINIMIZED; -} - -int -zr_window_is_closed(struct zr_context *ctx, const char *name) -{ - int title_len; - zr_hash title_hash; - struct zr_window *win; - ZR_ASSERT(ctx); - if (!ctx) return 1; - - title_len = (int)zr_strlen(name); - title_hash = zr_murmur_hash(name, (int)title_len, ZR_WINDOW_TITLE); - win = zr_find_window(ctx, title_hash); - if (!win) return 1; - return (win->flags & ZR_WINDOW_HIDDEN); -} - -int -zr_window_is_active(struct zr_context *ctx, const char *name) -{ - int title_len; - zr_hash title_hash; - struct zr_window *win; - ZR_ASSERT(ctx); - if (!ctx) return 0; - - title_len = (int)zr_strlen(name); - title_hash = zr_murmur_hash(name, (int)title_len, ZR_WINDOW_TITLE); - win = zr_find_window(ctx, title_hash); - if (!win) return 0; - return win == ctx->active; -} - -struct zr_window* -zr_window_find(struct zr_context *ctx, const char *name) -{ - int title_len; - zr_hash title_hash; - title_len = (int)zr_strlen(name); - title_hash = zr_murmur_hash(name, (int)title_len, ZR_WINDOW_TITLE); - return zr_find_window(ctx, title_hash); -} - -void -zr_window_close(struct zr_context *ctx, const char *name) -{ - struct zr_window *win; - ZR_ASSERT(ctx); - if (!ctx) return; - win = zr_window_find(ctx, name); - if (!win) return; - ZR_ASSERT(ctx->current != win && "You cannot close a current window"); - if (ctx->current == win) return; - win->flags |= ZR_WINDOW_HIDDEN; -} - -void -zr_window_set_bounds(struct zr_context *ctx, struct zr_rect bounds) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return; - ctx->current->bounds = bounds; -} - -void -zr_window_set_position(struct zr_context *ctx, struct zr_vec2 pos) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return; - ctx->current->bounds.x = pos.x; - ctx->current->bounds.y = pos.y; -} - -void -zr_window_set_size(struct zr_context *ctx, struct zr_vec2 size) -{ - ZR_ASSERT(ctx); ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) return; - ctx->current->bounds.w = size.x; - ctx->current->bounds.h = size.y; -} - -void -zr_window_collapse(struct zr_context *ctx, const char *name, - enum zr_collapse_states c) -{ - int title_len; - zr_hash title_hash; - struct zr_window *win; - ZR_ASSERT(ctx); - if (!ctx) return; - - title_len = (int)zr_strlen(name); - title_hash = zr_murmur_hash(name, (int)title_len, ZR_WINDOW_TITLE); - win = zr_find_window(ctx, title_hash); - if (!win) return; - if (c == ZR_MINIMIZED) - win->flags |= ZR_WINDOW_MINIMIZED; - else win->flags &= ~(zr_flags)ZR_WINDOW_MINIMIZED; -} - -void -zr_window_collapse_if(struct zr_context *ctx, const char *name, - enum zr_collapse_states c, int cond) -{ - ZR_ASSERT(ctx); - if (!ctx || !cond) return; - zr_window_collapse(ctx, name, c); -} - -void -zr_window_show(struct zr_context *ctx, const char *name, enum zr_show_states s) -{ - int title_len; - zr_hash title_hash; - struct zr_window *win; - ZR_ASSERT(ctx); - if (!ctx) return; - - title_len = (int)zr_strlen(name); - title_hash = zr_murmur_hash(name, (int)title_len, ZR_WINDOW_TITLE); - win = zr_find_window(ctx, title_hash); - if (!win) return; - if (s == ZR_HIDDEN) - win->flags |= ZR_WINDOW_HIDDEN; - else win->flags &= ~(zr_flags)ZR_WINDOW_HIDDEN; -} - -void -zr_window_show_if(struct zr_context *ctx, const char *name, - enum zr_show_states s, int cond) -{ - ZR_ASSERT(ctx); - if (!ctx || !cond) return; - zr_window_show(ctx, name, s); -} - -void -zr_window_set_focus(struct zr_context *ctx, const char *name) -{ - int title_len; - zr_hash title_hash; - struct zr_window *win; - ZR_ASSERT(ctx); - if (!ctx) return; - - title_len = (int)zr_strlen(name); - title_hash = zr_murmur_hash(name, (int)title_len, ZR_WINDOW_TITLE); - win = zr_find_window(ctx, title_hash); - if (win && ctx->end != win) { - zr_remove_window(ctx, win); - zr_insert_window(ctx, win); - } - ctx->active = win; -} - -/*---------------------------------------------------------------- - * - * PANEL - * - * --------------------------------------------------------------*/ -static int -zr_panel_begin(struct zr_context *ctx, const char *title) -{ - struct zr_input *in; - struct zr_window *win; - struct zr_panel *layout; - struct zr_command_buffer *out; - const struct zr_style *style; - const struct zr_user_font *font; - - int header_active = 0; - struct zr_vec2 scrollbar_size; - struct zr_vec2 item_spacing; - struct zr_vec2 window_padding; - struct zr_vec2 scaler_size; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - style = &ctx->style; - font = &style->font; - in = &ctx->input; - win = ctx->current; - layout = win->layout; - - /* cache style data */ - scrollbar_size = style->window.scrollbar_size; - window_padding = style->window.padding; - item_spacing = style->window.spacing; - scaler_size = style->window.scaler_size; - - /* check arguments */ - zr_zero(layout, sizeof(*layout)); - if (win->flags & ZR_WINDOW_HIDDEN) - return 0; - - /* move panel position if requested */ - layout->header_h = font->height + 2 * style->window.header.padding.y; - layout->header_h += 2 * style->window.header.label_padding.y; - if ((win->flags & ZR_WINDOW_MOVABLE) && !(win->flags & ZR_WINDOW_ROM)) { - int incursor; - struct zr_rect move; - move.x = win->bounds.x; - move.y = win->bounds.y; - move.w = win->bounds.w; - move.h = layout->header_h; - incursor = zr_input_is_mouse_prev_hovering_rect(in, move); - if (zr_input_is_mouse_down(in, ZR_BUTTON_LEFT) && incursor) { - win->bounds.x = win->bounds.x + in->mouse.delta.x; - win->bounds.y = win->bounds.y + in->mouse.delta.y; - } - } - -#if ZR_COMPILE_WITH_COMMAND_USERDATA - win->buffer.userdata = ctx->userdata; -#endif - - /* panel space without border */ - if (win->flags & ZR_WINDOW_BORDER) { - if (!(win->flags & ZR_WINDOW_SUB)) - layout->bounds = zr_shrink_rect(win->bounds, style->window.border); - else if (win->flags & ZR_WINDOW_COMBO) - layout->bounds = zr_shrink_rect(win->bounds, style->window.combo_border); - else if (win->flags & ZR_WINDOW_CONTEXTUAL) - layout->bounds = zr_shrink_rect(win->bounds, style->window.contextual_border); - else if (win->flags & ZR_WINDOW_MENU) - layout->bounds = zr_shrink_rect(win->bounds, style->window.menu_border); - else if (win->flags & ZR_WINDOW_GROUP) - layout->bounds = zr_shrink_rect(win->bounds, style->window.group_border); - else if (win->flags & ZR_WINDOW_TOOLTIP) - layout->bounds = zr_shrink_rect(win->bounds, style->window.tooltip_border); - else layout->bounds = zr_shrink_rect(win->bounds, style->window.border); - } else layout->bounds = win->bounds; - - /* setup panel */ - layout->border = layout->bounds.x - win->bounds.x; - layout->at_x = layout->bounds.x; - layout->at_y = layout->bounds.y; - layout->width = layout->bounds.w; - layout->height = layout->bounds.h; - layout->max_x = 0; - layout->row.index = 0; - layout->row.columns = 0; - layout->row.height = 0; - layout->row.ratio = 0; - layout->row.item_width = 0; - layout->row.tree_depth = 0; - layout->flags = win->flags; - out = &win->buffer; - - /* calculate window header */ - if (win->flags & ZR_WINDOW_MINIMIZED) { - layout->header_h = 0; - layout->row.height = 0; - } else { - layout->header_h = 0; - layout->row.height = item_spacing.y; - } - - /* calculate window footer height */ - if (!(win->flags & ZR_WINDOW_NONBLOCK) && - (!(win->flags & ZR_WINDOW_NO_SCROLLBAR) || (win->flags & ZR_WINDOW_SCALABLE))) - layout->footer_h = scaler_size.y + style->window.footer_padding.y; - else layout->footer_h = 0; - - /* calculate the window size */ - if (!(win->flags & ZR_WINDOW_NO_SCROLLBAR)) - layout->width = layout->bounds.w - scrollbar_size.x; - layout->height = layout->bounds.h - (layout->header_h + 2 * item_spacing.y); - layout->height -= layout->footer_h; - - /* window header state */ - header_active = (win->flags & (ZR_WINDOW_CLOSABLE|ZR_WINDOW_MINIMIZABLE)); - header_active = header_active || (win->flags & ZR_WINDOW_TITLE); - header_active = header_active && !(win->flags & ZR_WINDOW_HIDDEN) && title; - - /* window header */ - if (header_active) - { - struct zr_rect header; - struct zr_rect button; - struct zr_text text; - const struct zr_style_item *background; - - /* calculate header bounds */ - header.x = layout->bounds.x; - header.y = layout->bounds.y; - header.w = layout->bounds.w; - - /* calculate correct header height */ - layout->header_h = font->height + 2.0f * style->window.header.padding.y; - layout->header_h += 2.0f * style->window.header.label_padding.y; - layout->row.height += layout->header_h + style->window.padding.y; - header.h = layout->header_h + 0.5f; - - /* update window height */ - layout->height = layout->bounds.h - (header.h + 2 * item_spacing.y); - layout->height -= layout->footer_h; - - /* select correct header background and text color */ - if (ctx->active == win) { - background = &style->window.header.active; - text.text = style->window.header.label_active; - } else if (zr_input_is_mouse_hovering_rect(&ctx->input, header)) { - background = &style->window.header.hover; - text.text = style->window.header.label_hover; - } else { - background = &style->window.header.normal; - text.text = style->window.header.label_normal; - } - - /* draw header background */ - if (background->type == ZR_STYLE_ITEM_IMAGE) { - text.background = zr_rgba(0,0,0,0); - zr_draw_image(&win->buffer, header, &background->data.image); - } else { - text.background = background->data.color; - zr_fill_rect(out, zr_rect(layout->bounds.x, layout->bounds.y, - layout->bounds.w, layout->header_h), 0, background->data.color); - } - - /* window close button */ - button.y = header.y + style->window.header.padding.y; - button.h = layout->header_h - 2 * style->window.header.padding.y; - button.w = button.h; - if (win->flags & ZR_WINDOW_CLOSABLE) { - zr_flags ws; - if (style->window.header.align == ZR_HEADER_RIGHT) { - button.x = (header.w + header.x) - (button.w + style->window.header.padding.x); - header.w -= button.w + style->window.header.spacing.x + style->window.header.padding.x; - } else { - button.x = header.x + style->window.header.padding.x; - header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; - } - if (zr_do_button_symbol(&ws, &win->buffer, button, - style->window.header.close_symbol, ZR_BUTTON_DEFAULT, - &style->window.header.close_button, in, &style->font)) - layout->flags |= ZR_WINDOW_HIDDEN; - } - - /* window minimize button */ - if (win->flags & ZR_WINDOW_MINIMIZABLE) { - zr_flags ws; - if (style->window.header.align == ZR_HEADER_RIGHT) { - button.x = (header.w + header.x) - button.w; - if (!(win->flags & ZR_WINDOW_CLOSABLE)) { - button.x -= style->window.header.padding.x; - header.w -= style->window.header.padding.x; - } - header.w -= button.w + style->window.header.spacing.x; - } else { - button.x = header.x; - header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; - } - if (zr_do_button_symbol(&ws, &win->buffer, button, - (layout->flags & ZR_WINDOW_MINIMIZED)? - style->window.header.maximize_symbol: - style->window.header.minimize_symbol, - ZR_BUTTON_DEFAULT, &style->window.header.minimize_button, in, &style->font)) - layout->flags = (layout->flags & ZR_WINDOW_MINIMIZED) ? - layout->flags & (zr_flags)~ZR_WINDOW_MINIMIZED: - layout->flags | ZR_WINDOW_MINIMIZED; - } - { - /* window header title */ - zr_size text_len = zr_strlen(title); - struct zr_rect label = {0,0,0,0}; - zr_size t = font->width(font->userdata, font->height, title, text_len); - - label.x = header.x + style->window.header.padding.x; - label.x += style->window.header.label_padding.x; - label.y = header.y + style->window.header.label_padding.y; - label.h = font->height + 2 * style->window.header.label_padding.y; - label.w = (float)t + 2 * style->window.header.spacing.x; - text.padding = zr_vec2(0,0); - zr_widget_text(out, label,(const char*)title, text_len, &text, - ZR_TEXT_LEFT, font); - } - } - - /* fix header height for transistion between minimized and maximized window state */ - if (win->flags & ZR_WINDOW_MINIMIZED && !(layout->flags & ZR_WINDOW_MINIMIZED)) - layout->row.height += 2 * item_spacing.y + style->window.border; - - if (layout->flags & ZR_WINDOW_MINIMIZED) { - /* draw window background if minimized */ - layout->row.height = 0; - zr_fill_rect(out, zr_rect(layout->bounds.x, layout->bounds.y, - layout->bounds.w, layout->row.height), 0, style->window.background); - } else if (!(layout->flags & ZR_WINDOW_DYNAMIC)) { - /* draw fixed window body */ - struct zr_rect body = layout->bounds; - if (header_active) { - body.y += layout->header_h - 0.5f; - body.h -= layout->header_h; - } - if (style->window.fixed_background.type == ZR_STYLE_ITEM_IMAGE) - zr_draw_image(out, body, &style->window.fixed_background.data.image); - else zr_fill_rect(out, body, 0, style->window.fixed_background.data.color); - } else { - /* draw dynamic window body */ - zr_fill_rect(out, zr_rect(layout->bounds.x, layout->bounds.y, - layout->bounds.w, layout->row.height + window_padding.y), 0, - style->window.background); - } - - /* draw top window border line */ - if (layout->flags & ZR_WINDOW_BORDER) { - struct zr_color border; - if (!(win->flags & ZR_WINDOW_SUB)) - border = style->window.border_color; - else if (win->flags & ZR_WINDOW_COMBO) - border = style->window.combo_border_color; - else if (win->flags & ZR_WINDOW_CONTEXTUAL) - border = style->window.contextual_border_color; - else if (win->flags & ZR_WINDOW_MENU) - border = style->window.menu_border_color; - else if (win->flags & ZR_WINDOW_GROUP) - border = style->window.group_border_color; - else if (win->flags & ZR_WINDOW_TOOLTIP) - border = style->window.tooltip_border_color; - else border = style->window.border_color; - zr_stroke_line(out, - win->bounds.x + layout->border/2.0f, - win->bounds.y + layout->border/2.0f, - win->bounds.x + win->bounds.w - layout->border, - win->bounds.y + layout->border/2.0f, - style->window.border, border); - } - { - /* calculate and set the window clipping rectangle*/ - struct zr_rect clip; - if (!(win->flags & ZR_WINDOW_DYNAMIC)) { - layout->clip.x = layout->bounds.x + window_padding.x; - layout->clip.w = layout->width - 2 * window_padding.x; - } else { - layout->clip.x = layout->bounds.x; - layout->clip.w = layout->width; - } - - layout->clip.h = layout->bounds.h - (layout->footer_h + layout->header_h); - layout->clip.h -= (2.0f * window_padding.y); - layout->clip.y = layout->bounds.y; - - /* combo box and menu do not have header space */ - if (!(win->flags & ZR_WINDOW_COMBO) && !(win->flags & ZR_WINDOW_MENU)) - layout->clip.y += layout->header_h; - - zr_unify(&clip, &win->buffer.clip, layout->clip.x, layout->clip.y, - layout->clip.x + layout->clip.w, layout->clip.y + layout->clip.h); - zr_push_scissor(out, clip); - - win->buffer.clip.x = layout->bounds.x; - win->buffer.clip.w = layout->width; - if (!(win->flags & ZR_WINDOW_NO_SCROLLBAR)) - win->buffer.clip.w += scrollbar_size.x; - } - return !(layout->flags & ZR_WINDOW_HIDDEN) && !(layout->flags & ZR_WINDOW_MINIMIZED); -} - -static void -zr_panel_end(struct zr_context *ctx) -{ - struct zr_input *in; - struct zr_window *window; - struct zr_panel *layout; - const struct zr_style *style; - struct zr_command_buffer *out; - - struct zr_vec2 scrollbar_size; - struct zr_vec2 scaler_size; - struct zr_vec2 item_spacing; - struct zr_vec2 window_padding; - struct zr_rect footer = {0,0,0,0}; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - window = ctx->current; - layout = window->layout; - style = &ctx->style; - out = &window->buffer; - in = (layout->flags & ZR_WINDOW_ROM) ? 0 :&ctx->input; - if (!(layout->flags & ZR_WINDOW_SUB)) - zr_push_scissor(out, zr_null_rect); - - /* cache configuration data */ - item_spacing = style->window.spacing; - window_padding = style->window.padding; - scrollbar_size = style->window.scrollbar_size; - scaler_size = style->window.scaler_size; - - /* update the current cursor Y-position to point over the last added widget */ - layout->at_y += layout->row.height; - - /* draw footer and fill empty spaces inside a dynamically growing panel */ - if (layout->flags & ZR_WINDOW_DYNAMIC && !(layout->flags & ZR_WINDOW_MINIMIZED)) { - layout->height = layout->at_y - layout->bounds.y; - layout->height = ZR_MIN(layout->height, layout->bounds.h); - - if ((layout->offset->x == 0) || (layout->flags & ZR_WINDOW_NO_SCROLLBAR)) { - /* special case for dynamic windows without horizontal scrollbar - * or hidden scrollbars */ - footer.x = window->bounds.x; - footer.y = window->bounds.y + layout->height + item_spacing.y; - footer.w = window->bounds.w + scrollbar_size.x; - layout->footer_h = 0; - footer.h = 0; - - if ((layout->offset->x == 0) && !(layout->flags & ZR_WINDOW_NO_SCROLLBAR)) { - /* special case for windows like combobox, menu require draw call - * to fill the empty scrollbar background */ - struct zr_rect bounds; - bounds.x = layout->bounds.x + layout->width; - bounds.y = layout->clip.y; - bounds.w = scrollbar_size.x; - bounds.h = layout->height; - zr_fill_rect(out, bounds, 0, style->window.background); - } - } else { - /* dynamic window with visible scrollbars and therefore bigger footer */ - footer.x = window->bounds.x; - footer.w = window->bounds.w + scrollbar_size.x; - footer.h = layout->footer_h; - if ((layout->flags & ZR_WINDOW_COMBO) || (layout->flags & ZR_WINDOW_MENU) || - (layout->flags & ZR_WINDOW_CONTEXTUAL)) - footer.y = window->bounds.y + layout->height; - else footer.y = window->bounds.y + layout->height + layout->footer_h; - zr_fill_rect(out, footer, 0, style->window.background); - - if (!(layout->flags & ZR_WINDOW_COMBO) && !(layout->flags & ZR_WINDOW_MENU)) { - /* fill empty scrollbar space */ - struct zr_rect bounds; - bounds.x = layout->bounds.x; - bounds.y = window->bounds.y + layout->height; - bounds.w = layout->bounds.w; - bounds.h = layout->row.height; - zr_fill_rect(out, bounds, 0, style->window.background); - } - } - } - - /* scrollbars */ - if (!(layout->flags & ZR_WINDOW_NO_SCROLLBAR) && !(layout->flags & ZR_WINDOW_MINIMIZED)) - { - struct zr_rect bounds; - int scroll_has_scrolling; - float scroll_target; - float scroll_offset; - float scroll_step; - float scroll_inc; - { - /* vertical scollbar */ - zr_flags state; - bounds.x = layout->bounds.x + layout->width; - bounds.y = layout->clip.y; - bounds.w = scrollbar_size.y; - bounds.h = layout->clip.h; - if (layout->flags & ZR_WINDOW_BORDER) bounds.h -= 1; - - scroll_offset = layout->offset->y; - scroll_step = layout->clip.h * 0.10f; - scroll_inc = layout->clip.h * 0.01f; - scroll_target = (float)(int)(layout->at_y - layout->clip.y); - scroll_has_scrolling = (window == ctx->active); - scroll_offset = zr_do_scrollbarv(&state, out, bounds, scroll_has_scrolling, - scroll_offset, scroll_target, scroll_step, scroll_inc, - &ctx->style.scrollv, in, &style->font); - layout->offset->y = (unsigned short)scroll_offset; - } - { - /* horizontal scrollbar */ - zr_flags state; - bounds.x = layout->bounds.x + window_padding.x; - if (layout->flags & ZR_WINDOW_SUB) { - bounds.h = scrollbar_size.x; - bounds.y = (layout->flags & ZR_WINDOW_BORDER) ? - layout->bounds.y + 1 : layout->bounds.y; - bounds.y += layout->header_h + layout->menu.h + layout->height; - bounds.w = layout->clip.w; - } else if (layout->flags & ZR_WINDOW_DYNAMIC) { - bounds.h = ZR_MIN(scrollbar_size.x, layout->footer_h); - bounds.w = layout->bounds.w; - bounds.y = footer.y; - } else { - bounds.h = ZR_MIN(scrollbar_size.x, layout->footer_h); - bounds.y = layout->bounds.y + window->bounds.h; - bounds.y -= ZR_MAX(layout->footer_h, scrollbar_size.x); - bounds.w = layout->width - 2 * window_padding.x; - } - scroll_offset = layout->offset->x; - scroll_target = (float)(int)(layout->max_x - bounds.x); - scroll_step = layout->max_x * 0.05f; - scroll_inc = layout->max_x * 0.005f; - scroll_has_scrolling = zr_false; - scroll_offset = zr_do_scrollbarh(&state, out, bounds, scroll_has_scrolling, - scroll_offset, scroll_target, scroll_step, scroll_inc, - &ctx->style.scrollh, in, &style->font); - layout->offset->x = (unsigned short)scroll_offset; - } - } - - /* scaler */ - if ((layout->flags & ZR_WINDOW_SCALABLE) && in && !(layout->flags & ZR_WINDOW_MINIMIZED)) { - /* caluclate scaler bounds */ - const struct zr_style_item *scaler; - float scaler_w = ZR_MAX(0, scaler_size.x - window_padding.x); - float scaler_h = ZR_MAX(0, scaler_size.y - window_padding.y); - float scaler_x = (layout->bounds.x + layout->bounds.w) - (window_padding.x + scaler_w); - float scaler_y; - if (layout->flags & ZR_WINDOW_DYNAMIC) - scaler_y = footer.y + layout->footer_h - scaler_size.y; - else scaler_y = layout->bounds.y + layout->bounds.h - scaler_size.y; - - /* draw scaler */ - scaler = &style->window.scaler; - if (scaler->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, zr_rect(scaler_x, scaler_y, scaler_w, scaler_h), - &scaler->data.image); - } else { - zr_fill_triangle(out, scaler_x + scaler_w, scaler_y, scaler_x + scaler_w, - scaler_y + scaler_h, scaler_x, scaler_y + scaler_h, - scaler->data.color); - } - - /* do window scaling logic */ - if (!(window->flags & ZR_WINDOW_ROM)) { - float prev_x = in->mouse.prev.x; - float prev_y = in->mouse.prev.y; - struct zr_vec2 window_size = style->window.min_size; - int incursor = ZR_INBOX(prev_x,prev_y,scaler_x,scaler_y,scaler_w,scaler_h); - - if (zr_input_is_mouse_down(in, ZR_BUTTON_LEFT) && incursor) { - window->bounds.w = ZR_MAX(window_size.x, window->bounds.w + in->mouse.delta.x); - /* draging in y-direction is only possible if static window */ - if (!(layout->flags & ZR_WINDOW_DYNAMIC)) - window->bounds.h = ZR_MAX(window_size.y, window->bounds.h + in->mouse.delta.y); - } - } - } - - /* window border */ - if (layout->flags & ZR_WINDOW_BORDER) - { - const float padding_y = (layout->flags & ZR_WINDOW_MINIMIZED) ? - window->bounds.y + layout->header_h: - (layout->flags & ZR_WINDOW_DYNAMIC)? - layout->footer_h + footer.y: - layout->bounds.y + layout->bounds.h; - - struct zr_color border; - if (!(layout->flags & ZR_WINDOW_SUB)) - border = style->window.border_color; - else if (layout->flags & ZR_WINDOW_COMBO) - border = style->window.combo_border_color; - else if (layout->flags & ZR_WINDOW_CONTEXTUAL) - border = style->window.contextual_border_color; - else if (layout->flags & ZR_WINDOW_MENU) - border = style->window.menu_border_color; - else if (layout->flags & ZR_WINDOW_GROUP) - border = style->window.group_border_color; - else if (layout->flags & ZR_WINDOW_TOOLTIP) - border = style->window.tooltip_border_color; - else border = style->window.border_color; - - if (window->flags & ZR_WINDOW_BORDER_HEADER) - zr_stroke_line(out, window->bounds.x + layout->border/2.0f, - window->bounds.y + layout->header_h - layout->border, - window->bounds.x + window->bounds.w - layout->border, - window->bounds.y + layout->header_h - layout->border, - layout->border, border); - zr_stroke_line(out, window->bounds.x + layout->border/2.0f, - padding_y - layout->border, - window->bounds.x + window->bounds.w - layout->border, - padding_y - layout->border, - layout->border, border); - zr_stroke_line(out, window->bounds.x + layout->border/2.0f, - window->bounds.y + layout->border/2.0f, window->bounds.x + layout->border/2.0f, - padding_y - layout->border, layout->border, border); - zr_stroke_line(out, window->bounds.x + window->bounds.w - layout->border, - window->bounds.y + layout->border/2.0f, - window->bounds.x + window->bounds.w - layout->border, - padding_y - layout->border, layout->border, border); - } - - if (!(window->flags & ZR_WINDOW_SUB)) { - /* window is hidden so clear command buffer */ - if (layout->flags & ZR_WINDOW_HIDDEN) - zr_command_buffer_reset(&window->buffer); - /* window is visible and not tab */ - else zr_finish(ctx, window); - } - - /* ZR_WINDOW_REMOVE_ROM flag was set so remove ZR_WINDOW_ROM */ - if (layout->flags & ZR_WINDOW_REMOVE_ROM) { - layout->flags &= ~(zr_flags)ZR_WINDOW_ROM; - layout->flags &= ~(zr_flags)ZR_WINDOW_REMOVE_ROM; - } - window->flags = layout->flags; - - /* property garbage collector */ - if (window->property.active && window->property.old != window->property.seq && - window->property.active == window->property.prev) { - zr_zero(&window->property, sizeof(window->property)); - } else { - window->property.old = window->property.seq; - window->property.prev = window->property.active; - window->property.seq = 0; - } - - /* edit garbage collector */ - if (window->edit.active && window->edit.old != window->edit.seq && - window->edit.active == window->edit.prev) { - zr_zero(&window->edit, sizeof(window->edit)); - } else { - window->edit.old = window->edit.seq; - window->edit.prev = window->edit.active; - window->edit.seq = 0; - } - - /* contextual gargabe collector */ - if (window->popup.active_con && window->popup.con_old != window->popup.con_count) { - window->popup.con_count = 0; - window->popup.con_old = 0; - window->popup.active_con = 0; - } else { - window->popup.con_old = window->popup.con_count; - window->popup.con_count = 0; - } - window->popup.combo_count = 0; - /* helper to make sure you have a 'zr_layout_push' - * for every 'zr_layout_pop' */ - ZR_ASSERT(!layout->row.tree_depth); -} - -void -zr_menubar_begin(struct zr_context *ctx) -{ - struct zr_panel *layout; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - layout = ctx->current->layout; - if (layout->flags & ZR_WINDOW_HIDDEN || layout->flags & ZR_WINDOW_MINIMIZED) - return; - - layout->menu.x = layout->at_x; - layout->menu.y = layout->bounds.y + layout->header_h; - layout->menu.w = layout->width; - layout->menu.offset = *layout->offset; - layout->offset->y = 0; -} - -void -zr_menubar_end(struct zr_context *ctx) -{ - struct zr_window *win; - struct zr_panel *layout; - struct zr_command_buffer *out; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - if (!ctx || layout->flags & ZR_WINDOW_HIDDEN || layout->flags & ZR_WINDOW_MINIMIZED) - return; - - out = &win->buffer; - layout->menu.h = layout->at_y - layout->menu.y; - layout->clip.y = layout->bounds.y + layout->header_h + layout->menu.h + layout->row.height; - layout->height -= layout->menu.h; - *layout->offset = layout->menu.offset; - layout->clip.h -= layout->menu.h + layout->row.height; - layout->at_y = layout->menu.y + layout->menu.h; - zr_push_scissor(out, layout->clip); -} -/* ------------------------------------------------------------- - * - * LAYOUT - * - * --------------------------------------------------------------*/ -#define ZR_LAYOUT_DYNAMIC_FIXED 0 -#define ZR_LAYOUT_DYNAMIC_ROW 1 -#define ZR_LAYOUT_DYNAMIC_FREE 2 -#define ZR_LAYOUT_DYNAMIC 3 -#define ZR_LAYOUT_STATIC_FIXED 4 -#define ZR_LAYOUT_STATIC_ROW 5 -#define ZR_LAYOUT_STATIC_FREE 6 -#define ZR_LAYOUT_STATIC 7 - -static void -zr_panel_layout(const struct zr_context *ctx, struct zr_window *win, - float height, int cols) -{ - struct zr_panel *layout; - const struct zr_style *style; - struct zr_command_buffer *out; - - struct zr_vec2 item_spacing; - struct zr_vec2 panel_padding; - struct zr_color color; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - /* prefetch some configuration data */ - layout = win->layout; - style = &ctx->style; - out = &win->buffer; - color = style->window.background; - item_spacing = style->window.spacing; - panel_padding = style->window.padding; - - /* update the current row and set the current row layout */ - layout->row.index = 0; - layout->at_y += layout->row.height; - layout->row.columns = cols; - layout->row.height = height + item_spacing.y; - layout->row.item_offset = 0; - if (layout->flags & ZR_WINDOW_DYNAMIC) - zr_fill_rect(out, zr_rect(layout->bounds.x, layout->at_y, - layout->bounds.w, height + panel_padding.y), 0, color); -} - -static void -zr_row_layout(struct zr_context *ctx, enum zr_layout_format fmt, - float height, int cols, int width) -{ - /* update the current row and set the current row layout */ - struct zr_window *win; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - zr_panel_layout(ctx, win, height, cols); - if (fmt == ZR_DYNAMIC) - win->layout->row.type = ZR_LAYOUT_DYNAMIC_FIXED; - else win->layout->row.type = ZR_LAYOUT_STATIC_FIXED; - - win->layout->row.item_width = (float)width; - win->layout->row.ratio = 0; - win->layout->row.item_offset = 0; - win->layout->row.filled = 0; -} - -void -zr_layout_row_dynamic(struct zr_context *ctx, float height, int cols) -{ - zr_row_layout(ctx, ZR_DYNAMIC, height, cols, 0); -} - -void -zr_layout_row_static(struct zr_context *ctx, float height, int item_width, int cols) -{ - zr_row_layout(ctx, ZR_STATIC, height, cols, item_width); -} - -void -zr_layout_row_begin(struct zr_context *ctx, enum zr_layout_format fmt, - float row_height, int cols) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - - zr_panel_layout(ctx, win, row_height, cols); - if (fmt == ZR_DYNAMIC) - layout->row.type = ZR_LAYOUT_DYNAMIC_ROW; - else layout->row.type = ZR_LAYOUT_STATIC_ROW; - - layout->row.ratio = 0; - layout->row.item_width = 0; - layout->row.item_offset = 0; - layout->row.filled = 0; - layout->row.columns = cols; -} - -void -zr_layout_row_push(struct zr_context *ctx, float ratio_or_width) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - - if (layout->row.type == ZR_LAYOUT_DYNAMIC_ROW) { - float ratio = ratio_or_width; - if ((ratio + layout->row.filled) > 1.0f) return; - if (ratio > 0.0f) - layout->row.item_width = ZR_SATURATE(ratio); - else layout->row.item_width = 1.0f - layout->row.filled; - } else layout->row.item_width = ratio_or_width; -} - -void -zr_layout_row_end(struct zr_context *ctx) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - layout->row.item_width = 0; - layout->row.item_offset = 0; -} - -void -zr_layout_row(struct zr_context *ctx, enum zr_layout_format fmt, - float height, int cols, const float *ratio) -{ - int i; - int n_undef = 0; - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - zr_panel_layout(ctx, win, height, cols); - if (fmt == ZR_DYNAMIC) { - /* calculate width of undefined widget ratios */ - float r = 0; - layout->row.ratio = ratio; - for (i = 0; i < cols; ++i) { - if (ratio[i] < 0.0f) - n_undef++; - else r += ratio[i]; - } - r = ZR_SATURATE(1.0f - r); - layout->row.type = ZR_LAYOUT_DYNAMIC; - layout->row.item_width = (r > 0 && n_undef > 0) ? (r / (float)n_undef):0; - } else { - layout->row.ratio = ratio; - layout->row.type = ZR_LAYOUT_STATIC; - layout->row.item_width = 0; - layout->row.item_offset = 0; - } - layout->row.item_offset = 0; - layout->row.filled = 0; -} - -void -zr_layout_space_begin(struct zr_context *ctx, enum zr_layout_format fmt, - float height, int widget_count) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - zr_panel_layout(ctx, win, height, widget_count); - if (fmt == ZR_STATIC) - layout->row.type = ZR_LAYOUT_STATIC_FREE; - else layout->row.type = ZR_LAYOUT_DYNAMIC_FREE; - - layout->row.ratio = 0; - layout->row.item_width = 0; - layout->row.item_offset = 0; - layout->row.filled = 0; -} - -void -zr_layout_space_end(struct zr_context *ctx) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - layout->row.item_width = 0; - layout->row.item_height = 0; - layout->row.item_offset = 0; - zr_zero(&layout->row.item, sizeof(layout->row.item)); -} - -void -zr_layout_space_push(struct zr_context *ctx, struct zr_rect rect) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - layout->row.item = rect; -} - -struct zr_rect -zr_layout_space_bounds(struct zr_context *ctx) -{ - struct zr_rect ret; - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - win = ctx->current; - layout = win->layout; - - ret.x = layout->clip.x; - ret.y = layout->clip.y; - ret.w = layout->clip.w; - ret.h = layout->row.height; - return ret; -} - -struct zr_vec2 -zr_layout_space_to_screen(struct zr_context *ctx, struct zr_vec2 ret) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - win = ctx->current; - layout = win->layout; - - ret.x += layout->at_x - layout->offset->x; - ret.y += layout->at_y - layout->offset->y; - return ret; -} - -struct zr_vec2 -zr_layout_space_to_local(struct zr_context *ctx, struct zr_vec2 ret) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - win = ctx->current; - layout = win->layout; - - ret.x += -layout->at_x + layout->offset->x; - ret.y += -layout->at_y + layout->offset->y; - return ret; -} - -struct zr_rect -zr_layout_space_rect_to_screen(struct zr_context *ctx, struct zr_rect ret) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - win = ctx->current; - layout = win->layout; - - ret.x += layout->at_x - layout->offset->x; - ret.y += layout->at_y - layout->offset->y; - return ret; -} - -struct zr_rect -zr_layout_space_rect_to_local(struct zr_context *ctx, struct zr_rect ret) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - win = ctx->current; - layout = win->layout; - - ret.x += -layout->at_x + layout->offset->x; - ret.y += -layout->at_y + layout->offset->y; - return ret; -} - -static void -zr_panel_alloc_row(const struct zr_context *ctx, struct zr_window *win) -{ - struct zr_panel *layout = win->layout; - struct zr_vec2 spacing = ctx->style.window.spacing; - const float row_height = layout->row.height - spacing.y; - zr_panel_layout(ctx, win, row_height, layout->row.columns); -} - -static void -zr_layout_widget_space(struct zr_rect *bounds, const struct zr_context *ctx, - struct zr_window *win, int modify) -{ - struct zr_panel *layout; - float item_offset = 0; - float item_width = 0; - float item_spacing = 0; - - float panel_padding; - float panel_spacing; - float panel_space; - - struct zr_vec2 spacing; - struct zr_vec2 padding; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - ZR_ASSERT(bounds); - - /* cache some configuration data */ - spacing = ctx->style.window.spacing; - padding = ctx->style.window.padding; - - /* calculate the useable panel space */ - panel_padding = 2 * padding.x; - panel_spacing = (float)(layout->row.columns - 1) * spacing.x; - panel_space = layout->width - panel_padding - panel_spacing; - - /* calculate the width of one item inside the current layout space */ - switch (layout->row.type) { - case ZR_LAYOUT_DYNAMIC_FIXED: { - /* scaling fixed size widgets item width */ - item_width = panel_space / (float)layout->row.columns; - item_offset = (float)layout->row.index * item_width; - item_spacing = (float)layout->row.index * spacing.x; - } break; - case ZR_LAYOUT_DYNAMIC_ROW: { - /* scaling single ratio widget width */ - item_width = layout->row.item_width * panel_space; - item_offset = layout->row.item_offset; - item_spacing = (float)layout->row.index * spacing.x; - - if (modify) { - layout->row.item_offset += item_width + spacing.x; - layout->row.filled += layout->row.item_width; - layout->row.index = 0; - } - } break; - case ZR_LAYOUT_DYNAMIC_FREE: { - /* panel width depended free widget placing */ - bounds->x = layout->at_x + (layout->width * layout->row.item.x); - bounds->x -= layout->offset->x; - bounds->y = layout->at_y + (layout->row.height * layout->row.item.y); - bounds->y -= layout->offset->y; - bounds->w = layout->width * layout->row.item.w; - bounds->h = layout->row.height * layout->row.item.h; - return; - }; - case ZR_LAYOUT_DYNAMIC: { - /* scaling arrays of panel width ratios for every widget */ - float ratio; - ZR_ASSERT(layout->row.ratio); - ratio = (layout->row.ratio[layout->row.index] < 0) ? - layout->row.item_width : layout->row.ratio[layout->row.index]; - - item_spacing = (float)layout->row.index * spacing.x; - if (layout->row.index < layout->row.columns-1) - item_width = (ratio * panel_space) - spacing.x; - else item_width = (ratio * panel_space); - - item_offset = layout->row.item_offset; - if (modify) { - layout->row.item_offset += item_width + spacing.x; - layout->row.filled += ratio; - } - } break; - case ZR_LAYOUT_STATIC_FIXED: { - /* non-scaling fixed widgets item width */ - item_width = layout->row.item_width; - item_offset = (float)layout->row.index * item_width; - item_spacing = (float)layout->row.index * spacing.x; - } break; - case ZR_LAYOUT_STATIC_ROW: { - /* scaling single ratio widget width */ - item_width = layout->row.item_width; - item_offset = layout->row.item_offset; - item_spacing = (float)layout->row.index * spacing.x; - if (modify) { - layout->row.item_offset += item_width + spacing.x; - layout->row.index = 0; - } - } break; - case ZR_LAYOUT_STATIC_FREE: { - /* free widget placing */ - bounds->x = layout->at_x + layout->row.item.x; - bounds->w = layout->row.item.w; - if (((bounds->x + bounds->w) > layout->max_x) && modify) - layout->max_x = (bounds->x + bounds->w); - bounds->x -= layout->offset->x; - bounds->y = layout->at_y + layout->row.item.y; - bounds->y -= layout->offset->y; - bounds->h = layout->row.item.h; - return; - }; - case ZR_LAYOUT_STATIC: { - /* non-scaling array of panel pixel width for every widget */ - item_spacing = (float)layout->row.index * spacing.x; - item_width = layout->row.ratio[layout->row.index]; - item_offset = layout->row.item_offset; - if (modify) layout->row.item_offset += item_width + spacing.x; - } break; - default: ZR_ASSERT(0); break; - }; - - /* set the bounds of the newly allocated widget */ - bounds->w = item_width; - bounds->h = layout->row.height - spacing.y; - bounds->y = layout->at_y - layout->offset->y; - bounds->x = layout->at_x + item_offset + item_spacing + padding.x; - if (((bounds->x + bounds->w) > layout->max_x) && modify) - layout->max_x = bounds->x + bounds->w; - bounds->x -= layout->offset->x; -} - -static void -zr_panel_alloc_space(struct zr_rect *bounds, const struct zr_context *ctx) -{ - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - /* check if the end of the row has been hit and begin new row if so */ - win = ctx->current; - layout = win->layout; - if (layout->row.index >= layout->row.columns) - zr_panel_alloc_row(ctx, win); - - /* calculate widget position and size */ - zr_layout_widget_space(bounds, ctx, win, zr_true); - layout->row.index++; -} - -static void -zr_layout_peek(struct zr_rect *bounds, struct zr_context *ctx) -{ - float y; - int index; - struct zr_window *win; - struct zr_panel *layout; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - y = layout->at_y; - index = layout->row.index; - if (layout->row.index >= layout->row.columns) { - layout->at_y += layout->row.height; - layout->row.index = 0; - } - zr_layout_widget_space(bounds, ctx, win, zr_false); - layout->at_y = y; - layout->row.index = index; -} - -int -zr__layout_push(struct zr_context *ctx, enum zr_layout_node_type type, - const char *title, enum zr_collapse_states initial_state, - const char *file, int line) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_style *style; - struct zr_command_buffer *out; - const struct zr_input *in; - - struct zr_vec2 item_spacing; - struct zr_vec2 panel_padding; - struct zr_rect header = {0,0,0,0}; - struct zr_rect sym = {0,0,0,0}; - struct zr_text text; - - zr_flags ws; - int title_len; - zr_hash title_hash; - zr_uint *state = 0; - enum zr_widget_layout_states widget_state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - /* cache some data */ - win = ctx->current; - layout = win->layout; - out = &win->buffer; - style = &ctx->style; - - item_spacing = style->window.spacing; - panel_padding = style->window.padding; - - /* calculate header bounds and draw background */ - zr_layout_row_dynamic(ctx, style->font.height + 2 * style->tab.padding.y, 1); - widget_state = zr_widget(&header, ctx); - if (type == ZR_LAYOUT_TAB) { - const struct zr_style_item *background = &style->tab.background; - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(out, header, &background->data.image); - text.background = zr_rgba(0,0,0,0); - } else { - text.background = background->data.color; - zr_fill_rect(out, header, 0, style->tab.border_color); - zr_fill_rect(out, zr_shrink_rect(header, style->tab.border), - style->tab.rounding, background->data.color); - } - } else text.background = style->window.background; - - /* find or create tab persistent state (open/closed) */ - title_len = (int)zr_strlen(title); - title_hash = zr_murmur_hash(title, (int)title_len, (zr_hash)line); - if (file) title_hash += zr_murmur_hash(file, (int)zr_strlen(file), (zr_hash)line); - state = zr_find_value(win, title_hash); - if (!state) { - state = zr_add_value(ctx, win, title_hash, 0); - *state = initial_state; - } - - /* update node state */ - in = (!(layout->flags & ZR_WINDOW_ROM)) ? &ctx->input: 0; - in = (in && widget_state == ZR_WIDGET_VALID) ? &ctx->input : 0; - if (zr_button_behavior(&ws, header, in, ZR_BUTTON_DEFAULT)) - *state = (*state == ZR_MAXIMIZED) ? ZR_MINIMIZED : ZR_MAXIMIZED; - - { - /* draw closing/open icon */ - enum zr_heading heading; - heading = (*state == ZR_MAXIMIZED) ? ZR_DOWN : ZR_RIGHT; - - /* calculate the triangle bounds */ - sym.w = sym.h = style->font.height; - sym.y = header.y + style->tab.padding.y; - sym.x = header.x + panel_padding.x + style->tab.padding.x; - - /* calculate the triangle points and draw triangle */ - zr_do_button_symbol(&ws, &win->buffer, sym, - (*state == ZR_MAXIMIZED)? style->tab.sym_minimize: style->tab.sym_maximize, - ZR_BUTTON_DEFAULT, (type == ZR_LAYOUT_TAB)? - &style->tab.tab_button: &style->tab.node_button, - in, &style->font); - - /* calculate the space the icon occupied */ - sym.w = style->font.height + 2 * style->tab.spacing.x; - } - { - /* draw node label */ - struct zr_rect label; - header.w = ZR_MAX(header.w, sym.w + item_spacing.y + panel_padding.x); - label.x = sym.x + sym.w + item_spacing.x; - label.y = sym.y; - label.w = header.w - (sym.w + item_spacing.y + panel_padding.x); - label.h = style->font.height; - - text.text = style->tab.text; - text.padding = zr_vec2(0,0); - zr_widget_text(out, label, title, zr_strlen(title), &text, - ZR_TEXT_LEFT, &style->font); - } - - /* increase x-axis cursor widget position pointer */ - if (*state == ZR_MAXIMIZED) { - layout->at_x = header.x + layout->offset->x; - layout->width = ZR_MAX(layout->width, 2 * panel_padding.x); - layout->width -= 2 * panel_padding.x; - layout->row.tree_depth++; - return zr_true; - } else return zr_false; -} - -void zr_layout_pop(struct zr_context *ctx) -{ - struct zr_vec2 panel_padding; - struct zr_window *win = 0; - struct zr_panel *layout = 0; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - win = ctx->current; - layout = win->layout; - panel_padding = ctx->style.window.padding; - layout->at_x -= panel_padding.x; - layout->width += 2 * panel_padding.x; - ZR_ASSERT(layout->row.tree_depth); - layout->row.tree_depth--; -} -/*---------------------------------------------------------------- - * - * WIDGETS - * - * --------------------------------------------------------------*/ -struct zr_rect -zr_widget_bounds(struct zr_context *ctx) -{ - struct zr_rect bounds; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return zr_rect(0,0,0,0); - zr_layout_peek(&bounds, ctx); - return bounds; -} - -struct zr_vec2 -zr_widget_position(struct zr_context *ctx) -{ - struct zr_rect bounds; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return zr_vec2(0,0); - - zr_layout_peek(&bounds, ctx); - return zr_vec2(bounds.x, bounds.y); -} - -struct zr_vec2 -zr_widget_size(struct zr_context *ctx) -{ - struct zr_rect bounds; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return zr_vec2(0,0); - - zr_layout_peek(&bounds, ctx); - return zr_vec2(bounds.w, bounds.h); -} - -int -zr_widget_is_hovered(struct zr_context *ctx) -{ - int ret; - struct zr_rect bounds; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return 0; - - zr_layout_peek(&bounds, ctx); - ret = (ctx->active == ctx->current); - ret = ret && zr_input_is_mouse_hovering_rect(&ctx->input, bounds); - return ret; -} - -int -zr_widget_is_mouse_clicked(struct zr_context *ctx, enum zr_buttons btn) -{ - int ret; - struct zr_rect bounds; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return 0; - - zr_layout_peek(&bounds, ctx); - ret = (ctx->active == ctx->current); - ret = ret && zr_input_mouse_clicked(&ctx->input, btn, bounds); - return ret; -} - -int -zr_widget_has_mouse_click_down(struct zr_context *ctx, enum zr_buttons btn, int down) -{ - int ret; - struct zr_rect bounds; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return 0; - - zr_layout_peek(&bounds, ctx); - ret = (ctx->active == ctx->current); - ret = ret && zr_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down); - return ret; -} - -enum zr_widget_layout_states -zr_widget(struct zr_rect *bounds, const struct zr_context *ctx) -{ - struct zr_rect *c = 0; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return ZR_WIDGET_INVALID; - - /* allocate space and check if the widget needs to be updated and drawn */ - zr_panel_alloc_space(bounds, ctx); - c = &ctx->current->layout->clip; - if (!ZR_INTERSECT(c->x, c->y, c->w, c->h, bounds->x, bounds->y, bounds->w, bounds->h)) - return ZR_WIDGET_INVALID; - if (!ZR_CONTAINS(bounds->x, bounds->y, bounds->w, bounds->h, c->x, c->y, c->w, c->h)) - return ZR_WIDGET_ROM; - return ZR_WIDGET_VALID; -} - -enum zr_widget_layout_states -zr_widget_fitting(struct zr_rect *bounds, struct zr_context *ctx, - struct zr_vec2 item_padding) -{ - /* update the bounds to stand without padding */ - struct zr_window *win; - struct zr_style *style; - struct zr_panel *layout; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return ZR_WIDGET_INVALID; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget(bounds, ctx); - if (layout->row.index == 1) { - bounds->w += style->window.padding.x; - bounds->x -= style->window.padding.x; - } else bounds->x -= item_padding.x; - - if (layout->row.index == layout->row.columns) - bounds->w += style->window.padding.x; - else bounds->w += item_padding.x; - return state; -} - -/*---------------------------------------------------------------- - * - * MISC - * - * --------------------------------------------------------------*/ -void -zr_spacing(struct zr_context *ctx, int cols) -{ - struct zr_window *win; - struct zr_panel *layout; - struct zr_rect nil; - int i, index, rows; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - /* spacing over row boundries */ - win = ctx->current; - layout = win->layout; - index = (layout->row.index + cols) % layout->row.columns; - rows = (layout->row.index + cols) / layout->row.columns; - if (rows) { - for (i = 0; i < rows; ++i) - zr_panel_alloc_row(ctx, win); - cols = index; - } - - /* non table layout need to allocate space */ - if (layout->row.type != ZR_LAYOUT_DYNAMIC_FIXED && - layout->row.type != ZR_LAYOUT_STATIC_FIXED) { - for (i = 0; i < cols; ++i) - zr_panel_alloc_space(&nil, ctx); - } - layout->row.index = index; -} - -/*---------------------------------------------------------------- - * - * TEXT - * - * --------------------------------------------------------------*/ -void -zr_text_colored(struct zr_context *ctx, const char *str, zr_size len, zr_flags alignment, - struct zr_color color) -{ - struct zr_window *win; - const struct zr_style *style; - - struct zr_vec2 item_padding; - struct zr_rect bounds; - struct zr_text text; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) return; - - win = ctx->current; - style = &ctx->style; - zr_panel_alloc_space(&bounds, ctx); - item_padding = style->text.padding; - - text.padding.x = item_padding.x; - text.padding.y = item_padding.y; - text.background = style->window.background; - text.text = color; - zr_widget_text(&win->buffer, bounds, str, len, &text, alignment, &style->font); -} - -void -zr_text_wrap_colored(struct zr_context *ctx, const char *str, - zr_size len, struct zr_color color) -{ - struct zr_window *win; - const struct zr_style *style; - - struct zr_vec2 item_padding; - struct zr_rect bounds; - struct zr_text text; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) return; - - win = ctx->current; - style = &ctx->style; - zr_panel_alloc_space(&bounds, ctx); - item_padding = style->text.padding; - - text.padding.x = item_padding.x; - text.padding.y = item_padding.y; - text.background = style->window.background; - text.text = color; - zr_widget_text_wrap(&win->buffer, bounds, str, len, &text, &style->font); - ctx->last_widget_state = 0; -} - -#if ZR_COMPILE_WITH_STANDARD_IO -void zr_labelf_colored(struct zr_context *ctx, zr_flags flags, - struct zr_color color, const char *fmt, ...) -{ - char buf[256]; - va_list args; - va_start(args, fmt); - zr_strfmtv(buf, ZR_LEN(buf), fmt, args); - zr_label_colored(ctx, buf, flags, color); - va_end(args); -} - -void -zr_labelf_colored_wrap(struct zr_context *ctx, struct zr_color color, - const char *fmt, ...) -{ - char buf[256]; - va_list args; - va_start(args, fmt); - zr_strfmtv(buf, ZR_LEN(buf), fmt, args); - zr_label_colored_wrap(ctx, buf, color); - va_end(args); -} - -void -zr_labelf(struct zr_context *ctx, zr_flags flags, const char *fmt, ...) -{ - char buf[256]; - va_list args; - va_start(args, fmt); - zr_strfmtv(buf, ZR_LEN(buf), fmt, args); - zr_label(ctx, buf, flags); - va_end(args); -} - -void -zr_labelf_wrap(struct zr_context *ctx, const char *fmt,...) -{ - char buf[256]; - va_list args; - va_start(args, fmt); - zr_strfmtv(buf, ZR_LEN(buf), fmt, args); - zr_label_wrap(ctx, buf); - va_end(args); -} - -void -zr_value_bool(struct zr_context *ctx, const char *prefix, int value) -{zr_labelf(ctx, ZR_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false"));} - -void -zr_value_int(struct zr_context *ctx, const char *prefix, int value) -{zr_labelf(ctx, ZR_TEXT_LEFT, "%s: %d", prefix, value);} - -void -zr_value_uint(struct zr_context *ctx, const char *prefix, unsigned int value) -{zr_labelf(ctx, ZR_TEXT_LEFT, "%s: %u", prefix, value);} - -void -zr_value_float(struct zr_context *ctx, const char *prefix, float value) -{zr_labelf(ctx, ZR_TEXT_LEFT, "%s: %.3f", prefix, value);} - -void -zr_value_color_byte(struct zr_context *ctx, const char *p, struct zr_color c) -{zr_labelf(ctx, ZR_TEXT_LEFT, "%s: (%c, %c, %c, %c)", p, c.r, c.g, c.b, c.a);} - -void -zr_value_color_float(struct zr_context *ctx, const char *p, struct zr_color color) -{ - float c[4]; zr_color_fv(c, color); - zr_labelf(ctx, ZR_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)", - p, c[0], c[1], c[2], c[3]); -} - -void -zr_value_color_hex(struct zr_context *ctx, const char *prefix, struct zr_color color) -{ - char hex[16]; - zr_color_hex_rgba(hex, color); - zr_labelf(ctx, ZR_TEXT_LEFT, "%s: %s", prefix, hex); -} -#endif - -void -zr_text(struct zr_context *ctx, const char *str, zr_size len, zr_flags alignment) -{ - ZR_ASSERT(ctx); - if (!ctx) return; - zr_text_colored(ctx, str, len, alignment, ctx->style.text.color); -} - -void -zr_text_wrap(struct zr_context *ctx, const char *str, zr_size len) -{ - ZR_ASSERT(ctx); - if (!ctx) return; - zr_text_wrap_colored(ctx, str, len, ctx->style.text.color); -} - -void -zr_label(struct zr_context *ctx, const char *str, zr_flags alignment) -{zr_text(ctx, str, zr_strlen(str), alignment);} - -void -zr_label_colored(struct zr_context *ctx, const char *str, zr_flags align, - struct zr_color color) -{zr_text_colored(ctx, str, zr_strlen(str), align, color);} - -void -zr_label_wrap(struct zr_context *ctx, const char *str) -{zr_text_wrap(ctx, str, zr_strlen(str));} - -void -zr_label_colored_wrap(struct zr_context *ctx, const char *str, struct zr_color color) -{zr_text_wrap_colored(ctx, str, zr_strlen(str), color);} - -void -zr_image(struct zr_context *ctx, struct zr_image img) -{ - struct zr_window *win; - struct zr_rect bounds; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) return; - - win = ctx->current; - if (!zr_widget(&bounds, ctx)) return; - zr_draw_image(&win->buffer, bounds, &img); - ctx->last_widget_state = 0; -} - -/*---------------------------------------------------------------- - * - * BUTTON - * - * --------------------------------------------------------------*/ -int -zr_button_text(struct zr_context *ctx, const char *title, zr_size len, - enum zr_button_behavior behavior) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget(&bounds, ctx); - - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - return zr_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, - title, len, style->button.text_alignment, behavior, - &style->button, in, &style->font); -} - -int zr_button_label(struct zr_context *ctx, const char *title, - enum zr_button_behavior behavior) -{return zr_button_text(ctx, title, zr_strlen(title), behavior);} - -int -zr_button_color(struct zr_context *ctx, struct zr_color color, - enum zr_button_behavior behavior) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - struct zr_style_button button; - const struct zr_style *style; - - int ret = 0; - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - - button = ctx->style.button; - button.normal = zr_style_item_color(color); - button.hover = zr_style_item_color(color); - button.active = zr_style_item_color(color); - button.padding = zr_vec2(0,0); - ret = zr_do_button(&ctx->last_widget_state, &win->buffer, bounds, - &button, in, behavior, &bounds); - zr_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button); - return ret; -} - -int -zr_button_symbol(struct zr_context *ctx, enum zr_symbol_type symbol, - enum zr_button_behavior behavior) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - return zr_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds, - symbol, behavior, &style->button, in, &style->font); -} - -int -zr_button_image(struct zr_context *ctx, struct zr_image img, - enum zr_button_behavior behavior) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - return zr_do_button_image(&ctx->last_widget_state, &win->buffer, bounds, - img, behavior, &style->button, in); -} - -int -zr_button_symbol_text(struct zr_context *ctx, enum zr_symbol_type symbol, - const char* text, zr_size len, zr_flags align, enum zr_button_behavior behavior) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - return zr_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, - symbol, text, len, align, behavior, &style->button, &style->font, in); -} - -int zr_button_symbol_label(struct zr_context *ctx, enum zr_symbol_type symbol, - const char *label, zr_flags align, enum zr_button_behavior behavior) -{return zr_button_symbol_text(ctx, symbol, label, zr_strlen(label), align, behavior);} - -int -zr_button_image_text(struct zr_context *ctx, struct zr_image img, - const char *text, zr_size len, zr_flags align, enum zr_button_behavior behavior) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - return zr_do_button_text_image(&ctx->last_widget_state, &win->buffer, - bounds, img, text, len, align, behavior, &style->button, &style->font, in); -} - -int zr_button_image_label(struct zr_context *ctx, struct zr_image img, - const char *label, zr_flags align, enum zr_button_behavior behavior) -{return zr_button_image_text(ctx, img, label, zr_strlen(label), align, behavior);} - -/*---------------------------------------------------------------- - * - * SELECTABLE - * - * --------------------------------------------------------------*/ -int -zr_selectable_text(struct zr_context *ctx, const char *str, zr_size len, - zr_flags align, int *value) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - enum zr_widget_layout_states state; - struct zr_rect bounds; - - ZR_ASSERT(ctx); - ZR_ASSERT(value); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !value) - return 0; - - win = ctx->current; - layout = win->layout; - style = &ctx->style; - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - return zr_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, - str, len, align, value, &style->selectable, in, &style->font); -} - -int zr_select_text(struct zr_context *ctx, const char *str, zr_size len, - zr_flags align, int value) -{zr_selectable_text(ctx, str, len, align, &value);return value;} - -int zr_selectable_label(struct zr_context *ctx, const char *str, zr_flags align, int *value) -{return zr_selectable_text(ctx, str, zr_strlen(str), align, value);} - -int zr_select_label(struct zr_context *ctx, const char *str, zr_flags align, int value) -{zr_selectable_text(ctx, str, zr_strlen(str), align, &value);return value;} - -/*---------------------------------------------------------------- - * - * CHECKBOX - * - * --------------------------------------------------------------*/ -int -zr_check_text(struct zr_context *ctx, const char *text, zr_size len, int active) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return active; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget(&bounds, ctx); - if (!state) return active; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - zr_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active, - text, len, ZR_TOGGLE_CHECK, &style->checkbox, in, &style->font); - return active; -} - -unsigned int -zr_check_flags_text(struct zr_context *ctx, const char *text, zr_size len, - unsigned int flags, unsigned int value) -{ - int old_active, active; - ZR_ASSERT(ctx); - ZR_ASSERT(text); - if (!ctx || !text) return flags; - old_active = active = (int)((flags & value) & value); - if (zr_check_text(ctx, text, len, old_active)) - flags |= value; - else flags &= ~value; - return flags; -} - -int -zr_checkbox_text(struct zr_context *ctx, const char *text, zr_size len, int *active) -{ - int old_val; - ZR_ASSERT(ctx); - ZR_ASSERT(text); - ZR_ASSERT(active); - if (!ctx || !text || !active) return 0; - old_val = *active; - *active = zr_check_text(ctx, text, len, *active); - return old_val != *active; -} - -int -zr_checkbox_flags_text(struct zr_context *ctx, const char *text, zr_size len, - unsigned int *flags, unsigned int value) -{ - int active; - ZR_ASSERT(ctx); - ZR_ASSERT(text); - ZR_ASSERT(flags); - if (!ctx || !text || !flags) return 0; - active = (int)((*flags & value) & value); - if (zr_checkbox_text(ctx, text, len, &active)) { - if (active) *flags |= value; - else *flags &= ~value; - return 1; - } - return 0; -} - -int zr_check_label(struct zr_context *ctx, const char *label, int active) -{return zr_check_text(ctx, label, zr_strlen(label), active);} - -unsigned int zr_check_flags_label(struct zr_context *ctx, const char *label, - unsigned int flags, unsigned int value) -{return zr_check_flags_text(ctx, label, zr_strlen(label), flags, value);} - -int zr_checkbox_label(struct zr_context *ctx, const char *label, int *active) -{return zr_checkbox_text(ctx, label, zr_strlen(label), active);} - -int zr_checkbox_flags_label(struct zr_context *ctx, const char *label, - unsigned int *flags, unsigned int value) -{return zr_checkbox_flags_text(ctx, label, zr_strlen(label), flags, value);} - -/*---------------------------------------------------------------- - * - * OPTION - * - * --------------------------------------------------------------*/ -int -zr_option_text(struct zr_context *ctx, const char *text, zr_size len, int is_active) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return is_active; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget(&bounds, ctx); - if (!state) return state; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - zr_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active, - text, len, ZR_TOGGLE_OPTION, &style->option, in, &style->font); - return is_active; -} - -int -zr_radio_text(struct zr_context *ctx, const char *text, zr_size len, int *active) -{ - int old_value; - ZR_ASSERT(ctx); - ZR_ASSERT(text); - ZR_ASSERT(active); - if (!ctx || !text || !active) return 0; - old_value = *active; - *active = zr_option_text(ctx, text, len, old_value); - return old_value != *active; -} - -int -zr_option_label(struct zr_context *ctx, const char *label, int active) -{return zr_option_text(ctx, label, zr_strlen(label), active);} - -int -zr_radio_label(struct zr_context *ctx, const char *label, int *active) -{return zr_radio_text(ctx, label, zr_strlen(label), active);} - -/*---------------------------------------------------------------- - * - * SLIDER - * - * --------------------------------------------------------------*/ -int -zr_slider_float(struct zr_context *ctx, float min_value, float *value, float max_value, - float value_step) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - int ret = 0; - float old_value; - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - ZR_ASSERT(value); - if (!ctx || !ctx->current || !ctx->current->layout || !value) - return ret; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget(&bounds, ctx); - if (!state) return ret; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - - old_value = *value; - *value = zr_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value, - old_value, max_value, value_step, &style->slider, in, &style->font); - return (old_value > *value || old_value < *value); -} - -float -zr_slide_float(struct zr_context *ctx, float min, float val, float max, float step) -{ - zr_slider_float(ctx, min, &val, max, step); return val; -} - -int -zr_slide_int(struct zr_context *ctx, int min, int val, int max, int step) -{ - float value = (float)val; - zr_slider_float(ctx, (float)min, &value, (float)max, (float)step); - return (int)value; -} - -int -zr_slider_int(struct zr_context *ctx, int min, int *val, int max, int step) -{ - int ret; - float value = (float)*val; - ret = zr_slider_float(ctx, (float)min, &value, (float)max, (float)step); - *val = (int)value; - return ret; -} - -/*---------------------------------------------------------------- - * - * PROGRESSBAR - * - * --------------------------------------------------------------*/ -int -zr_progress(struct zr_context *ctx, zr_size *cur, zr_size max, int is_modifyable) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_style *style; - const struct zr_input *in; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - zr_size old_value; - - ZR_ASSERT(ctx); - ZR_ASSERT(cur); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !cur) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - old_value = *cur; - *cur = zr_do_progress(&ctx->last_widget_state, &win->buffer, bounds, - *cur, max, is_modifyable, &style->progress, in); - return (*cur != old_value); -} - -zr_size zr_prog(struct zr_context *ctx, zr_size cur, zr_size max, int modifyable) -{zr_progress(ctx, &cur, max, modifyable);return cur;} - -/*---------------------------------------------------------------- - * - * EDIT - * - * --------------------------------------------------------------*/ -zr_flags -zr_edit_string(struct zr_context *ctx, zr_flags flags, - char *memory, zr_size *len, zr_size max, zr_filter filter) -{ - zr_flags active; - struct zr_buffer buffer; - - max = ZR_MAX(1, max); - *len = ZR_MIN(*len, max-1); - zr_buffer_init_fixed(&buffer, memory, max); - buffer.allocated = *len; - active = zr_edit_buffer(ctx, flags, &buffer, filter); - *len = buffer.allocated; - return active; -} - -zr_flags -zr_edit_buffer(struct zr_context *ctx, zr_flags flags, - struct zr_buffer *buffer, zr_filter filter) -{ - struct zr_window *win; - struct zr_style *style; - struct zr_input *in; - - enum zr_widget_layout_states state; - struct zr_rect bounds; - zr_flags ret_flags = 0; - int modifiable = 0; - zr_flags old_flags; - int show_cursor = 0; - zr_hash hash; - - int *active = 0; - float *scroll = 0; - zr_size *cursor = 0; - struct zr_text_selection *sel = 0; - - /* dummy state for non active edit */ - int dummy_active = 0; - float dummy_scroll = 0; - zr_size dummy_cursor = 0; - struct zr_text_selection dummy_sel = {0,0,0}; - - /* make sure correct values */ - ZR_ASSERT(ctx); - ZR_ASSERT(buffer); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - state = zr_widget(&bounds, ctx); - if (!state) return state; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if ((flags & ZR_EDIT_READ_ONLY)) { - modifiable = 0; - show_cursor = 0; - } else { - modifiable = 1; - show_cursor = 1; - } - - /* check if edit is currently hot item */ - hash = win->edit.seq++; - if (win->edit.active && hash == win->edit.name) { - active = &win->edit.active; - cursor = &win->edit.cursor; - scroll = &win->edit.scrollbar; - sel = &win->edit.sel; - } else { - active = &dummy_active; - cursor = &dummy_cursor; - scroll = &dummy_scroll; - sel = &dummy_sel; - } - - old_flags = (*active) ? ZR_EDIT_ACTIVE: ZR_EDIT_INACTIVE; - if (!flags || flags == ZR_EDIT_CURSOR) { - /* edit which only support appending and removing at end of line */ - int old = *active; - in = (flags & ZR_EDIT_READ_ONLY || !(modifiable)) ? 0: in; - if (!flags) { - /* simple edit field with only appending and removing at the end of the buffer */ - buffer->allocated = zr_do_edit_string(&ctx->last_widget_state, - &win->buffer, bounds, (char*)buffer->memory.ptr, buffer->allocated, - buffer->memory.size, active, 0, show_cursor, &style->edit, - filter, in, &ctx->style.font); - } else { - /* simple edit field cursor based movement, inserting and removing */ - zr_size glyphs = zr_utf_len((const char*)buffer->memory.ptr, buffer->allocated); - *cursor = ZR_MIN(*cursor, glyphs); - buffer->allocated = zr_do_edit_string(&ctx->last_widget_state, - &win->buffer, bounds, (char*)buffer->memory.ptr, buffer->allocated, - buffer->memory.size, active, cursor, show_cursor, &style->edit, - filter, in, &ctx->style.font); - } - - if (dummy_active) { - /* set hot edit widget state */ - win->edit.active = 1; - win->edit.name = hash; - win->edit.scrollbar = 0; - win->edit.sel.begin = 0; - win->edit.sel.end = 0; - win->edit.cursor = 0; - } else if (old && !*active) { - win->edit.active = 0; - } - } else { - /* edit with cursor and text selection */ - struct zr_edit_box box; - in = (flags & ZR_EDIT_READ_ONLY || !(modifiable)) ? 0: in; - if (flags & ZR_EDIT_CLIPBOARD) - zr_edit_box_init_buffer(&box, buffer, &ctx->clip, filter); - else zr_edit_box_init_buffer(&box, buffer, 0, filter); - - box.glyphs = zr_utf_len((const char*)buffer->memory.ptr, buffer->allocated); - box.active = *active; - box.filter = filter; - box.scrollbar = *scroll; - *cursor = ZR_MIN(box.glyphs, *cursor); - box.cursor = *cursor; - - if (!(flags & ZR_EDIT_CURSOR)) { - box.sel.begin = box.cursor; - box.sel.end = box.cursor; - } else { - if (!(flags & ZR_EDIT_SELECTABLE)) { - box.sel.active = 0; - box.sel.begin = box.cursor; - box.sel.end = box.cursor; - } else box.sel = *sel; - } - - if (flags & ZR_EDIT_MULTILINE) { - zr_do_edit_field(&ctx->last_widget_state, &win->buffer, bounds, &box, - &style->edit, show_cursor, modifiable, in, &style->font); - } else { - zr_do_edit_buffer(&ctx->last_widget_state, &win->buffer, bounds, &box, - &style->edit, show_cursor, in, &style->font); - } - - if (box.active) { - /* update hot edit widget state */ - *active = 1; - win->edit.active = 1; - win->edit.name = hash; - win->edit.scrollbar = box.scrollbar; - win->edit.sel = box.sel; - win->edit.cursor = box.cursor; - buffer->allocated = box.buffer.allocated; - } else if (!box.active && *active) { - win->edit.active = 0; - } - } - - /* enter deactivates edit and returns SIGCOMIT flag */ - if (*active && (flags & ZR_EDIT_SIGCOMIT) && - zr_input_is_key_pressed(in, ZR_KEY_ENTER)) { - ret_flags |= ZR_EDIT_SIGCOMIT; - *active = 0; - } - - /* pack edit widget state and state changes into flags */ - ret_flags |= (*active) ? ZR_EDIT_ACTIVE: ZR_EDIT_INACTIVE; - if (old_flags == ZR_EDIT_INACTIVE && ret_flags & ZR_EDIT_ACTIVE) - ret_flags |= ZR_EDIT_ACTIVATED; - else if (old_flags == ZR_EDIT_ACTIVE && ret_flags & ZR_EDIT_INACTIVE) - ret_flags |= ZR_EDIT_DEACTIVATED; - return ret_flags; -} - -/*---------------------------------------------------------------- - * - * PROPERTY - * - * --------------------------------------------------------------*/ -static float -zr_property(struct zr_context *ctx, const char *name, float min, float val, - float max, float step, float inc_per_pixel, const enum zr_property_filter filter) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states s; - - int *state = 0; - zr_hash hash = 0; - char *buffer = 0; - zr_size *len = 0; - zr_size *cursor = 0; - int old_state; - - char dummy_buffer[ZR_MAX_NUMBER_BUFFER]; - int dummy_state = ZR_PROPERTY_DEFAULT; - zr_size dummy_length = 0; - zr_size dummy_cursor = 0; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return val; - - win = ctx->current; - layout = win->layout; - style = &ctx->style; - s = zr_widget(&bounds, ctx); - if (!s) return val; - in = (s == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - - /* calculate hash from name */ - if (name[0] == '#') { - hash = zr_murmur_hash(name, (int)zr_strlen(name), win->property.seq++); - name++; /* special number hash */ - } else hash = zr_murmur_hash(name, (int)zr_strlen(name), 42); - - /* check if property is currently hot item */ - if (win->property.active && hash == win->property.name) { - buffer = win->property.buffer; - len = &win->property.length; - cursor = &win->property.cursor; - state = &win->property.state; - } else { - buffer = dummy_buffer; - len = &dummy_length; - cursor = &dummy_cursor; - state = &dummy_state; - } - - /* execute property widget */ - old_state = *state; - val = zr_do_property(&ctx->last_widget_state, &win->buffer, bounds, name, - min, val, max, step, inc_per_pixel, buffer, len, state, cursor, - &style->property, filter, in, &style->font); - - if (in && *state != ZR_PROPERTY_DEFAULT && !win->property.active) { - /* current property is now hot */ - win->property.active = 1; - zr_memcopy(win->property.buffer, buffer, *len); - win->property.length = *len; - win->property.cursor = *cursor; - win->property.state = *state; - win->property.name = hash; - } - - /* check if previously active property is now unactive */ - if (*state == ZR_PROPERTY_DEFAULT && old_state != ZR_PROPERTY_DEFAULT) - win->property.active = 0; - return val; -} - -void -zr_property_float(struct zr_context *ctx, const char *name, - float min, float *val, float max, float step, float inc_per_pixel) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(name); - ZR_ASSERT(val); - if (!ctx || !ctx->current || !name || !val) return; - *val = zr_property(ctx, name, min, *val, max, step, inc_per_pixel, ZR_FILTER_FLOAT); -} - -void -zr_property_int(struct zr_context *ctx, const char *name, - int min, int *val, int max, int step, int inc_per_pixel) -{ - float value; - ZR_ASSERT(ctx); - ZR_ASSERT(name); - ZR_ASSERT(val); - if (!ctx || !ctx->current || !name || !val) return; - value = zr_property(ctx, name, (float)min, (float)*val, (float)max, (float)step, - (float)inc_per_pixel, ZR_FILTER_FLOAT); - *val = (int)value; -} - -float -zr_propertyf(struct zr_context *ctx, const char *name, float min, - float val, float max, float step, float inc_per_pixel) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(name); - if (!ctx || !ctx->current || !name) return val; - return zr_property(ctx, name, min, val, max, step, inc_per_pixel, ZR_FILTER_FLOAT); -} - -int -zr_propertyi(struct zr_context *ctx, const char *name, int min, int val, - int max, int step, int inc_per_pixel) -{ - float value; - ZR_ASSERT(ctx); - ZR_ASSERT(name); - if (!ctx || !ctx->current || !name) return val; - value = zr_property(ctx, name, (float)min, (float)val, (float)max, (float)step, - (float)inc_per_pixel, ZR_FILTER_FLOAT); - return (int)value; -} - -/*---------------------------------------------------------------- - * - * COLOR PICKER - * - * --------------------------------------------------------------*/ -int -zr_color_pick(struct zr_context * ctx, struct zr_color *color, - enum zr_color_picker_format fmt) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_style *config; - const struct zr_input *in; - - zr_flags ws; - enum zr_widget_layout_states state; - struct zr_rect bounds; - - ZR_ASSERT(ctx); - ZR_ASSERT(color); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !color) - return 0; - - win = ctx->current; - config = &ctx->style; - layout = win->layout; - state = zr_widget(&bounds, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - return zr_do_color_picker(&ws, &win->buffer, color, fmt, bounds, - zr_vec2(0,0), in, &config->font); -} - -struct zr_color -zr_color_picker(struct zr_context *ctx, struct zr_color color, - enum zr_color_picker_format fmt) -{ - zr_color_pick(ctx, &color, fmt); - return color; -} - -/* ------------------------------------------------------------- - * - * CHART - * - * --------------------------------------------------------------*/ -int -zr_chart_begin(struct zr_context *ctx, const enum zr_chart_type type, - int count, float min_value, float max_value) -{ - struct zr_window *win; - struct zr_command_buffer *out; - struct zr_chart *chart; - const struct zr_style *config; - - const struct zr_style_item *background; - struct zr_rect bounds = {0, 0, 0, 0}; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) return 0; - if (!zr_widget(&bounds, ctx)) { - chart = &ctx->current->layout->chart; - chart->style = 0; - zr_zero(chart, sizeof(*chart)); - return 0; - } - - win = ctx->current; - out = &win->buffer; - config = &ctx->style; - chart = &win->layout->chart; - - /* setup basic generic chart */ - zr_zero(chart, sizeof(*chart)); - chart->type = type; - chart->style = (type == ZR_CHART_LINES) ? &config->line_chart: &config->column_chart; - chart->index = 0; - chart->count = count; - chart->min = ZR_MIN(min_value, max_value); - chart->max = ZR_MAX(min_value, max_value); - chart->range = chart->max - chart->min; - chart->x = bounds.x + chart->style->padding.x; - chart->y = bounds.y + chart->style->padding.y; - chart->w = bounds.w - 2 * chart->style->padding.x; - chart->h = bounds.h - 2 * chart->style->padding.y; - chart->w = ZR_MAX(chart->w, 2 * chart->style->padding.x); - chart->h = ZR_MAX(chart->h, 2 * chart->style->padding.y); - chart->last.x = 0; chart->last.y = 0; - - /* draw chart background */ - background = &chart->style->background; - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(&win->buffer, bounds, &background->data.image); - } else { - zr_fill_rect(&win->buffer, bounds, chart->style->rounding, chart->style->border_color); - zr_fill_rect(&win->buffer, zr_shrink_rect(bounds, chart->style->border), - chart->style->rounding, chart->style->border_color); - } - return 1; -} - -static zr_flags -zr_chart_push_line(struct zr_context *ctx, struct zr_window *win, - struct zr_chart *g, float value) -{ - struct zr_panel *layout = win->layout; - const struct zr_input *i = &ctx->input; - struct zr_command_buffer *out = &win->buffer; - - zr_flags ret = 0; - struct zr_vec2 cur; - struct zr_rect bounds; - struct zr_color color; - float step; - float range; - float ratio; - - step = g->w / (float)g->count; - range = g->max - g->min; - ratio = (value - g->min) / range; - - if (g->index == 0) { - /* first data point does not have a connection */ - g->last.x = g->x; - g->last.y = (g->y + g->h) - ratio * (float)g->h; - - bounds.x = g->last.x - 2; - bounds.y = g->last.y - 2; - bounds.w = 4; - bounds.h = 4; - - color = g->style->color; - if (!(layout->flags & ZR_WINDOW_ROM) && - ZR_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->last.x-3, g->last.y-3, 6, 6)){ - ret = zr_input_is_mouse_hovering_rect(i, bounds) ? ZR_CHART_HOVERING : 0; - ret |= (i->mouse.buttons[ZR_BUTTON_LEFT].down && - i->mouse.buttons[ZR_BUTTON_LEFT].clicked) ? ZR_CHART_CLICKED: 0; - color = g->style->selected_color; - } - zr_fill_rect(out, bounds, 0, color); - g->index++; - return ret; - } - - /* draw a line between the last data point and the new one */ - cur.x = g->x + (float)(step * (float)g->index); - cur.y = (g->y + g->h) - (ratio * (float)g->h); - zr_stroke_line(out, g->last.x, g->last.y, cur.x, cur.y, 1.0f, g->style->color); - - bounds.x = cur.x - 3; - bounds.y = cur.y - 3; - bounds.w = 6; - bounds.h = 6; - - /* user selection of current data point */ - color = g->style->color; - if (!(layout->flags & ZR_WINDOW_ROM)) { - if (zr_input_is_mouse_hovering_rect(i, bounds)) { - ret = ZR_CHART_HOVERING; - ret |= (!i->mouse.buttons[ZR_BUTTON_LEFT].down && - i->mouse.buttons[ZR_BUTTON_LEFT].clicked) ? ZR_CHART_CLICKED: 0; - color = g->style->selected_color; - } - } - zr_fill_rect(out, zr_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color); - - /* save current data point position */ - g->last.x = cur.x; - g->last.y = cur.y; - g->index++; - return ret; -} - -static zr_flags -zr_chart_push_column(const struct zr_context *ctx, struct zr_window *win, - struct zr_chart *chart, float value) -{ - struct zr_command_buffer *out = &win->buffer; - const struct zr_input *in = &ctx->input; - struct zr_panel *layout = win->layout; - - float ratio; - zr_flags ret = 0; - struct zr_color color; - struct zr_rect item = {0,0,0,0}; - - if (chart->index >= chart->count) - return zr_false; - if (chart->count) { - float padding = (float)(chart->count-1); - item.w = (chart->w - padding) / (float)(chart->count); - } - - /* calculate bounds of the current bar chart entry */ - color = chart->style->color; - item.h = chart->h * ZR_ABS((value/chart->range)); - if (value >= 0) { - ratio = (value + ZR_ABS(chart->min)) / ZR_ABS(chart->range); - item.y = (chart->y + chart->h) - chart->h * ratio; - } else { - ratio = (value - chart->max) / chart->range; - item.y = chart->y + (chart->h * ZR_ABS(ratio)) - item.h; - } - item.x = chart->x + ((float)chart->index * item.w); - item.x = item.x + ((float)chart->index); - - /* user chart bar selection */ - if (!(layout->flags & ZR_WINDOW_ROM) && - ZR_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) { - ret = ZR_CHART_HOVERING; - ret |= (!in->mouse.buttons[ZR_BUTTON_LEFT].down && - in->mouse.buttons[ZR_BUTTON_LEFT].clicked) ? ZR_CHART_CLICKED: 0; - color = chart->style->selected_color; - } - zr_fill_rect(out, item, 0, color); - chart->index++; - return ret; -} - -zr_flags -zr_chart_push(struct zr_context *ctx, float value) -{ - zr_flags flags; - struct zr_window *win; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current || !ctx->current->layout->chart.style) - return zr_false; - - win = ctx->current; - switch (win->layout->chart.type) { - case ZR_CHART_LINES: - flags = zr_chart_push_line(ctx, win, &win->layout->chart, value); break; - case ZR_CHART_COLUMN: - flags = zr_chart_push_column(ctx, win, &win->layout->chart, value); break; - default: - case ZR_CHART_MAX: - flags = 0; - } - return flags; -} - -void -zr_chart_end(struct zr_context *ctx) -{ - struct zr_window *win; - struct zr_chart *chart; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return; - - win = ctx->current; - chart = &win->layout->chart; - chart->type = ZR_CHART_MAX; - chart->index = 0; - chart->count = 0; - chart->min = 0; - chart->max = 0; - chart->x = 0; - chart->y = 0; - chart->w = 0; - chart->h = 0; - return; -} - -/* ------------------------------------------------------------- - * - * GROUP - * - * --------------------------------------------------------------*/ -int -zr_group_begin(struct zr_context *ctx, struct zr_panel *layout, const char *title, - zr_flags flags) -{ - struct zr_window *win; - const struct zr_rect *c; - union {struct zr_scroll *s; zr_uint *i;} value; - struct zr_window panel; - struct zr_rect bounds; - zr_hash title_hash; - int title_len; - - ZR_ASSERT(ctx); - ZR_ASSERT(title); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !title) - return 0; - - /* allocate space for the group panel inside the panel */ - win = ctx->current; - c = &win->layout->clip; - zr_panel_alloc_space(&bounds, ctx); - zr_zero(layout, sizeof(*layout)); - - /* find group persistent scrollbar value */ - title_len = (int)zr_strlen(title); - title_hash = zr_murmur_hash(title, (int)title_len, ZR_WINDOW_SUB); - value.i = zr_find_value(win, title_hash); - if (!value.i) { - value.i = zr_add_value(ctx, win, title_hash, 0); - *value.i = 0; - } - if (!ZR_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && - !(flags & ZR_WINDOW_MOVABLE)) { - return 0; - } - - flags |= ZR_WINDOW_SUB; - if (win->flags & ZR_WINDOW_ROM) - flags |= ZR_WINDOW_ROM; - - /* initialize a fake window to create the layout from */ - zr_zero(&panel, sizeof(panel)); - panel.bounds = bounds; - panel.flags = flags; - panel.scrollbar.x = (unsigned short)value.s->x; - panel.scrollbar.y = (unsigned short)value.s->y; - panel.buffer = win->buffer; - panel.layout = layout; - ctx->current = &panel; - zr_panel_begin(ctx, (flags & ZR_WINDOW_TITLE) ? title: 0); - - win->buffer = panel.buffer; - layout->offset = value.s; - layout->parent = win->layout; - win->layout = layout; - ctx->current = win; - return 1; -} - -void -zr_group_end(struct zr_context *ctx) -{ - struct zr_window *win; - struct zr_panel *parent; - struct zr_panel *g; - - struct zr_rect clip; - struct zr_window pan; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return; - - /* make sure zr_group_begin was called correctly */ - ZR_ASSERT(ctx->current); - win = ctx->current; - ZR_ASSERT(win->layout); - g = win->layout; - ZR_ASSERT(g->parent); - parent = g->parent; - - /* dummy window */ - zr_zero(&pan, sizeof(pan)); - pan.bounds = g->bounds; - pan.scrollbar.x = (unsigned short)g->offset->x; - pan.scrollbar.y = (unsigned short)g->offset->y; - pan.flags = g->flags|ZR_WINDOW_SUB; - pan.buffer = win->buffer; - pan.layout = g; - ctx->current = &pan; - - /* make sure group has correct clipping rectangle */ - zr_unify(&clip, &parent->clip, - g->bounds.x, g->clip.y - g->header_h, - g->bounds.x + g->bounds.w+1, - g->bounds.y + g->bounds.h + 1); - zr_push_scissor(&pan.buffer, clip); - zr_end(ctx); - - win->buffer = pan.buffer; - zr_push_scissor(&win->buffer, parent->clip); - ctx->current = win; - win->layout = parent; - win->bounds = parent->bounds; - if (win->flags & ZR_WINDOW_BORDER) - win->bounds = zr_shrink_rect(win->bounds, -win->layout->border); - return; -} - -/* -------------------------------------------------------------- - * - * POPUP - * - * --------------------------------------------------------------*/ -int -zr_popup_begin(struct zr_context *ctx, struct zr_panel *layout, - enum zr_popup_type type, const char *title, zr_flags flags, struct zr_rect rect) -{ - struct zr_window *popup; - struct zr_window *win; - - int title_len; - zr_hash title_hash; - zr_size allocated; - - ZR_ASSERT(ctx); - ZR_ASSERT(title); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - ZR_ASSERT(!(win->flags & ZR_WINDOW_POPUP)); - title_len = (int)zr_strlen(title); - title_hash = zr_murmur_hash(title, (int)title_len, ZR_WINDOW_POPUP); - - popup = win->popup.win; - if (!popup) { - popup = (struct zr_window*)zr_create_window(ctx); - win->popup.win = popup; - win->popup.active = 0; - } - - /* make sure we have to correct popup */ - if (win->popup.name != title_hash) { - if (!win->popup.active) { - zr_zero(popup, sizeof(*popup)); - win->popup.name = title_hash; - win->popup.active = 1; - } else return 0; - } - - /* popup position is local to window */ - ctx->current = popup; - rect.x += win->layout->clip.x; - rect.y += win->layout->clip.y; - - /* setup popup data */ - popup->parent = win; - popup->bounds = rect; - popup->seq = ctx->seq; - popup->layout = layout; - popup->flags = flags; - popup->flags |= ZR_WINDOW_BORDER|ZR_WINDOW_SUB|ZR_WINDOW_POPUP; - if (type == ZR_POPUP_DYNAMIC) - popup->flags |= ZR_WINDOW_DYNAMIC; - - popup->buffer = win->buffer; - zr_start_popup(ctx, win); - allocated = ctx->memory.allocated; - zr_push_scissor(&popup->buffer, zr_null_rect); - - if (zr_panel_begin(ctx, title)) { - /* popup is running therefore invalidate parent window */ - win->layout->flags |= ZR_WINDOW_ROM; - win->layout->flags &= ~(zr_flags)ZR_WINDOW_REMOVE_ROM; - win->popup.active = 1; - layout->offset = &popup->scrollbar; - return 1; - } else { - /* popup was closed/is invalid so cleanup */ - win->layout->flags |= ZR_WINDOW_REMOVE_ROM; - win->layout->popup_buffer.active = 0; - win->popup.active = 0; - ctx->memory.allocated = allocated; - ctx->current = win; - return 0; - } -} - -static int -zr_nonblock_begin(struct zr_panel *layout, struct zr_context *ctx, - zr_flags flags, struct zr_rect body, struct zr_rect header) -{ - struct zr_window *popup; - struct zr_window *win; - int is_active = zr_true; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - /* popups cannot have popups */ - win = ctx->current; - ZR_ASSERT(!(win->flags & ZR_WINDOW_POPUP)); - popup = win->popup.win; - if (!popup) { - /* create window for nonblocking popup */ - popup = (struct zr_window*)zr_create_window(ctx); - win->popup.win = popup; - zr_command_buffer_init(&popup->buffer, &ctx->memory, ZR_CLIPPING_ON); - } else { - /* check if user clicked outside the popup and close if so */ - int in_panel, in_body, in_header; - in_panel = zr_input_is_mouse_click_in_rect(&ctx->input, ZR_BUTTON_LEFT, win->layout->bounds); - in_body = zr_input_is_mouse_click_in_rect(&ctx->input, ZR_BUTTON_LEFT, body); - in_header = zr_input_is_mouse_click_in_rect(&ctx->input, ZR_BUTTON_LEFT, header); - if (!in_body && in_panel && !in_header) - is_active = zr_false; - } - - if (!is_active) { - win->layout->flags |= ZR_WINDOW_REMOVE_ROM; - return is_active; - } - - popup->bounds = body; - popup->parent = win; - popup->layout = layout; - popup->flags = flags; - popup->flags |= ZR_WINDOW_BORDER|ZR_WINDOW_POPUP; - popup->flags |= ZR_WINDOW_DYNAMIC|ZR_WINDOW_SUB; - popup->flags |= ZR_WINDOW_NONBLOCK; - popup->seq = ctx->seq; - win->popup.active = 1; - - zr_start_popup(ctx, win); - popup->buffer = win->buffer; - zr_push_scissor(&popup->buffer, zr_null_rect); - ctx->current = popup; - - zr_panel_begin(ctx, 0); - win->buffer = popup->buffer; - win->layout->flags |= ZR_WINDOW_ROM; - layout->offset = &popup->scrollbar; - return is_active; -} - -void -zr_popup_close(struct zr_context *ctx) -{ - struct zr_window *popup; - ZR_ASSERT(ctx); - if (!ctx || !ctx->current) return; - - popup = ctx->current; - ZR_ASSERT(popup->parent); - ZR_ASSERT(popup->flags & ZR_WINDOW_POPUP); - popup->flags |= ZR_WINDOW_HIDDEN; -} - -void -zr_popup_end(struct zr_context *ctx) -{ - struct zr_window *win; - struct zr_window *popup; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - popup = ctx->current; - ZR_ASSERT(popup->parent); - win = popup->parent; - if (popup->flags & ZR_WINDOW_HIDDEN) { - win->layout->flags |= ZR_WINDOW_REMOVE_ROM; - win->popup.active = 0; - } - zr_push_scissor(&popup->buffer, zr_null_rect); - zr_end(ctx); - - win->buffer = popup->buffer; - zr_finish_popup(ctx, win); - ctx->current = win; - zr_push_scissor(&win->buffer, win->layout->clip); -} -/* ------------------------------------------------------------- - * - * TOOLTIP - * - * -------------------------------------------------------------- */ -int -zr_tooltip_begin(struct zr_context *ctx, struct zr_panel *layout, float width) -{ - struct zr_window *win; - const struct zr_input *in; - struct zr_rect bounds; - int ret; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - /* make sure that no nonblocking popup is currently active */ - win = ctx->current; - in = &ctx->input; - if (win->popup.win && (win->popup.win->flags & ZR_WINDOW_NONBLOCK)) - return 0; - - bounds.w = width; - bounds.h = zr_null_rect.h; - bounds.x = (in->mouse.pos.x + 1) - win->layout->clip.x; - bounds.y = (in->mouse.pos.y + 1) - win->layout->clip.y; - - ret = zr_popup_begin(ctx, layout, ZR_POPUP_DYNAMIC, - "__##Tooltip##__", ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_TOOLTIP, bounds); - if (ret) win->layout->flags &= ~(zr_flags)ZR_WINDOW_ROM; - return ret; -} - -void -zr_tooltip_end(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return; - zr_popup_close(ctx); - zr_popup_end(ctx); -} - -void -zr_tooltip(struct zr_context *ctx, const char *text) -{ - const struct zr_style *style; - struct zr_vec2 padding; - struct zr_panel layout; - - zr_size text_len; - zr_size text_width; - zr_size text_height; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - ZR_ASSERT(text); - if (!ctx || !ctx->current || !ctx->current->layout || !text) - return; - - /* fetch configuration data */ - style = &ctx->style; - padding = style->window.padding; - - /* calculate size of the text and tooltip */ - text_len = zr_strlen(text); - text_width = style->font.width(style->font.userdata, - style->font.height, text, text_len); - text_width += (zr_size)(4 * padding.x); - text_height = (zr_size)(style->font.height + 2 * padding.y); - - /* execute tooltip and fill with text */ - if (zr_tooltip_begin(ctx, &layout, (float)text_width)) { - zr_layout_row_dynamic(ctx, (float)text_height, 1); - zr_text(ctx, text, text_len, ZR_TEXT_LEFT); - zr_tooltip_end(ctx); - } -} - -/* ------------------------------------------------------------- - * - * CONTEXTUAL - * - * -------------------------------------------------------------- */ -int -zr_contextual_begin(struct zr_context *ctx, struct zr_panel *layout, - zr_flags flags, struct zr_vec2 size, struct zr_rect trigger_bounds) -{ - struct zr_window *win; - struct zr_window *popup; - struct zr_rect body; - - static const struct zr_rect null_rect = {0,0,0,0}; - int is_clicked = 0; - int is_active = 0; - int is_open = 0; - int ret = 0; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - ++win->popup.con_count; - - /* check if currently active contextual is active */ - popup = win->popup.win; - is_open = (popup && (popup->flags & ZR_WINDOW_CONTEXTUAL) && win->popup.type == ZR_WINDOW_CONTEXTUAL); - is_clicked = zr_input_mouse_clicked(&ctx->input, ZR_BUTTON_RIGHT, trigger_bounds); - if (win->popup.active_con && win->popup.con_count != win->popup.active_con) - return 0; - if ((is_clicked && is_open && !is_active) || (!is_open && !is_active && !is_clicked)) - return 0; - - /* calculate contextual position on click */ - win->popup.active_con = win->popup.con_count; - if (is_clicked) { - body.x = ctx->input.mouse.pos.x; - body.y = ctx->input.mouse.pos.y; - } else { - body.x = popup->bounds.x; - body.y = popup->bounds.y; - } - body.w = size.x; - body.h = size.y; - - /* start nonblocking contextual popup */ - ret = zr_nonblock_begin(layout, ctx, - flags|ZR_WINDOW_CONTEXTUAL|ZR_WINDOW_NO_SCROLLBAR, body, null_rect); - if (ret) win->popup.type = ZR_WINDOW_CONTEXTUAL; - else { - win->popup.active_con = 0; - win->popup.win->flags = 0; - } - return ret; -} - -int -zr_contextual_item_text(struct zr_context *ctx, const char *text, zr_size len, - zr_flags alignment) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget_fitting(&bounds, ctx, style->contextual_button.padding); - if (!state) return zr_false; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, - text, len, alignment, ZR_BUTTON_DEFAULT, &style->contextual_button, in, &style->font)) { - zr_contextual_close(ctx); - return zr_true; - } - return zr_false; -} - -int zr_contextual_item_label(struct zr_context *ctx, const char *label, zr_flags align) -{return zr_contextual_item_text(ctx, label, zr_strlen(label), align);} - -int -zr_contextual_item_image_text(struct zr_context *ctx, struct zr_image img, - const char *text, zr_size len, zr_flags align) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget_fitting(&bounds, ctx, style->contextual_button.padding); - if (!state) return zr_false; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, - img, text, len, align, ZR_BUTTON_DEFAULT, &style->contextual_button, &style->font, in)){ - zr_contextual_close(ctx); - return zr_true; - } - return zr_false; -} - -int zr_contextual_item_image_label(struct zr_context *ctx, struct zr_image img, - const char *label, zr_flags align) -{return zr_contextual_item_image_text(ctx, img, label, zr_strlen(label), align);} - -int -zr_contextual_item_symbol_text(struct zr_context *ctx, enum zr_symbol_type symbol, - const char *text, zr_size len, zr_flags align) -{ - struct zr_window *win; - struct zr_panel *layout; - const struct zr_input *in; - const struct zr_style *style; - - struct zr_rect bounds; - enum zr_widget_layout_states state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - layout = win->layout; - state = zr_widget_fitting(&bounds, ctx, style->contextual_button.padding); - if (!state) return zr_false; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, - symbol, text, len, align, ZR_BUTTON_DEFAULT, &style->contextual_button, &style->font, in)) { - zr_contextual_close(ctx); - return zr_true; - } - return zr_false; -} - -int zr_contextual_item_symbol_label(struct zr_context *ctx, enum zr_symbol_type symbol, - const char *text, zr_flags align) -{return zr_contextual_item_symbol_text(ctx, symbol, text, zr_strlen(text), align);} - -void -zr_contextual_close(struct zr_context *ctx) -{ - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - if (!ctx->current) - return; - zr_popup_close(ctx); -} - -void -zr_contextual_end(struct zr_context *ctx) -{ - struct zr_window *popup; - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - popup = ctx->current; - ZR_ASSERT(popup->parent); - if (popup->flags & ZR_WINDOW_HIDDEN) - popup->seq = 0; - zr_popup_end(ctx); - return; -} -/* ------------------------------------------------------------- - * - * COMBO - * - * --------------------------------------------------------------*/ -static int -zr_combo_begin(struct zr_panel *layout, struct zr_context *ctx, struct zr_window *win, - int height, int is_clicked, struct zr_rect header) -{ - struct zr_window *popup; - int is_open = 0; - int is_active = 0; - struct zr_rect body; - zr_hash hash; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - popup = win->popup.win; - body.x = header.x; - body.w = header.w; - body.y = header.y + header.h-1; - body.h = (float)height; - - hash = win->popup.combo_count++; - is_open = (popup && (popup->flags & ZR_WINDOW_COMBO)); - is_active = (popup && (win->popup.name == hash) && win->popup.type == ZR_WINDOW_COMBO); - if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || - (!is_open && !is_active && !is_clicked)) return 0; - if (!zr_nonblock_begin(layout, ctx, ZR_WINDOW_COMBO, - body, zr_rect(0,0,0,0))) return 0; - - win->popup.type = ZR_WINDOW_COMBO; - win->popup.name = hash; - return 1; -} - -int -zr_combo_begin_text(struct zr_context *ctx, struct zr_panel *layout, - const char *selected, zr_size len, int height) -{ - const struct zr_input *in; - struct zr_window *win; - struct zr_style *style; - - enum zr_widget_layout_states s; - int is_active = zr_false; - struct zr_rect header; - - const struct zr_style_item *background; - struct zr_text text; - - ZR_ASSERT(ctx); - ZR_ASSERT(selected); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !selected) - return 0; - - win = ctx->current; - style = &ctx->style; - s = zr_widget(&header, ctx); - if (s == ZR_WIDGET_INVALID) - return 0; - - in = (win->layout->flags & ZR_WINDOW_ROM || s == ZR_WIDGET_ROM)? 0: &ctx->input; - if (zr_button_behavior(&ctx->last_widget_state, header, in, ZR_BUTTON_DEFAULT)) - is_active = zr_true; - - /* draw combo box header background and border */ - if (ctx->last_widget_state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->combo.active; - text.text = style->combo.label_active; - } else if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) { - background = &style->combo.hover; - text.text = style->combo.label_hover; - } else { - background = &style->combo.normal; - text.text = style->combo.label_normal; - } - if (background->type == ZR_STYLE_ITEM_IMAGE) { - text.background = zr_rgba(0,0,0,0); - zr_draw_image(&win->buffer, header, &background->data.image); - } else { - text.background = background->data.color; - zr_fill_rect(&win->buffer, header, style->combo.rounding, style->combo.border_color); - zr_fill_rect(&win->buffer, zr_shrink_rect(header, 1), style->combo.rounding, - background->data.color); - } - { - /* print currently selected text item */ - struct zr_rect label; - struct zr_rect button; - struct zr_rect content; - - enum zr_symbol_type sym; - if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - sym = style->combo.sym_hover; - else if (is_active) - sym = style->combo.sym_active; - else sym = style->combo.sym_normal; - - /* calculate button */ - button.w = header.h - 2 * style->combo.button_padding.y; - button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; - button.y = header.y + style->combo.button_padding.y; - button.h = button.w; - - content.x = button.x + style->combo.button.padding.x; - content.y = button.y + style->combo.button.padding.y; - content.w = button.w - 2 * style->combo.button.padding.x; - content.h = button.h - 2 * style->combo.button.padding.y; - - /* draw selected label */ - text.padding = zr_vec2(0,0); - label.x = header.x + style->combo.content_padding.x; - label.y = header.y + style->combo.content_padding.y; - label.w = button.x - (style->combo.content_padding.x + style->combo.spacing.x) - label.x;; - label.h = header.h - 2 * style->combo.content_padding.y; - zr_widget_text(&win->buffer, label, selected, len, &text, - ZR_TEXT_LEFT, &ctx->style.font); - - /* draw open/close button */ - zr_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, - &ctx->style.combo.button, sym, &style->font); - } - return zr_combo_begin(layout, ctx, win, height, is_active, header); -} - -int zr_combo_begin_label(struct zr_context *ctx, struct zr_panel *layout, - const char *selected, int max_height) -{return zr_combo_begin_text(ctx, layout, selected, zr_strlen(selected), max_height);} - -int -zr_combo_begin_color(struct zr_context *ctx, struct zr_panel *layout, - struct zr_color color, int height) -{ - struct zr_window *win; - struct zr_style *style; - const struct zr_input *in; - - struct zr_rect header; - int is_active = zr_false; - enum zr_widget_layout_states s; - const struct zr_style_item *background; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - s = zr_widget(&header, ctx); - if (s == ZR_WIDGET_INVALID) - return 0; - - in = (win->layout->flags & ZR_WINDOW_ROM || s == ZR_WIDGET_ROM)? 0: &ctx->input; - if (zr_button_behavior(&ctx->last_widget_state, header, in, ZR_BUTTON_DEFAULT)) - is_active = zr_true; - - /* draw combo box header background and border */ - if (ctx->last_widget_state & ZR_WIDGET_STATE_ACTIVE) - background = &style->combo.active; - else if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - background = &style->combo.hover; - else background = &style->combo.normal; - - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(&win->buffer, header, &background->data.image); - } else { - zr_fill_rect(&win->buffer, header, 0, style->combo.border_color); - zr_fill_rect(&win->buffer, zr_shrink_rect(header, 1), 0, - background->data.color); - } - { - struct zr_rect content; - struct zr_rect button; - struct zr_rect bounds; - - enum zr_symbol_type sym; - if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - sym = style->combo.sym_hover; - else if (is_active) - sym = style->combo.sym_active; - else sym = style->combo.sym_normal; - - /* calculate button */ - button.w = header.h - 2 * style->combo.button_padding.y; - button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; - button.y = header.y + style->combo.button_padding.y; - button.h = button.w; - - content.x = button.x + style->combo.button.padding.x; - content.y = button.y + style->combo.button.padding.y; - content.w = button.w - 2 * style->combo.button.padding.x; - content.h = button.h - 2 * style->combo.button.padding.y; - - /* draw color */ - bounds.h = header.h - 4 * style->combo.content_padding.y; - bounds.y = header.y + 2 * style->combo.content_padding.y; - bounds.x = header.x + 2 * style->combo.content_padding.x; - bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x; - zr_fill_rect(&win->buffer, bounds, 0, color); - - /* draw open/close button */ - zr_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, - &ctx->style.combo.button, sym, &style->font); - } - return zr_combo_begin(layout, ctx, win, height, is_active, header); -} - -int -zr_combo_begin_symbol(struct zr_context *ctx, struct zr_panel *layout, - enum zr_symbol_type symbol, int height) -{ - struct zr_window *win; - struct zr_style *style; - const struct zr_input *in; - - struct zr_rect header; - int is_active = zr_false; - enum zr_widget_layout_states s; - const struct zr_style_item *background; - struct zr_color sym_background; - struct zr_color symbol_color; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - s = zr_widget(&header, ctx); - if (s == ZR_WIDGET_INVALID) - return 0; - - in = (win->layout->flags & ZR_WINDOW_ROM || s == ZR_WIDGET_ROM)? 0: &ctx->input; - if (zr_button_behavior(&ctx->last_widget_state, header, in, ZR_BUTTON_DEFAULT)) - is_active = zr_true; - - /* draw combo box header background and border */ - if (ctx->last_widget_state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->combo.active; - symbol_color = style->combo.symbol_active; - } else if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) { - background = &style->combo.hover; - symbol_color = style->combo.symbol_hover; - } else { - background = &style->combo.normal; - symbol_color = style->combo.symbol_hover; - } - - if (background->type == ZR_STYLE_ITEM_IMAGE) { - sym_background = zr_rgba(0,0,0,0); - zr_draw_image(&win->buffer, header, &background->data.image); - } else { - sym_background = background->data.color; - zr_fill_rect(&win->buffer, header, 0, style->combo.border_color); - zr_fill_rect(&win->buffer, zr_shrink_rect(header, 1), 0, - background->data.color); - } - { - struct zr_rect bounds = {0,0,0,0}; - struct zr_rect content; - struct zr_rect button; - - enum zr_symbol_type sym; - if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - sym = style->combo.sym_hover; - else if (is_active) - sym = style->combo.sym_active; - else sym = style->combo.sym_normal; - - /* calculate button */ - button.w = header.h - 2 * style->combo.button_padding.y; - button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; - button.y = header.y + style->combo.button_padding.y; - button.h = button.w; - - content.x = button.x + style->combo.button.padding.x; - content.y = button.y + style->combo.button.padding.y; - content.w = button.w - 2 * style->combo.button.padding.x; - content.h = button.h - 2 * style->combo.button.padding.y; - - /* draw symbol */ - bounds.h = header.h - 2 * style->combo.content_padding.y; - bounds.y = header.y + style->combo.content_padding.y; - bounds.x = header.x + style->combo.content_padding.x; - bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; - zr_draw_symbol(&win->buffer, symbol, bounds, sym_background, symbol_color, - 1.0f, &style->font); - - /* draw open/close button */ - zr_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, - &ctx->style.combo.button, sym, &style->font); - } - return zr_combo_begin(layout, ctx, win, height, is_active, header); -} - -int -zr_combo_begin_symbol_text(struct zr_context *ctx, struct zr_panel *layout, - const char *selected, zr_size len, enum zr_symbol_type symbol, int height) -{ - struct zr_window *win; - struct zr_style *style; - struct zr_input *in; - - struct zr_rect header; - int is_active = zr_false; - enum zr_widget_layout_states s; - const struct zr_style_item *background; - struct zr_color symbol_color; - struct zr_text text; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - s = zr_widget(&header, ctx); - if (!s) return 0; - - in = (win->layout->flags & ZR_WINDOW_ROM || s == ZR_WIDGET_ROM)? 0: &ctx->input; - if (zr_button_behavior(&ctx->last_widget_state, header, in, ZR_BUTTON_DEFAULT)) - is_active = zr_true; - - /* draw combo box header background and border */ - if (ctx->last_widget_state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->combo.active; - symbol_color = style->combo.symbol_active; - text.text = style->combo.label_active; - } else if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) { - background = &style->combo.hover; - symbol_color = style->combo.symbol_hover; - text.text = style->combo.label_hover; - } else { - background = &style->combo.normal; - symbol_color = style->combo.symbol_normal; - text.text = style->combo.label_normal; - } - if (background->type == ZR_STYLE_ITEM_IMAGE) { - text.background = zr_rgba(0,0,0,0); - zr_draw_image(&win->buffer, header, &background->data.image); - } else { - text.background = background->data.color; - zr_fill_rect(&win->buffer, header, 0, style->combo.border_color); - zr_fill_rect(&win->buffer, zr_shrink_rect(header, 1), 0, - background->data.color); - } - { - struct zr_rect content; - struct zr_rect button; - struct zr_rect label; - struct zr_rect image; - - enum zr_symbol_type sym; - if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - sym = style->combo.sym_hover; - else if (is_active) - sym = style->combo.sym_active; - else sym = style->combo.sym_normal; - - /* calculate button */ - button.w = header.h - 2 * style->combo.button_padding.y; - button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; - button.y = header.y + style->combo.button_padding.y; - button.h = button.w; - - content.x = button.x + style->combo.button.padding.x; - content.y = button.y + style->combo.button.padding.y; - content.w = button.w - 2 * style->combo.button.padding.x; - content.h = button.h - 2 * style->combo.button.padding.y; - zr_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, - &ctx->style.combo.button, sym, &style->font); - - /* draw symbol */ - image.x = header.x + style->combo.content_padding.x; - image.y = header.y + style->combo.content_padding.y; - image.h = header.h - 2 * style->combo.content_padding.y; - image.w = image.h; - zr_draw_symbol(&win->buffer, symbol, image, text.background, symbol_color, - 1.0f, &style->font); - - /* draw label */ - text.padding = zr_vec2(0,0); - label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; - label.y = header.y + style->combo.content_padding.y; - label.w = (button.x - style->combo.content_padding.x) - label.x; - label.h = header.h - 2 * style->combo.content_padding.y; - zr_widget_text(&win->buffer, label, selected, len, &text, ZR_TEXT_LEFT, &style->font); - } - return zr_combo_begin(layout, ctx, win, height, is_active, header); -} - - -int -zr_combo_begin_image(struct zr_context *ctx, struct zr_panel *layout, - struct zr_image img, int height) -{ - struct zr_window *win; - struct zr_style *style; - const struct zr_input *in; - - struct zr_rect header; - int is_active = zr_false; - enum zr_widget_layout_states s; - const struct zr_style_item *background; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - s = zr_widget(&header, ctx); - if (s == ZR_WIDGET_INVALID) - return 0; - - in = (win->layout->flags & ZR_WINDOW_ROM || s == ZR_WIDGET_ROM)? 0: &ctx->input; - if (zr_button_behavior(&ctx->last_widget_state, header, in, ZR_BUTTON_DEFAULT)) - is_active = zr_true; - - /* draw combo box header background and border */ - if (ctx->last_widget_state & ZR_WIDGET_STATE_ACTIVE) - background = &style->combo.active; - else if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - background = &style->combo.hover; - else background = &style->combo.normal; - - if (background->type == ZR_STYLE_ITEM_IMAGE) { - zr_draw_image(&win->buffer, header, &background->data.image); - } else { - zr_fill_rect(&win->buffer, header, 0, style->combo.border_color); - zr_fill_rect(&win->buffer, zr_shrink_rect(header, 1), 0, - background->data.color); - } - { - struct zr_rect bounds = {0,0,0,0}; - struct zr_rect content; - struct zr_rect button; - - enum zr_symbol_type sym; - if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - sym = style->combo.sym_hover; - else if (is_active) - sym = style->combo.sym_active; - else sym = style->combo.sym_normal; - - /* calculate button */ - button.w = header.h - 2 * style->combo.button_padding.y; - button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; - button.y = header.y + style->combo.button_padding.y; - button.h = button.w; - - content.x = button.x + style->combo.button.padding.x; - content.y = button.y + style->combo.button.padding.y; - content.w = button.w - 2 * style->combo.button.padding.x; - content.h = button.h - 2 * style->combo.button.padding.y; - - /* draw image */ - bounds.h = header.h - 2 * style->combo.content_padding.y; - bounds.y = header.y + style->combo.content_padding.y; - bounds.x = header.x + style->combo.content_padding.x; - bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; - zr_draw_image(&win->buffer, bounds, &img); - - /* draw open/close button */ - zr_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, - &ctx->style.combo.button, sym, &style->font); - } - return zr_combo_begin(layout, ctx, win, height, is_active, header); -} - -int -zr_combo_begin_image_text(struct zr_context *ctx, struct zr_panel *layout, - const char *selected, zr_size len, struct zr_image img, int height) -{ - struct zr_window *win; - struct zr_style *style; - struct zr_input *in; - - struct zr_rect header; - int is_active = zr_false; - enum zr_widget_layout_states s; - const struct zr_style_item *background; - struct zr_text text; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - s = zr_widget(&header, ctx); - if (!s) return 0; - - in = (win->layout->flags & ZR_WINDOW_ROM || s == ZR_WIDGET_ROM)? 0: &ctx->input; - if (zr_button_behavior(&ctx->last_widget_state, header, in, ZR_BUTTON_DEFAULT)) - is_active = zr_true; - - /* draw combo box header background and border */ - if (ctx->last_widget_state & ZR_WIDGET_STATE_ACTIVE) { - background = &style->combo.active; - text.text = style->combo.label_active; - } else if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) { - background = &style->combo.hover; - text.text = style->combo.label_hover; - } else { - background = &style->combo.normal; - text.text = style->combo.label_normal; - } - if (background->type == ZR_STYLE_ITEM_IMAGE) { - text.background = zr_rgba(0,0,0,0); - zr_draw_image(&win->buffer, header, &background->data.image); - } else { - text.background = background->data.color; - zr_fill_rect(&win->buffer, header, 0, style->combo.border_color); - zr_fill_rect(&win->buffer, zr_shrink_rect(header, 1), 0, - background->data.color); - } - - { - struct zr_rect content; - struct zr_rect button; - struct zr_rect label; - struct zr_rect image; - - enum zr_symbol_type sym; - if (ctx->last_widget_state & ZR_WIDGET_STATE_HOVERED) - sym = style->combo.sym_hover; - else if (is_active) - sym = style->combo.sym_active; - else sym = style->combo.sym_normal; - - /* calculate button */ - button.w = header.h - 2 * style->combo.button_padding.y; - button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; - button.y = header.y + style->combo.button_padding.y; - button.h = button.w; - - content.x = button.x + style->combo.button.padding.x; - content.y = button.y + style->combo.button.padding.y; - content.w = button.w - 2 * style->combo.button.padding.x; - content.h = button.h - 2 * style->combo.button.padding.y; - zr_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, - &ctx->style.combo.button, sym, &style->font); - - /* draw image */ - image.x = header.x + style->combo.content_padding.x; - image.y = header.y + style->combo.content_padding.y; - image.h = header.h - 2 * style->combo.content_padding.y; - image.w = image.h; - zr_draw_image(&win->buffer, image, &img); - - /* draw label */ - text.padding = zr_vec2(0,0); - label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; - label.y = header.y + style->combo.content_padding.y; - label.w = (button.x - style->combo.content_padding.x) - label.x; - label.h = header.h - 2 * style->combo.content_padding.y; - zr_widget_text(&win->buffer, label, selected, len, &text, ZR_TEXT_LEFT, &style->font); - } - return zr_combo_begin(layout, ctx, win, height, is_active, header); -} - -int zr_combo_begin_symbol_label(struct zr_context *ctx, struct zr_panel *layout, - const char *selected, enum zr_symbol_type type, int height) -{return zr_combo_begin_symbol_text(ctx, layout, selected, zr_strlen(selected), type, height);} - -int zr_combo_begin_image_label(struct zr_context *ctx, struct zr_panel *layout, - const char *selected, struct zr_image img, int height) -{return zr_combo_begin_image_text(ctx, layout, selected, zr_strlen(selected), img, height);} - -int zr_combo_item_text(struct zr_context *ctx, const char *text, zr_size len,zr_flags align) -{return zr_contextual_item_text(ctx, text, len, align);} - -int zr_combo_item_label(struct zr_context *ctx, const char *label, zr_flags align) -{return zr_contextual_item_label(ctx, label, align);} - -int zr_combo_item_image_text(struct zr_context *ctx, struct zr_image img, const char *text, - zr_size len, zr_flags alignment) -{return zr_contextual_item_image_text(ctx, img, text, len, alignment);} - -int zr_combo_item_image_label(struct zr_context *ctx, struct zr_image img, - const char *text, zr_flags alignment) -{return zr_contextual_item_image_label(ctx, img, text, alignment);} - -int zr_combo_item_symbol_text(struct zr_context *ctx, enum zr_symbol_type sym, - const char *text, zr_size len, zr_flags alignment) -{return zr_contextual_item_symbol_text(ctx, sym, text, len, alignment);} - -int zr_combo_item_symbol_label(struct zr_context *ctx, enum zr_symbol_type sym, - const char *label, zr_flags alignment) -{return zr_contextual_item_symbol_label(ctx, sym, label, alignment);} - -void zr_combo_end(struct zr_context *ctx) -{zr_contextual_end(ctx);} - -void zr_combo_close(struct zr_context *ctx) -{zr_contextual_close(ctx);} - -int -zr_combo(struct zr_context *ctx, const char **items, int count, - int selected, int item_height) -{ - int i = 0; - int max_height; - struct zr_panel combo; - float item_padding; - float window_padding; - - ZR_ASSERT(ctx); - ZR_ASSERT(items); - if (!ctx || !items ||!count) - return selected; - - item_padding = ctx->style.combo.button_padding.y; - window_padding = ctx->style.window.padding.y; - max_height = (count+1) * item_height + (int)item_padding * 3 + (int)window_padding * 2; - if (zr_combo_begin_label(ctx, &combo, items[selected], max_height)) { - zr_layout_row_dynamic(ctx, (float)item_height, 1); - for (i = 0; i < count; ++i) { - if (zr_combo_item_label(ctx, items[i], ZR_TEXT_LEFT)) - selected = i; - } - zr_combo_end(ctx); - } - return selected; -} - -int -zr_combo_seperator(struct zr_context *ctx, const char *items_seperated_by_seperator, - int seperator, int selected, int count, int item_height) -{ - int i; - int max_height; - struct zr_panel combo; - float item_padding; - float window_padding; - const char *current_item; - const char *iter; - zr_size length = 0; - - ZR_ASSERT(ctx); - ZR_ASSERT(items_seperated_by_seperator); - if (!ctx || !items_seperated_by_seperator) - return selected; - - /* calculate popup window */ - item_padding = ctx->style.combo.content_padding.y; - window_padding = ctx->style.window.padding.y; - max_height = (count+1) * item_height + (int)item_padding * 3 + (int)window_padding * 2; - - /* find selected item */ - current_item = items_seperated_by_seperator; - for (i = 0; i < selected; ++i) { - iter = current_item; - while (*iter != seperator) iter++; - length = (zr_size)(iter - current_item); - current_item = iter + 1; - } - - if (zr_combo_begin_text(ctx, &combo, current_item, length, max_height)) { - current_item = items_seperated_by_seperator; - zr_layout_row_dynamic(ctx, (float)item_height, 1); - for (i = 0; i < count; ++i) { - iter = current_item; - while (*iter != seperator) iter++; - length = (zr_size)(iter - current_item); - if (zr_combo_item_text(ctx, current_item, length, ZR_TEXT_LEFT)) - selected = i; - current_item = current_item + length + 1; - } - zr_combo_end(ctx); - } - return selected; -} - -int -zr_combo_string(struct zr_context *ctx, const char *items_seperated_by_zeros, - int selected, int count, int item_height) -{return zr_combo_seperator(ctx, items_seperated_by_zeros, '\0', selected, count, item_height);} - -int -zr_combo_callback(struct zr_context *ctx, void(item_getter)(void*, int, const char**), - void *userdata, int selected, int count, int item_height) -{ - int i; - int max_height; - struct zr_panel combo; - float item_padding; - float window_padding; - const char *item; - - ZR_ASSERT(ctx); - ZR_ASSERT(item_getter); - if (!ctx || !item_getter) - return selected; - - /* calculate popup window */ - item_padding = ctx->style.combo.content_padding.y; - window_padding = ctx->style.window.padding.y; - max_height = (count+1) * item_height + (int)item_padding * 3 + (int)window_padding * 2; - - item_getter(userdata, selected, &item); - if (zr_combo_begin_label(ctx, &combo, item, max_height)) { - zr_layout_row_dynamic(ctx, (float)item_height, 1); - for (i = 0; i < count; ++i) { - item_getter(userdata, i, &item); - if (zr_combo_item_label(ctx, item, ZR_TEXT_LEFT)) - selected = i; - } - zr_combo_end(ctx); - } - return selected; -} - -void zr_combobox(struct zr_context *ctx, const char **items, int count, - int *selected, int item_height) -{*selected = zr_combo(ctx, items, count, *selected, item_height);} - -void zr_combobox_string(struct zr_context *ctx, const char *items_seperated_by_zeros, - int *selected, int count, int item_height) -{*selected = zr_combo_string(ctx, items_seperated_by_zeros, *selected, count, item_height);} - -void zr_combobox_seperator(struct zr_context *ctx, const char *items_seperated_by_seperator, - int seperator,int *selected, int count, int item_height) -{*selected = zr_combo_seperator(ctx, items_seperated_by_seperator, seperator, - *selected, count, item_height);} - -void zr_combobox_callback(struct zr_context *ctx, - void(item_getter)(void* data, int id, const char **out_text), - void *userdata, int *selected, int count, int item_height) -{*selected = zr_combo_callback(ctx, item_getter, userdata, *selected, count, item_height);} - -/* - * ------------------------------------------------------------- - * - * MENU - * - * -------------------------------------------------------------- - */ -static int -zr_menu_begin(struct zr_panel *layout, struct zr_context *ctx, struct zr_window *win, - const char *id, int is_clicked, struct zr_rect header, float width) -{ - int is_open = 0; - int is_active = 0; - struct zr_rect body; - struct zr_window *popup; - zr_hash hash = zr_murmur_hash(id, (int)zr_strlen(id), ZR_WINDOW_MENU); - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - body.x = header.x; - body.w = width; - body.y = header.y + header.h; - body.h = (win->layout->bounds.y + win->layout->bounds.h) - body.y; - - popup = win->popup.win; - is_open = (popup && (popup->flags & ZR_WINDOW_MENU)); - is_active = (popup && (win->popup.name == hash) && win->popup.type == ZR_WINDOW_MENU); - if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || - (!is_open && !is_active && !is_clicked)) return 0; - if (!zr_nonblock_begin(layout, ctx, ZR_WINDOW_MENU|ZR_WINDOW_NO_SCROLLBAR, body, header)) - return 0; - win->popup.type = ZR_WINDOW_MENU; - win->popup.name = hash; - return 1; -} - -int -zr_menu_begin_text(struct zr_context *ctx, struct zr_panel *layout, - const char *title, zr_size len, zr_flags align, float width) -{ - struct zr_window *win; - const struct zr_input *in; - struct zr_rect header; - int is_clicked = zr_false; - zr_flags state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = zr_widget(&header, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || win->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_text(&ctx->last_widget_state, &win->buffer, header, - title, len, align, ZR_BUTTON_DEFAULT, &ctx->style.menu_button, in, &ctx->style.font)) - is_clicked = zr_true; - return zr_menu_begin(layout, ctx, win, title, is_clicked, header, width); -} - -int zr_menu_begin_label(struct zr_context *ctx, struct zr_panel *layout, - const char *text, zr_flags align, float width) -{return zr_menu_begin_text(ctx, layout, text, zr_strlen(text), align, width);} - -int -zr_menu_begin_image(struct zr_context *ctx, struct zr_panel *layout, - const char *id, struct zr_image img, float width) -{ - struct zr_window *win; - struct zr_rect header; - const struct zr_input *in; - int is_clicked = zr_false; - zr_flags state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = zr_widget(&header, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_image(&ctx->last_widget_state, &win->buffer, header, - img, ZR_BUTTON_DEFAULT, &ctx->style.menu_button, in)) - is_clicked = zr_true; - return zr_menu_begin(layout, ctx, win, id, is_clicked, header, width); -} - -int -zr_menu_begin_symbol(struct zr_context *ctx, struct zr_panel *layout, - const char *id, enum zr_symbol_type sym, float width) -{ - struct zr_window *win; - const struct zr_input *in; - struct zr_rect header; - int is_clicked = zr_false; - zr_flags state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = zr_widget(&header, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, - sym, ZR_BUTTON_DEFAULT, &ctx->style.menu_button, in, &ctx->style.font)) - is_clicked = zr_true; - return zr_menu_begin(layout, ctx, win, id, is_clicked, header, width); -} - -int -zr_menu_begin_image_text(struct zr_context *ctx, struct zr_panel *layout, - const char *title, zr_size len, zr_flags align, struct zr_image img, float width) -{ - struct zr_window *win; - struct zr_rect header; - const struct zr_input *in; - int is_clicked = zr_false; - zr_flags state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = zr_widget(&header, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_text_image(&ctx->last_widget_state, &win->buffer, - header, img, title, len, align, ZR_BUTTON_DEFAULT, &ctx->style.menu_button, - &ctx->style.font, in)) - is_clicked = zr_true; - return zr_menu_begin(layout, ctx, win, title, is_clicked, header, width); -} - -int zr_menu_begin_image_label(struct zr_context *ctx, struct zr_panel *layout, - const char *title, zr_flags align, struct zr_image img, float width) -{return zr_menu_begin_image_text(ctx, layout, title, zr_strlen(title), align, img, width);} - -int -zr_menu_begin_symbol_text(struct zr_context *ctx, struct zr_panel *layout, - const char *title, zr_size size, zr_flags align, enum zr_symbol_type sym, float width) -{ - struct zr_window *win; - struct zr_rect header; - const struct zr_input *in; - int is_clicked = zr_false; - zr_flags state; - - ZR_ASSERT(ctx); - ZR_ASSERT(ctx->current); - ZR_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = zr_widget(&header, ctx); - if (!state) return 0; - in = (state == ZR_WIDGET_ROM || win->layout->flags & ZR_WINDOW_ROM) ? 0 : &ctx->input; - if (zr_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, - header, sym, title, size, align, ZR_BUTTON_DEFAULT, &ctx->style.menu_button, - &ctx->style.font, in)) - is_clicked = zr_true; - return zr_menu_begin(layout, ctx, win, title, is_clicked, header, width); -} - -int zr_menu_begin_symbol_label(struct zr_context *ctx, struct zr_panel *layout, - const char *title, zr_flags align, enum zr_symbol_type sym, float width) -{return zr_menu_begin_symbol_text(ctx, layout, title, zr_strlen(title), align,sym, width);} - -int zr_menu_item_text(struct zr_context *ctx, const char *title, zr_size len, zr_flags align) -{return zr_contextual_item_text(ctx, title, len, align);} - -int zr_menu_item_label(struct zr_context *ctx, const char *label, zr_flags align) -{return zr_contextual_item_label(ctx, label, align);} - -int zr_menu_item_image_label(struct zr_context *ctx, struct zr_image img, - const char *label, zr_flags align) -{return zr_contextual_item_image_label(ctx, img, label, align);} - -int zr_menu_item_image_text(struct zr_context *ctx, struct zr_image img, - const char *text, zr_size len, zr_flags align) -{return zr_contextual_item_image_text(ctx, img, text, len, align);} - -int zr_menu_item_symbol_text(struct zr_context *ctx, enum zr_symbol_type sym, - const char *text, zr_size len, zr_flags align) -{return zr_contextual_item_symbol_text(ctx, sym, text, len, align);} - -int zr_menu_item_symbol_label(struct zr_context *ctx, enum zr_symbol_type sym, - const char *label, zr_flags align) -{return zr_contextual_item_symbol_label(ctx, sym, label, align);} - -void zr_menu_close(struct zr_context *ctx) -{zr_contextual_close(ctx);} - -void -zr_menu_end(struct zr_context *ctx) -{zr_contextual_end(ctx);} - diff --git a/zahnrad.h b/zahnrad.h deleted file mode 100644 index 52370cb..0000000 --- a/zahnrad.h +++ /dev/null @@ -1,2398 +0,0 @@ -/* - Copyright (c) 2016 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. -*/ -#ifndef ZR_H_ -#define ZR_H_ - -#ifdef __cplusplus -extern "C" { -#endif -/* - * ============================================================== - * - * CONSTANTS - * - * =============================================================== - */ -#define ZR_UTF_INVALID 0xFFFD -#define ZR_UTF_SIZE 4 -/* describes the number of bytes a glyph consists of*/ -#define ZR_INPUT_MAX 16 -/* defines the max number of bytes to be added as text input in one frame */ -#define ZR_MAX_COLOR_STACK 32 -/* Number of temporary configuration color changes that can be stored */ -#define ZR_MAX_ATTRIB_STACK 32 -/* Number of temporary configuration attribute changes that can be stored */ -#define ZR_MAX_FONT_STACK 32 -/* Number of temporary configuration user font changes that can be stored */ -#define ZR_MAX_FONT_HEIGHT_STACK 32 -/* Number of temporary configuration font height changes that can be stored */ -#define ZR_MAX_NUMBER_BUFFER 64 -/* Buffer size for the conversion buffer between float and string */ -#define ZR_BUFFER_DEFAULT_INITIAL_SIZE (4*1024) -/* Initial buffer size for buffer with default allocator */ -/* - * ============================================================== - * - * COMPILING FLAGS - * - * =============================================================== - */ -#define ZR_COMPILE_WITH_FIXED_TYPES 1 -/* setting this define to 1 adds header for fixed sized types - * if 0 each type has to be set to the correct size */ -#define ZR_COMPILE_WITH_ASSERT 1 -/* setting this define to 1 adds header for the assert macro - IMPORTANT: it also adds the standard library assert so only use it if wanted */ -#define ZR_COMPILE_WITH_DEFAULT_ALLOCATOR 1 -/* setting this to 1 adds default allocator functions to ease memory management - * if 0 you either have to provide a fixed size memory block or a custom allocator. - * IMPORTANT: this adds with malloc and free so set 0 if you don't want - * to link to the standard library!*/ -#define ZR_COMPILE_WITH_STANDARD_IO 1 -/* setting this to 1 includes standard file IO and variable arguments - * IMPORTANT: this adds with fopen,fclose,... and so set 0 - * if you don't want to link to the standard library!*/ -#define ZR_COMPILE_WITH_VERTEX_BUFFER 1 -/* setting this to 1 adds a vertex draw command list backend to this - library, which allows you to convert queue commands into vertex draw commands. - If you do not want or need a default backend you can set this flag to zero - and the module of the library will not be compiled */ -#define ZR_COMPILE_WITH_FONT 1 -/* setting this to 1 adds the `stb_truetype` and `stb_rect_pack` header - to this library and provides a default font for font loading and rendering. - If you already have font handling or do not want to use this font handler - you can just set this define to zero and the font module will not be compiled - and the two headers will not be needed. */ -#define ZR_DISABLE_STB_RECT_PACK_IMPLEMENTATION 0 -/* If you already provide the implementation for stb_rect_pack.h in one of your - files you have to define this as 1 to prevent another implementation and the - resulting symbol collision. */ -#define ZR_DISABLE_STB_TRUETYPE_IMPLEMENTATION 0 -/* If you already provide the implementation for stb_truetype.h in one of your - files you have to define this as 1 to prevent another implementation and the - resulting symbol collision. */ -#define ZR_COMPILE_WITH_DEFAULT_FONT 1 -/* setting this to 1 adds a default font into this library which can be loaded - * into a font atlas and allows using this library without having a truetype font - * IMPORTANT: enableing this requires ~12kb global stack memory. */ -#define ZR_COMPILE_WITH_COMMAND_USERDATA 0 -/* Activating this adds a userdata pointer into each command. Can be usefull if - * you want to provide custom shader depending on the used widget. Can be combined - * with the style structures. */ -/* - * =============================================================== - * - * BASIC - * - * =============================================================== - */ -#if ZR_COMPILE_WITH_FIXED_TYPES -#include -typedef int32_t zr_int; -typedef uint32_t zr_uint; -typedef uint32_t zr_hash; -typedef uintptr_t zr_size; -typedef uintptr_t zr_ptr; -typedef uint32_t zr_flags; -typedef uint32_t zr_rune; -typedef uint8_t zr_byte; -#else -typedef int zr_int; -typedef unsigned int zr_uint; -typedef unsigned int zr_hash; -typedef unsigned long zr_size; -typedef zr_size zr_ptr; -typedef unsigned int zr_flags; -typedef unsigned int zr_rune; -typedef unsigned char zr_byte; -#endif - -#if ZR_COMPILE_WITH_ASSERT -#ifndef ZR_ASSERT -#include -#define ZR_ASSERT(expr) assert(expr) -#endif -#else -#define ZR_ASSERT(expr) -#endif - -struct zr_user_font; -struct zr_edit_box; -struct zr_user_font_glyph; - -/* =============================================================== - * UTILITY - * ===============================================================*/ -#define ZR_UNDEFINED (-1.0f) -#define ZR_FLAG(x) (1 << (x)) - -typedef zr_size zr_element; -enum {zr_false, zr_true}; -struct zr_color {zr_byte r,g,b,a;}; -struct zr_vec2 {float x,y;}; -struct zr_vec2i {short x, y;}; -struct zr_rect {float x,y,w,h;}; -struct zr_recti {short x,y,w,h;}; -typedef char zr_glyph[ZR_UTF_SIZE]; -typedef union {void *ptr; int id;} zr_handle; -struct zr_image {zr_handle handle;unsigned short w,h;unsigned short region[4];}; -struct zr_scroll {unsigned short x, y;}; -enum zr_heading {ZR_UP, ZR_RIGHT, ZR_DOWN, ZR_LEFT}; - -/* math */ -zr_hash zr_murmur_hash(const void *key, int len, zr_hash seed); -void zr_triangle_from_direction(struct zr_vec2 *result, struct zr_rect r, - float pad_x, float pad_y, enum zr_heading); - -struct zr_vec2 zr_vec2(float x, float y); -struct zr_vec2 zr_vec2i(int x, int y); -struct zr_vec2 zr_vec2v(const float *xy); -struct zr_vec2 zr_vec2iv(const int *xy); - -struct zr_rect zr_get_null_rect(void); -struct zr_rect zr_rect(float x, float y, float w, float h); -struct zr_rect zr_recti(int x, int y, int w, int h); -struct zr_rect zr_recta(struct zr_vec2 pos, struct zr_vec2 size); -struct zr_rect zr_rectv(const float *xywh); -struct zr_rect zr_rectiv(const int *xywh); - -/* string*/ -zr_size zr_strlen(const char *str); -int zr_stricmp(const char *s1, const char *s2); -int zr_stricmpn(const char *s1, const char *s2, int n); -int zr_strtof(float *number, const char *buffer); -int zr_strfilter(const char *text, const char *regexp); -int zr_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score); -int zr_strmatch_fuzzy_text(const char *txt, int txt_len, const char *pattern, - int *out_score); -#if ZR_COMPILE_WITH_STANDARD_IO -int zr_strfmt(char *buf, zr_size len, const char *fmt,...); -#endif - -/* UTF-8 */ -zr_size zr_utf_decode(const char*, zr_rune*, zr_size); -zr_size zr_utf_encode(zr_rune, char*, zr_size); -zr_size zr_utf_len(const char*, zr_size byte_len); -const char* zr_utf_at(const char *buffer, zr_size length, int index, - zr_rune *unicode, zr_size *len); - -/* color (conversion user --> zahnrad) */ -struct zr_color zr_rgb(int r, int g, int b); -struct zr_color zr_rgb_iv(const int *rgb); -struct zr_color zr_rgb_bv(const zr_byte* rgb); -struct zr_color zr_rgb_f(float r, float g, float b); -struct zr_color zr_rgb_fv(const float *rgb); -struct zr_color zr_rgb_hex(const char *rgb); - -struct zr_color zr_rgba(int r, int g, int b, int a); -struct zr_color zr_rgba_u32(zr_uint); -struct zr_color zr_rgba_iv(const int *rgba); -struct zr_color zr_rgba_bv(const zr_byte *rgba); -struct zr_color zr_rgba_f(float r, float g, float b, float a); -struct zr_color zr_rgba_fv(const float *rgba); -struct zr_color zr_rgba_hex(const char *rgb); - -struct zr_color zr_hsv(int h, int s, int v); -struct zr_color zr_hsv_iv(const int *hsv); -struct zr_color zr_hsv_bv(const zr_byte *hsv); -struct zr_color zr_hsv_f(float h, float s, float v); -struct zr_color zr_hsv_fv(const float *hsv); - -struct zr_color zr_hsva(int h, int s, int v, int a); -struct zr_color zr_hsva_iv(const int *hsva); -struct zr_color zr_hsva_bv(const zr_byte *hsva); -struct zr_color zr_hsva_f(float h, float s, float v, float a); -struct zr_color zr_hsva_fv(const float *hsva); - -/* color (conversion zahnrad --> user) */ -void zr_color_f(float *r, float *g, float *b, float *a, struct zr_color); -void zr_color_fv(float *rgba_out, struct zr_color); -zr_uint zr_color_u32(struct zr_color); -void zr_color_hex_rgba(char *output, struct zr_color); -void zr_color_hex_rgb(char *output, struct zr_color); - -void zr_color_hsv_i(int *out_h, int *out_s, int *out_v, struct zr_color); -void zr_color_hsv_b(zr_byte *out_h, zr_byte *out_s, zr_byte *out_v, struct zr_color); -void zr_color_hsv_iv(int *hsv_out, struct zr_color); -void zr_color_hsv_bv(zr_byte *hsv_out, struct zr_color); -void zr_color_hsv_f(float *out_h, float *out_s, float *out_v, struct zr_color); -void zr_color_hsv_fv(float *hsv_out, struct zr_color); - -void zr_color_hsva_i(int *h, int *s, int *v, int *a, struct zr_color); -void zr_color_hsva_b(zr_byte *h, zr_byte *s, zr_byte *v, zr_byte *a, struct zr_color); -void zr_color_hsva_iv(int *hsva_out, struct zr_color); -void zr_color_hsva_bv(zr_byte *hsva_out, struct zr_color); -void zr_color_hsva_f(float *out_h, float *out_s, float *out_v, - float *out_a, struct zr_color); -void zr_color_hsva_fv(float *hsva_out, struct zr_color); - -/* image */ -zr_handle zr_handle_ptr(void*); -zr_handle zr_handle_id(int); -struct zr_image zr_image_ptr(void*); -struct zr_image zr_image_id(int); -int zr_image_is_subimage(const struct zr_image* img); -struct zr_image zr_subimage_ptr(void*, unsigned short w, unsigned short h, - struct zr_rect sub_region); -struct zr_image zr_subimage_id(int, unsigned short w, unsigned short h, - struct zr_rect sub_region); - -/* ============================================================== - * - * MEMORY BUFFER - * - * ===============================================================*/ -/* A basic (double)-buffer with linear allocation and resetting as only - freeing policy. The buffers main purpose is to control all memory management - inside the GUI toolkit and still leave memory control as much as possible in - the hand of the user while also making sure the library is easy to use if - not as much control is needed. - In general all memory inside this library can be provided from the user in - three different ways. - - The first way and the one providing most control is by just passing a fixed - size memory block. In this case all control lies in the hand of the user - since he can exactly control where the memory comes from and how much memory - the library should consume. Of course using the fixed size API removes the - ability to automatically resize a buffer if not enough memory is provided so - you have to take over the resizing. While being a fixed sized buffer sounds - quite limiting, it is very effective in this library since the actual memory - consumption is quite stable and has a fixed upper bound for a lot of cases. - - If you don't want to think about how much memory the library should allocate - at all time or have a very dynamic UI with unpredictable memory consumoption - habits but still want control over memory allocation you can use the dynamic - allocator based API. The allocator consists of two callbacks for allocating - and freeing memory and optional userdata so you can plugin your own allocator. - - The final and easiest way can be used by defining - ZR_COMPILE_WITH_DEFAULT_ALLOCATOR as 1 which uses the standard library memory - allocation functions malloc and free and takes over complete control over - memory in this library. -*/ -struct zr_memory_status { - void *memory; - unsigned int type; - zr_size size; - zr_size allocated; - zr_size needed; - zr_size calls; -}; - -struct zr_allocator { - zr_handle userdata; - void*(*alloc)(zr_handle, zr_size); - void(*free)(zr_handle, void*); -}; - -enum zr_allocation_type { - ZR_BUFFER_FIXED, - ZR_BUFFER_DYNAMIC -}; - -enum zr_buffer_allocation_type { - ZR_BUFFER_FRONT, - ZR_BUFFER_BACK, - ZR_BUFFER_MAX -}; - -struct zr_buffer_marker { - int active; - zr_size offset; -}; - -struct zr_memory {void *ptr;zr_size size;}; -struct zr_buffer { - struct zr_buffer_marker marker[ZR_BUFFER_MAX]; - /* buffer marker to free a buffer to a certain offset */ - struct zr_allocator pool; - /* allocator callback for dynamic buffers */ - enum zr_allocation_type type; - /* memory management type */ - struct zr_memory memory; - /* memory and size of the current memory block */ - float grow_factor; - /* growing factor for dynamic memory management */ - zr_size allocated; - /* total amount of memory allocated */ - zr_size needed; - /* totally consumed memory given that enough memory is present */ - zr_size calls; - /* number of allocation calls */ - zr_size size; - /* current size of the buffer */ -}; - -#if ZR_COMPILE_WITH_DEFAULT_ALLOCATOR -void zr_buffer_init_default(struct zr_buffer*); -#endif -void zr_buffer_init(struct zr_buffer*, const struct zr_allocator*, zr_size size); -void zr_buffer_init_fixed(struct zr_buffer*, void *memory, zr_size size); -void zr_buffer_info(struct zr_memory_status*, struct zr_buffer*); -void zr_buffer_push(struct zr_buffer*, enum zr_buffer_allocation_type type, - void *memory, zr_size size, zr_size align); -void zr_buffer_mark(struct zr_buffer*, enum zr_buffer_allocation_type type); -void zr_buffer_reset(struct zr_buffer*, enum zr_buffer_allocation_type type); -void zr_buffer_clear(struct zr_buffer*); -void zr_buffer_free(struct zr_buffer*); -void *zr_buffer_memory(struct zr_buffer*); -const void *zr_buffer_memory_const(const struct zr_buffer*); -zr_size zr_buffer_total(struct zr_buffer*); - -/* =============================================================== - * - * FONT - * - * ===============================================================*/ -/* Font handling in this library was designed to be quite customizeable and lets - you decide what you want to use and what you want to provide. In this sense - there are four different degrees between control and ease of use and two - different drawing APIs to provide for. - - So first of the easiest way to do font handling is by just providing a - `zr_user_font` struct which only requires the height in pixel of the used - font and a callback to calculate the width of a string. This way of handling - fonts is best fitted for using the normal draw shape command API were you - do all the text drawing yourself and the library does not require any kind - of deeper knowledge about which font handling mechanism you use. - - While the first approach works fine if you don't want to use the optional - vertex buffer output it is not enough if you do. To get font handling working - for these cases you have to provide to additional parameter inside the - `zr_user_font`. First a texture atlas handle used to draw text as subimages - of a bigger font atlas texture and a callback to query a characters glyph - information (offset, size, ...). So it is still possible to provide your own - font and use the vertex buffer output. - - The final approach if you do not have a font handling functionality or don't - want to use it in this library is by using the optional font baker. This API - is divided into a high- and low-level API with different priorites between - ease of use and control. Both API's can be used to create a font and - font atlas texture and can even be used with or without the vertex buffer - output. So it still uses the `zr_user_font` struct and the two different - approaches previously stated still work. - Now to the difference between the low level API and the high level API. The low - level API provides a lot of control over the baking process of the font and - provides total control over memory. It consists of a number of functions that - need to be called from begin to end and each step requires some additional - configuration, so it is a lot more complex than the high-level API. - If you don't want to do all the work required for using the low-level API - you can use the font atlas API. It provides the same functionality as the - low-level API but takes away some configuration and all of memory control and - in term provides a easier to use API. -*/ -typedef zr_size(*zr_text_width_f)(zr_handle, float h, const char*, zr_size len); -typedef void(*zr_query_font_glyph_f)(zr_handle handle, float font_height, - struct zr_user_font_glyph *glyph, - zr_rune codepoint, zr_rune next_codepoint); - -#if ZR_COMPILE_WITH_VERTEX_BUFFER -struct zr_user_font_glyph { - struct zr_vec2 uv[2]; - /* texture coordinates */ - struct zr_vec2 offset; - /* offset between top left and glyph */ - float width, height; - /* size of the glyph */ - float xadvance; - /* offset to the next glyph */ -}; -#endif - -struct zr_user_font { - zr_handle userdata; - /* user provided font handle */ - float height; - /* max height of the font */ - zr_text_width_f width; - /* font string width in pixel callback */ -#if ZR_COMPILE_WITH_VERTEX_BUFFER - zr_query_font_glyph_f query; - /* font glyph callback to query drawing info */ - zr_handle texture; - /* texture handle to the used font atlas or texture */ -#endif -}; - -#ifdef ZR_COMPILE_WITH_FONT -enum zr_font_coord_type { - ZR_COORD_UV, - /* texture coordinates inside font glyphs are clamped between 0-1 */ - ZR_COORD_PIXEL - /* texture coordinates inside font glyphs are in absolute pixel */ -}; - -struct zr_baked_font { - float height; - /* height of the font */ - float ascent, descent; - /* font glyphs ascent and descent */ - zr_rune glyph_offset; - /* glyph array offset inside the font glyph baking output array */ - zr_rune glyph_count; - /* number of glyphs of this font inside the glyph baking array output */ - const zr_rune *ranges; - /* font codepoint ranges as pairs of (from/to) and 0 as last element */ -}; - -struct zr_font_config { - void *ttf_blob; - /* pointer to loaded TTF file memory block. - * NOTE: not needed for zr_font_atlas_add_from_memory and zr_font_atlas_add_from_file. */ - zr_size ttf_size; - /* size of the loaded TTF file memory block - * NOTE: not needed for zr_font_atlas_add_from_memory and zr_font_atlas_add_from_file. */ - int ttf_data_owned_by_atlas; - /* used inside font atlas: default to: 0*/ - float size; - /* baked pixel height of the font */ - unsigned int oversample_h, oversample_v; - /* rasterize at hight quality for sub-pixel position */ - int pixel_snap; - /* align very character to pixel boundry (if true set oversample (1,1)) */ - enum zr_font_coord_type coord_type; - /* texture coordinate format with either pixel or UV coordinates */ - struct zr_vec2 spacing; - /* extra pixel spacing between glyphs */ - const zr_rune *range; - /* list of unicode ranges (2 values per range, zero terminated) */ - struct zr_baked_font *font; - /* font to setup in the baking process: NOTE: not needed for font atlas */ - zr_rune fallback_glyph; - /* fallback glyph to use if a given rune is not found */ - int merge_mode; - /* merges this font into the last font */ -}; - -struct zr_font_glyph { - zr_rune codepoint; - float xadvance; - float x0, y0, x1, y1, w, h; - float u0, v0, u1, v1; -}; - -struct zr_font { - struct zr_user_font handle; - struct zr_baked_font info; - float scale; - struct zr_font_glyph *glyphs; - const struct zr_font_glyph *fallback; - zr_rune fallback_codepoint; - zr_handle texture; - int config; -}; - -enum zr_font_atlas_format { - ZR_FONT_ATLAS_ALPHA8, - ZR_FONT_ATLAS_RGBA32 -}; - -struct zr_draw_null_texture { - zr_handle texture; - /* texture handle to a texture with a white pixel */ - struct zr_vec2 uv; - /* coordinates to a white pixel in the texture */ -}; - -struct zr_font_atlas { - void *pixel; - int tex_width; - int tex_height; - struct zr_allocator alloc; - struct zr_recti custom; - - int glyph_count; - struct zr_font_glyph *glyphes; - struct zr_font **fonts; - struct zr_font_config *config; - int font_num, font_cap; -}; - -/* some language glyph codepoint ranges */ -const zr_rune *zr_font_default_glyph_ranges(void); -const zr_rune *zr_font_chinese_glyph_ranges(void); -const zr_rune *zr_font_cyrillic_glyph_ranges(void); -const zr_rune *zr_font_korean_glyph_ranges(void); - -/* Font baking (needs to be called sequentially top to bottom) - * -------------------------------------------------------------------- - * This is a low level API to bake font glyphs into an image and is more - * complex than the atlas API but provides more control over the baking - * process with custom bake data and memory management. */ -void zr_font_bake_memory(zr_size *temporary_memory, int *glyph_count, - struct zr_font_config*, int count); -int zr_font_bake_pack(zr_size *img_memory, int *img_width, int *img_height, - struct zr_recti *custom_space, - void *temporary_memory, zr_size temporary_size, - const struct zr_font_config*, int font_count); -void zr_font_bake(void *image_memory, int image_width, int image_height, - void *temporary_memory, zr_size temporary_memory_size, - struct zr_font_glyph*, int glyphs_count, - const struct zr_font_config*, int font_count); -void zr_font_bake_custom_data(void *img_memory, int img_width, int img_height, - struct zr_recti img_dst, const char *image_data_mask, - int tex_width, int tex_height,char white,char black); -void zr_font_bake_convert(void *out_memory, int image_width, int image_height, - const void *in_memory); - -/* Font - * ----------------------------------------------------------------- - * The font structure is just a simple container to hold the output of a baking - * process in the low level API. */ -void zr_font_init(struct zr_font*, float pixel_height, zr_rune fallback_codepoint, - struct zr_font_glyph*, const struct zr_baked_font*, - zr_handle atlas); -const struct zr_font_glyph* zr_font_find_glyph(struct zr_font*, zr_rune unicode); - -/* Font Atlas - * --------------------------------------------------------------- - * This is the high level font baking and handling API to generate an image - * out of font glyphes used to draw text onto the screen. This API takes away - * some control over the baking process like fine grained memory control and - * custom baking data but provides additional functionality and easier to - * use and manage datastructures and functions. */ -#if ZR_COMPILE_WITH_DEFAULT_ALLOCATOR -void zr_font_atlas_init_default(struct zr_font_atlas*); -#endif -void zr_font_atlas_init(struct zr_font_atlas*, struct zr_allocator*); -void zr_font_atlas_begin(struct zr_font_atlas*); - -struct zr_font_config zr_font_config(float pixel_height); -struct zr_font *zr_font_atlas_add(struct zr_font_atlas*, const struct zr_font_config*); -#if ZR_COMPILE_WITH_DEFAULT_FONT -struct zr_font* zr_font_atlas_add_default(struct zr_font_atlas*, float height, - const struct zr_font_config*); -#endif -struct zr_font* zr_font_atlas_add_from_memory(struct zr_font_atlas *atlas, void *memory, - zr_size size, float height, - const struct zr_font_config *config); -#if ZR_COMPILE_WITH_STANDARD_IO -struct zr_font* zr_font_atlas_add_from_file(struct zr_font_atlas *atlas, - const char *file_path, float height, - const struct zr_font_config*); -#endif -struct zr_font *zr_font_atlas_add_compressed(struct zr_font_atlas*, - void *memory, zr_size size, float height, - const struct zr_font_config*); -struct zr_font* zr_font_atlas_add_compressed_base85(struct zr_font_atlas *atlas, - const char *data, float height, - const struct zr_font_config *config); -const void* zr_font_atlas_bake(struct zr_font_atlas*, int *width, int *height, - enum zr_font_atlas_format); -void zr_font_atlas_end(struct zr_font_atlas*, zr_handle tex, struct zr_draw_null_texture*); -void zr_font_atlas_clear(struct zr_font_atlas*); -#endif - -/*=============================================================== - * - * EDIT BOX - * - * ===============================================================*/ -typedef int(*zr_filter)(const struct zr_edit_box*, zr_rune unicode); -typedef void(*zr_paste_f)(zr_handle, struct zr_edit_box*); -typedef void(*zr_copy_f)(zr_handle, const char*, zr_size size); - -enum zr_edit_remove_operation { - ZR_DELETE = 0, - ZR_REMOVE -}; - -enum zr_edit_flags { - ZR_EDIT_READ_ONLY = ZR_FLAG(0), - /* text inside the edit widget cannot be modified */ - ZR_EDIT_CURSOR = ZR_FLAG(1), - /* edit widget will have a movable cursor */ - ZR_EDIT_SELECTABLE = ZR_FLAG(2), - /* edit widget allows text selection */ - ZR_EDIT_CLIPBOARD = ZR_FLAG(3), - /* edit widget tries to use the clipbard callback for copy & paste */ - ZR_EDIT_SIGCOMIT = ZR_FLAG(4), - /* edit widget generateds ZR_EDIT_COMMITED event on enter */ - ZR_EDIT_MULTILINE = ZR_FLAG(5) -}; - -enum zr_edit_types { - ZR_EDIT_SIMPLE = 0, - ZR_EDIT_FIELD = (ZR_EDIT_CURSOR|ZR_EDIT_SELECTABLE|ZR_EDIT_CLIPBOARD), - ZR_EDIT_BOX = (ZR_EDIT_CURSOR|ZR_EDIT_SELECTABLE| ZR_EDIT_CLIPBOARD|ZR_EDIT_MULTILINE) -}; - -enum zr_edit_events { - ZR_EDIT_ACTIVE = ZR_FLAG(0), - /* edit widget is currently being modified */ - ZR_EDIT_INACTIVE = ZR_FLAG(1), - /* edit widget is not active and is not being modified */ - ZR_EDIT_ACTIVATED = ZR_FLAG(2), - /* edit widget went from state inactive to state active */ - ZR_EDIT_DEACTIVATED = ZR_FLAG(3), - /* edit widget went from state active to state inactive */ - ZR_EDIT_COMMITED = ZR_FLAG(4) - /* edit widget has received an enter and lost focus */ -}; - -struct zr_text_selection { - int active; - zr_size begin; - zr_size end; -}; - -struct zr_clipboard { - zr_handle userdata; - zr_paste_f paste; - zr_copy_f copy; -}; - -struct zr_edit_box { - struct zr_buffer buffer; - int active; - zr_size cursor; - zr_size glyphs; - struct zr_clipboard clip; - zr_filter filter; - struct zr_text_selection sel; - float scrollbar; - int text_inserted; -}; - -/* filter function */ -struct zr_edit_box; -int zr_filter_default(const struct zr_edit_box*, zr_rune unicode); -int zr_filter_ascii(const struct zr_edit_box*, zr_rune unicode); -int zr_filter_float(const struct zr_edit_box*, zr_rune unicode); -int zr_filter_decimal(const struct zr_edit_box*, zr_rune unicode); -int zr_filter_hex(const struct zr_edit_box*, zr_rune unicode); -int zr_filter_oct(const struct zr_edit_box*, zr_rune unicode); -int zr_filter_binary(const struct zr_edit_box*, zr_rune unicode); - -/* editbox */ -void zr_edit_box_clear(struct zr_edit_box*); -void zr_edit_box_add(struct zr_edit_box*, const char*, zr_size); -void zr_edit_box_remove(struct zr_edit_box*, enum zr_edit_remove_operation); -char *zr_edit_box_get(struct zr_edit_box*); -const char *zr_edit_box_get_const(const struct zr_edit_box*); -void zr_edit_box_at(struct zr_edit_box*, zr_size pos, zr_glyph, zr_size*); -void zr_edit_box_at_cursor(struct zr_edit_box*, zr_glyph, zr_size*); -char zr_edit_box_at_char(struct zr_edit_box*, zr_size pos); -void zr_edit_box_set_cursor(struct zr_edit_box*, zr_size pos); -zr_size zr_edit_box_get_cursor(struct zr_edit_box *eb); -zr_size zr_edit_box_len_char(struct zr_edit_box*); -zr_size zr_edit_box_len(struct zr_edit_box*); -int zr_edit_box_has_selection(const struct zr_edit_box*); -const char *zr_edit_box_get_selection(zr_size *len, struct zr_edit_box*); - -/* =============================================================== - * - * DRAWING - * - * ===============================================================*/ -/* This library was designed to be render backend agnostic so it does - not draw anything to the screen. Instead all drawn shapes, widgets - are made of, are buffered into memory and make up a command queue. - Each frame therefore fills the command buffer with draw commands - that then need to be executed by the user and his own render backend. - After that the command buffer needs to be cleared and a new frame can be - started. It is probably important to note that the command buffer is the main - drawing API and the optional vertex buffer API only takes this format and - converts it into a hardware accessable format. - - Draw commands are divided into filled shapes and shape outlines but only - the filled shapes as well as line, curves and scissor are required to be provided. - All other shape drawing commands can be used but are not required. This was - done to allow the maximum number of render backends to be able to use this - library without you having to do additional work. -*/ -enum zr_command_type { - ZR_COMMAND_NOP, - ZR_COMMAND_SCISSOR, - ZR_COMMAND_LINE, - ZR_COMMAND_CURVE, - ZR_COMMAND_RECT, - ZR_COMMAND_RECT_FILLED, - ZR_COMMAND_RECT_MULTI_COLOR, - ZR_COMMAND_CIRCLE, - ZR_COMMAND_CIRCLE_FILLED, - ZR_COMMAND_ARC, - ZR_COMMAND_ARC_FILLED, - ZR_COMMAND_TRIANGLE, - ZR_COMMAND_TRIANGLE_FILLED, - ZR_COMMAND_POLYGON, - ZR_COMMAND_POLYGON_FILLED, - ZR_COMMAND_POLYLINE, - ZR_COMMAND_TEXT, - ZR_COMMAND_IMAGE -}; - -/* command base and header of every comand inside the buffer */ -struct zr_command { - enum zr_command_type type; - zr_size next; -#if ZR_COMPILE_WITH_COMMAND_USERDATA - zr_handle userdata; -#endif -}; - -struct zr_command_scissor { - struct zr_command header; - short x, y; - unsigned short w, h; -}; - -struct zr_command_line { - struct zr_command header; - unsigned short line_thickness; - struct zr_vec2i begin; - struct zr_vec2i end; - struct zr_color color; -}; - -struct zr_command_curve { - struct zr_command header; - unsigned short line_thickness; - struct zr_vec2i begin; - struct zr_vec2i end; - struct zr_vec2i ctrl[2]; - struct zr_color color; -}; - -struct zr_command_rect { - struct zr_command header; - unsigned short rounding; - unsigned short line_thickness; - short x, y; - unsigned short w, h; - struct zr_color color; -}; - -struct zr_command_rect_filled { - struct zr_command header; - unsigned short rounding; - short x, y; - unsigned short w, h; - struct zr_color color; -}; - -struct zr_command_rect_multi_color { - struct zr_command header; - short x, y; - unsigned short w, h; - struct zr_color left; - struct zr_color top; - struct zr_color bottom; - struct zr_color right; -}; - -struct zr_command_triangle { - struct zr_command header; - unsigned short line_thickness; - struct zr_vec2i a; - struct zr_vec2i b; - struct zr_vec2i c; - struct zr_color color; -}; - -struct zr_command_triangle_filled { - struct zr_command header; - struct zr_vec2i a; - struct zr_vec2i b; - struct zr_vec2i c; - struct zr_color color; -}; - -struct zr_command_circle { - struct zr_command header; - short x, y; - unsigned short line_thickness; - unsigned short w, h; - struct zr_color color; -}; - -struct zr_command_circle_filled { - struct zr_command header; - short x, y; - unsigned short w, h; - struct zr_color color; -}; - -struct zr_command_arc { - struct zr_command header; - short cx, cy; - unsigned short r; - unsigned short line_thickness; - float a[2]; - struct zr_color color; -}; - -struct zr_command_arc_filled { - struct zr_command header; - short cx, cy; - unsigned short r; - float a[2]; - struct zr_color color; -}; - -struct zr_command_polygon { - struct zr_command header; - struct zr_color color; - unsigned short line_thickness; - unsigned short point_count; - struct zr_vec2i points[1]; -}; - -struct zr_command_polygon_filled { - struct zr_command header; - struct zr_color color; - unsigned short point_count; - struct zr_vec2i points[1]; -}; - -struct zr_command_polyline { - struct zr_command header; - struct zr_color color; - unsigned short line_thickness; - unsigned short point_count; - struct zr_vec2i points[1]; -}; - -struct zr_command_image { - struct zr_command header; - short x, y; - unsigned short w, h; - struct zr_image img; -}; - -struct zr_command_text { - struct zr_command header; - const struct zr_user_font *font; - struct zr_color background; - struct zr_color foreground; - short x, y; - unsigned short w, h; - float height; - zr_size length; - char string[1]; -}; - -enum zr_command_clipping { - ZR_CLIPPING_OFF = zr_false, - ZR_CLIPPING_ON = zr_true -}; - -struct zr_command_buffer { - struct zr_buffer *base; - struct zr_rect clip; - int use_clipping; - zr_handle userdata; - zr_size begin, end, last; -}; - -/* shape outlines */ -void zr_push_scissor(struct zr_command_buffer*, struct zr_rect); -void zr_stroke_line(struct zr_command_buffer *b, float x0, float y0, - float x1, float y1, float line_thickness, struct zr_color); -void zr_stroke_curve(struct zr_command_buffer*, float, float, float, float, - float, float, float, float, float line_thickness, struct zr_color); -void zr_stroke_rect(struct zr_command_buffer*, struct zr_rect, float rounding, - float line_thickness, struct zr_color); -void zr_stroke_circle(struct zr_command_buffer*, struct zr_rect, float line_thickness, - struct zr_color); -void zr_stroke_arc(struct zr_command_buffer*, float cx, float cy, float radius, - float a_min, float a_max, float line_thickness, struct zr_color); -void zr_stroke_triangle(struct zr_command_buffer*, float, float, float, float, - float, float, float line_thichness, struct zr_color); -void zr_stroke_polyline(struct zr_command_buffer*, float *points, int point_count, - float line_thickness, struct zr_color col); -void zr_stroke_polygon(struct zr_command_buffer*, float*, int point_count, - float line_thickness, struct zr_color); - -/* filled shades */ -void zr_fill_rect(struct zr_command_buffer*, struct zr_rect, float rounding, - struct zr_color); -void zr_fill_rect_multi_color(struct zr_command_buffer*, struct zr_rect, - struct zr_color left, struct zr_color top, - struct zr_color right, struct zr_color bottom); -void zr_fill_circle(struct zr_command_buffer*, struct zr_rect, struct zr_color); -void zr_fill_arc(struct zr_command_buffer*, float cx, float cy, float radius, - float a_min, float a_max, struct zr_color); -void zr_fill_triangle(struct zr_command_buffer*, float x0, float y0, - float x1, float y1, float x2, float y2, struct zr_color); -void zr_fill_polygon(struct zr_command_buffer*, float*, int point_count, struct zr_color); -void zr_draw_image(struct zr_command_buffer*, struct zr_rect, const struct zr_image*); -void zr_draw_text(struct zr_command_buffer*, struct zr_rect, - const char *text, zr_size len, const struct zr_user_font*, - struct zr_color, struct zr_color); - -/* =============================================================== - * - * DRAW LIST - * - * ===============================================================*/ -/* The optional vertex buffer draw list provides a 2D drawing context - with antialiasing functionality which takes basic filled or outlined shapes - or a path and outputs vertexes, elements and draw commands. - The actual draw list API is not required to be used dirctly while using this - library since converting the default library draw command output is done by - just calling `zr_convert` but I decided to still makes this library accessable - since it can be useful. - - The draw list is based on a path buffering and polygon and polyline - rendering API which allows a lot of ways to draw 2D content to screen. - In fact it is probably more powerful than needed but allows even more crazy - things than this library provides by default. -*/ -#if ZR_COMPILE_WITH_VERTEX_BUFFER -typedef unsigned short zr_draw_index; -typedef zr_uint zr_draw_vertex_color; - -enum zr_draw_list_stroke { - ZR_STROKE_OPEN = zr_false, - /* build up path has no connection back to the beginning */ - ZR_STROKE_CLOSED = zr_true - /* build up path has a connection back to the beginning */ -}; - -enum zr_anti_aliasing { - ZR_ANTI_ALIASING_OFF = zr_false, - ZR_ANTI_ALIASING_ON -}; - -struct zr_draw_vertex { - struct zr_vec2 position; - struct zr_vec2 uv; - zr_draw_vertex_color col; -}; - -struct zr_draw_command { - unsigned int elem_count; - /* number of elements in the current draw batch */ - struct zr_rect clip_rect; - /* current screen clipping rectangle */ - zr_handle texture; - /* current texture to set */ -#if ZR_COMPILE_WITH_COMMAND_USERDATA - zr_handle userdata; -#endif -}; - -struct zr_draw_list { - float global_alpha; - enum zr_anti_aliasing shape_AA; - enum zr_anti_aliasing line_AA; - struct zr_draw_null_texture null; - struct zr_rect clip_rect; - struct zr_buffer *buffer; - struct zr_buffer *vertices; - struct zr_buffer *elements; - unsigned int element_count; - unsigned int vertex_count; - zr_size cmd_offset; - unsigned int cmd_count; - unsigned int path_count; - unsigned int path_offset; - struct zr_vec2 circle_vtx[12]; -#if ZR_COMPILE_WITH_COMMAND_USERDATA - zr_handle userdata; -#endif -}; -#endif - -/* draw list */ -void zr_draw_list_init(struct zr_draw_list*); -void zr_draw_list_setup(struct zr_draw_list*, float global_alpha, - enum zr_anti_aliasing line_AA, enum zr_anti_aliasing shape_AA, - struct zr_draw_null_texture, struct zr_buffer *cmds, - struct zr_buffer *vertices, struct zr_buffer *elements); -void zr_draw_list_clear(struct zr_draw_list*); - -/* command drawing */ -#define zr_draw_list_foreach(cmd, can, b)\ - for((cmd)=zr__draw_list_begin(can, b); (cmd)!=0; (cmd)=zr__draw_list_next(cmd, b, can)) -const struct zr_draw_command* zr__draw_list_begin(const struct zr_draw_list*, const struct zr_buffer*); -const struct zr_draw_command* zr__draw_list_next(const struct zr_draw_command*, - const struct zr_buffer*, - const struct zr_draw_list*); -/* path */ -void zr_draw_list_path_clear(struct zr_draw_list*); -void zr_draw_list_path_line_to(struct zr_draw_list *list, struct zr_vec2 pos); -void zr_draw_list_path_arc_to_fast(struct zr_draw_list*, struct zr_vec2 center, - float radius, int a_min, int a_max); -void zr_draw_list_path_arc_to(struct zr_draw_list*, struct zr_vec2 center, - float radius, float a_min, float a_max, - unsigned int segments); -void zr_draw_list_path_rect_to(struct zr_draw_list*, struct zr_vec2 a, - struct zr_vec2 b, float rounding); -void zr_draw_list_path_curve_to(struct zr_draw_list*, struct zr_vec2 p2, - struct zr_vec2 p3, struct zr_vec2 p4, - unsigned int num_segments); -void zr_draw_list_path_fill(struct zr_draw_list*, struct zr_color); -void zr_draw_list_path_stroke(struct zr_draw_list*, struct zr_color, - enum zr_draw_list_stroke closed, float thickness); -/* stroke */ -void zr_draw_list_stroke_line(struct zr_draw_list*, struct zr_vec2 a, struct zr_vec2 b, - struct zr_color, float thickness); -void zr_draw_list_stroke_rect(struct zr_draw_list*, struct zr_rect rect, struct zr_color, - float rounding, float thickness); -void zr_draw_list_stroke_triangle(struct zr_draw_list*, struct zr_vec2 a, struct zr_vec2 b, - struct zr_vec2 c, struct zr_color, float thickness); -void zr_draw_list_stroke_circle(struct zr_draw_list*, struct zr_vec2 center, float radius, - struct zr_color, unsigned int segs, float thickness); -void zr_draw_list_stroke_curve(struct zr_draw_list*, struct zr_vec2 p0, struct zr_vec2 cp0, - struct zr_vec2 cp1, struct zr_vec2 p1, struct zr_color, - unsigned int segments, float thickness); -void zr_draw_list_stroke_poly_line(struct zr_draw_list*, const struct zr_vec2 *points, - const unsigned int count, struct zr_color, - enum zr_draw_list_stroke closed, float thickness, - enum zr_anti_aliasing aliasing); -/* fill */ -void zr_draw_list_fill_rect(struct zr_draw_list*, struct zr_rect rect, - struct zr_color, float rounding); -void zr_draw_list_fill_rect_multi_color(struct zr_draw_list *list, struct zr_rect rect, - struct zr_color left, struct zr_color top, - struct zr_color right, struct zr_color bottom); -void zr_draw_list_fill_triangle(struct zr_draw_list*, struct zr_vec2 a, struct zr_vec2 b, - struct zr_vec2 c, struct zr_color); -void zr_draw_list_fill_circle(struct zr_draw_list*, struct zr_vec2 center, - float radius, struct zr_color col, unsigned int segs); -void zr_draw_list_fill_poly_convex(struct zr_draw_list*, const struct zr_vec2 *points, - const unsigned int count, struct zr_color, - enum zr_anti_aliasing aliasing); -/* misc */ -void zr_draw_list_add_image(struct zr_draw_list*, struct zr_image texture, - struct zr_rect rect, struct zr_color); -void zr_draw_list_add_text(struct zr_draw_list*, const struct zr_user_font*, - struct zr_rect, const char *text, zr_size len, - float font_height, struct zr_color); -#if ZR_COMPILE_WITH_COMMAND_USERDATA -void zr_draw_list_push_userdata(struct zr_draw_list*, zr_handle userdata); -#endif - -/* =============================================================== - * - * GUI - * - * ===============================================================*/ -enum zr_keys { - ZR_KEY_SHIFT, - ZR_KEY_DEL, - ZR_KEY_ENTER, - ZR_KEY_TAB, - ZR_KEY_BACKSPACE, - ZR_KEY_COPY, - ZR_KEY_CUT, - ZR_KEY_PASTE, - ZR_KEY_UP, - ZR_KEY_DOWN, - ZR_KEY_LEFT, - ZR_KEY_RIGHT, - ZR_KEY_MAX -}; - -enum zr_buttons { - ZR_BUTTON_LEFT, - ZR_BUTTON_MIDDLE, - ZR_BUTTON_RIGHT, - ZR_BUTTON_MAX -}; - -struct zr_mouse_button { - int down; - unsigned int clicked; - struct zr_vec2 clicked_pos; -}; - -struct zr_mouse { - struct zr_mouse_button buttons[ZR_BUTTON_MAX]; - struct zr_vec2 pos; - struct zr_vec2 prev; - struct zr_vec2 delta; - float scroll_delta; -}; - -struct zr_key { - int down; - unsigned int clicked; -}; - -struct zr_keyboard { - struct zr_key keys[ZR_KEY_MAX]; - char text[ZR_INPUT_MAX]; - zr_size text_len; -}; - -struct zr_input { - struct zr_keyboard keyboard; - struct zr_mouse mouse; -}; - -int zr_input_has_mouse_click_in_rect(const struct zr_input*, - enum zr_buttons, struct zr_rect); -int zr_input_has_mouse_click_down_in_rect(const struct zr_input*, enum zr_buttons, - struct zr_rect, int down); -int zr_input_is_mouse_click_in_rect(const struct zr_input*, - enum zr_buttons, struct zr_rect); -int zr_input_is_mouse_click_down_in_rect(const struct zr_input *i, enum zr_buttons id, - struct zr_rect b, int down); -int zr_input_any_mouse_click_in_rect(const struct zr_input*, struct zr_rect); -int zr_input_is_mouse_prev_hovering_rect(const struct zr_input*, struct zr_rect); -int zr_input_is_mouse_hovering_rect(const struct zr_input*, struct zr_rect); -int zr_input_mouse_clicked(const struct zr_input*, enum zr_buttons, struct zr_rect); -int zr_input_is_mouse_down(const struct zr_input*, enum zr_buttons); -int zr_input_is_mouse_pressed(const struct zr_input*, enum zr_buttons); -int zr_input_is_mouse_released(const struct zr_input*, enum zr_buttons); -int zr_input_is_key_pressed(const struct zr_input*, enum zr_keys); -int zr_input_is_key_released(const struct zr_input*, enum zr_keys); -int zr_input_is_key_down(const struct zr_input*, enum zr_keys); - -/* ============================================================== - * STYLE - * ===============================================================*/ -enum zr_symbol_type { - ZR_SYMBOL_NONE, - ZR_SYMBOL_X, - ZR_SYMBOL_UNDERSCORE, - ZR_SYMBOL_CIRCLE, - ZR_SYMBOL_CIRCLE_FILLED, - ZR_SYMBOL_RECT, - ZR_SYMBOL_RECT_FILLED, - ZR_SYMBOL_TRIANGLE_UP, - ZR_SYMBOL_TRIANGLE_DOWN, - ZR_SYMBOL_TRIANGLE_LEFT, - ZR_SYMBOL_TRIANGLE_RIGHT, - ZR_SYMBOL_PLUS, - ZR_SYMBOL_MINUS, - ZR_SYMBOL_MAX -}; - -enum zr_style_item_type { - ZR_STYLE_ITEM_COLOR, - ZR_STYLE_ITEM_IMAGE -}; - -union zr_style_item_data { - struct zr_image image; - struct zr_color color; -}; - -struct zr_style_item { - enum zr_style_item_type type; - union zr_style_item_data data; -}; - -struct zr_style_text { - struct zr_color color; - struct zr_vec2 padding; -}; - -struct zr_style_button; -struct zr_style_custom_button_drawing { - void(*button_text)(struct zr_command_buffer*, - const struct zr_rect *background, const struct zr_rect*, - zr_flags state, const struct zr_style_button*, - const char*, zr_size len, zr_flags text_alignment, - const struct zr_user_font*); - void(*button_symbol)(struct zr_command_buffer*, - const struct zr_rect *background, const struct zr_rect*, - zr_flags state, const struct zr_style_button*, - enum zr_symbol_type, const struct zr_user_font*); - void(*button_image)(struct zr_command_buffer*, - const struct zr_rect *background, const struct zr_rect*, - zr_flags state, const struct zr_style_button*, - const struct zr_image *img); - void(*button_text_symbol)(struct zr_command_buffer*, - const struct zr_rect *background, const struct zr_rect*, - const struct zr_rect *symbol, zr_flags state, - const struct zr_style_button*, - const char *text, zr_size len, zr_flags align, - enum zr_symbol_type, const struct zr_user_font*); - void(*button_text_image)(struct zr_command_buffer*, - const struct zr_rect *background, const struct zr_rect*, - const struct zr_rect *image, zr_flags state, - const struct zr_style_button*, - const char *text, zr_size len, zr_flags align, const struct zr_user_font*, - const struct zr_image *img); -}; - -struct zr_style_button { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - struct zr_color border_color; - - /* text */ - struct zr_color text_background; - struct zr_color text_normal; - struct zr_color text_hover; - struct zr_color text_active; - zr_flags text_alignment; - - /* properties */ - float border; - float rounding; - struct zr_vec2 padding; - struct zr_vec2 image_padding; - struct zr_vec2 touch_padding; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*, zr_handle userdata); - struct zr_style_custom_button_drawing draw; - void(*draw_end)(zr_handle, struct zr_command_buffer*, zr_handle userdata); -}; - -struct zr_style_toggle; -union zr_style_custom_toggle_drawing { - void(*radio)(struct zr_command_buffer*, zr_flags state, - const struct zr_style_toggle *toggle, int active, - const struct zr_rect *label, const struct zr_rect *selector, - const struct zr_rect *cursor, const char *string, zr_size len, - const struct zr_user_font *font); - void(*checkbox)(struct zr_command_buffer*, zr_flags state, - const struct zr_style_toggle *toggle, int active, - const struct zr_rect *label, const struct zr_rect *selector, - const struct zr_rect *cursor, const char *string, zr_size len, - const struct zr_user_font *font); -}; - -struct zr_style_toggle { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - - /* cursor */ - struct zr_style_item cursor_normal; - struct zr_style_item cursor_hover; - - /* text */ - struct zr_color text_normal; - struct zr_color text_hover; - struct zr_color text_active; - struct zr_color text_background; - zr_flags text_alignment; - - /* properties */ - struct zr_vec2 padding; - struct zr_vec2 touch_padding; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*, zr_handle); - union zr_style_custom_toggle_drawing draw; - void(*draw_end)(struct zr_command_buffer*, zr_handle); -}; - -struct zr_style_selectable { - /* background (inactive) */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item pressed; - - /* background (active) */ - struct zr_style_item normal_active; - struct zr_style_item hover_active; - struct zr_style_item pressed_active; - - /* text color (inactive) */ - struct zr_color text_normal; - struct zr_color text_hover; - struct zr_color text_pressed; - - /* text color (active) */ - struct zr_color text_normal_active; - struct zr_color text_hover_active; - struct zr_color text_pressed_active; - struct zr_color text_background; - zr_flags text_alignment; - - /* properties */ - float rounding; - struct zr_vec2 padding; - struct zr_vec2 touch_padding; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*, zr_handle); - void(*draw)(struct zr_command_buffer*, - zr_flags state, const struct zr_style_selectable*, int active, - const struct zr_rect*, const char *string, zr_size len, - zr_flags align, const struct zr_user_font*); - void(*draw_end)(struct zr_command_buffer*, zr_handle); -}; - -struct zr_style_slider { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - struct zr_color border_color; - - /* background bar */ - struct zr_color bar_normal; - struct zr_color bar_hover; - struct zr_color bar_active; - struct zr_color bar_filled; - - /* cursor */ - struct zr_style_item cursor_normal; - struct zr_style_item cursor_hover; - struct zr_style_item cursor_active; - - /* properties */ - float border; - float rounding; - float bar_height; - struct zr_vec2 padding; - struct zr_vec2 spacing; - struct zr_vec2 cursor_size; - - /* optional buttons */ - int show_buttons; - struct zr_style_button inc_button; - struct zr_style_button dec_button; - enum zr_symbol_type inc_symbol; - enum zr_symbol_type dec_symbol; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*, zr_handle); - void(*draw)(struct zr_command_buffer*, zr_flags state, - const struct zr_style_slider*, const struct zr_rect *bar, - const struct zr_rect *cursor, float min, float value, float max); - void(*draw_end)(struct zr_command_buffer*); -}; - -struct zr_style_progress { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - - /* cursor */ - struct zr_style_item cursor_normal; - struct zr_style_item cursor_hover; - struct zr_style_item cursor_active; - - /* properties */ - float rounding; - struct zr_vec2 padding; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*, zr_handle); - void(*draw)(struct zr_command_buffer*, zr_flags state, - const struct zr_style_progress*, const struct zr_rect *bounds, - const struct zr_rect *cursor, zr_size value, zr_size max); - void(*draw_end)(struct zr_command_buffer*); -}; - -struct zr_style_scrollbar { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - struct zr_color border_color; - - /* cursor */ - struct zr_style_item cursor_normal; - struct zr_style_item cursor_hover; - struct zr_style_item cursor_active; - - /* properties */ - float border; - float rounding; - struct zr_vec2 padding; - - /* optional buttons */ - int show_buttons; - struct zr_style_button inc_button; - struct zr_style_button dec_button; - enum zr_symbol_type inc_symbol; - enum zr_symbol_type dec_symbol; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*, zr_handle); - void(*draw)(struct zr_command_buffer*, zr_flags state, - const struct zr_style_scrollbar*, const struct zr_rect *scroll, - const struct zr_rect *cursor); - void(*draw_end)(struct zr_command_buffer*, zr_handle); -}; - -struct zr_style_edit { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - struct zr_color border_color; - struct zr_style_scrollbar scrollbar; - - /* cursor */ - struct zr_style_item cursor_normal; - struct zr_style_item cursor_hover; - struct zr_style_item cursor_active; - - /* text (unselected) */ - struct zr_color text_normal; - struct zr_color text_hover; - struct zr_color text_active; - - /* text (selected) */ - struct zr_color selected_normal; - struct zr_color selected_hover; - struct zr_color selected_text_normal; - struct zr_color selected_text_hover; - - - /* properties */ - float border; - float rounding; - float cursor_size; - float scrollbar_size; - struct zr_vec2 padding; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*,zr_handle); - void(*draw)(struct zr_command_buffer*, zr_flags state, - const struct zr_style_edit*, const struct zr_rect *bounds, - const struct zr_rect *label, const struct zr_rect *selection, - int show_cursor, const char *unselected_text, zr_size unselected_len, - const char *selected_text, zr_size selected_len, - const struct zr_edit_box*, const struct zr_user_font*); - void(*draw_end)(struct zr_command_buffer*,zr_handle); -}; - -struct zr_style_property { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - struct zr_color border_color; - - /* text */ - struct zr_color label_normal; - struct zr_color label_hover; - struct zr_color label_active; - - /* symbols */ - enum zr_symbol_type sym_left; - enum zr_symbol_type sym_right; - - /* properties */ - float border; - float rounding; - struct zr_vec2 padding; - - struct zr_style_edit edit; - struct zr_style_button inc_button; - struct zr_style_button dec_button; - - /* optional user callbacks */ - zr_handle userdata; - void(*draw_begin)(struct zr_command_buffer*, zr_handle); - void(*draw)(struct zr_command_buffer*, const struct zr_style_property*, - const struct zr_rect*, const struct zr_rect *label, zr_flags state, - const char *name, zr_size len, const struct zr_user_font*); - void(*draw_end)(struct zr_command_buffer*, zr_handle); -}; - -struct zr_style_chart { - /* colors */ - struct zr_style_item background; - struct zr_color border_color; - struct zr_color selected_color; - struct zr_color color; - - /* properties */ - float border; - float rounding; - struct zr_vec2 padding; -}; - -struct zr_style_combo { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - struct zr_color border_color; - - /* label */ - struct zr_color label_normal; - struct zr_color label_hover; - struct zr_color label_active; - - /* symbol */ - struct zr_color symbol_normal; - struct zr_color symbol_hover; - struct zr_color symbol_active; - - /* button */ - struct zr_style_button button; - enum zr_symbol_type sym_normal; - enum zr_symbol_type sym_hover; - enum zr_symbol_type sym_active; - - /* properties */ - float border; - float rounding; - struct zr_vec2 content_padding; - struct zr_vec2 button_padding; - struct zr_vec2 spacing; -}; - -struct zr_style_tab { - /* background */ - struct zr_style_item background; - struct zr_color border_color; - struct zr_color text; - - /* button */ - struct zr_style_button tab_button; - struct zr_style_button node_button; - enum zr_symbol_type sym_minimize; - enum zr_symbol_type sym_maximize; - - /* properties */ - float border; - float rounding; - struct zr_vec2 padding; - struct zr_vec2 spacing; -}; - -enum zr_style_header_align { - ZR_HEADER_LEFT, - ZR_HEADER_RIGHT -}; - -struct zr_style_window_header { - /* background */ - struct zr_style_item normal; - struct zr_style_item hover; - struct zr_style_item active; - - /* button */ - struct zr_style_button close_button; - struct zr_style_button minimize_button; - enum zr_symbol_type close_symbol; - enum zr_symbol_type minimize_symbol; - enum zr_symbol_type maximize_symbol; - - /* title */ - struct zr_color label_normal; - struct zr_color label_hover; - struct zr_color label_active; - - /* properties */ - enum zr_style_header_align align; - struct zr_vec2 padding; - struct zr_vec2 label_padding; - struct zr_vec2 spacing; -}; - -struct zr_style_window { - struct zr_style_window_header header; - struct zr_style_item fixed_background; - struct zr_color background; - - struct zr_color border_color; - struct zr_color combo_border_color; - struct zr_color contextual_border_color; - struct zr_color menu_border_color; - struct zr_color group_border_color; - struct zr_color tooltip_border_color; - - struct zr_style_item scaler; - struct zr_vec2 footer_padding; - - float border; - float combo_border; - float contextual_border; - float menu_border; - float group_border; - float tooltip_border; - - float rounding; - struct zr_vec2 scaler_size; - struct zr_vec2 padding; - struct zr_vec2 spacing; - struct zr_vec2 scrollbar_size; - struct zr_vec2 min_size; -}; - -struct zr_style { - struct zr_user_font font; - struct zr_style_text text; - struct zr_style_button button; - struct zr_style_button contextual_button; - struct zr_style_button menu_button; - struct zr_style_toggle option; - struct zr_style_toggle checkbox; - struct zr_style_selectable selectable; - struct zr_style_slider slider; - struct zr_style_progress progress; - struct zr_style_property property; - struct zr_style_edit edit; - struct zr_style_chart line_chart; - struct zr_style_chart column_chart; - struct zr_style_scrollbar scrollh; - struct zr_style_scrollbar scrollv; - struct zr_style_tab tab; - struct zr_style_combo combo; - struct zr_style_window window; -}; - -enum zr_style_colors { - ZR_COLOR_TEXT, - ZR_COLOR_WINDOW, - ZR_COLOR_HEADER, - ZR_COLOR_BORDER, - ZR_COLOR_BUTTON, - ZR_COLOR_BUTTON_HOVER, - ZR_COLOR_BUTTON_ACTIVE, - ZR_COLOR_TOGGLE, - ZR_COLOR_TOGGLE_HOVER, - ZR_COLOR_TOGGLE_CURSOR, - ZR_COLOR_SELECTABLE, - ZR_COLOR_SELECTABLE_HOVER, - ZR_COLOR_SELECTABLE_TEXT, - ZR_COLOR_SLIDER, - ZR_COLOR_SLIDER_CURSOR, - ZR_COLOR_SLIDER_CURSOR_HOVER, - ZR_COLOR_SLIDER_CURSOR_ACTIVE, - ZR_COLOR_PROPERTY, - ZR_COLOR_EDIT, - ZR_COLOR_EDIT_CURSOR, - ZR_COLOR_COMBO, - ZR_COLOR_CHART, - ZR_COLOR_CHART_COLOR, - ZR_COLOR_CHART_COLOR_HIGHLIGHT, - ZR_COLOR_SCROLLBAR, - ZR_COLOR_SCROLLBAR_CURSOR, - ZR_COLOR_SCROLLBAR_CURSOR_HOVER, - ZR_COLOR_SCROLLBAR_CURSOR_ACTIVE, - ZR_COLOR_TAB_HEADER, - ZR_COLOR_COUNT -}; - -struct zr_style_item zr_style_item_image(struct zr_image img); -struct zr_style_item zr_style_item_color(struct zr_color); -struct zr_style_item zr_style_item_hide(void); - -/*============================================================== - * PANEL - * =============================================================*/ -enum zr_modify { - ZR_FIXED = zr_false, - ZR_MODIFIABLE = zr_true -}; - -enum zr_orientation { - ZR_VERTICAL, - ZR_HORIZONTAL -}; - -enum zr_collapse_states { - ZR_MINIMIZED = zr_false, - ZR_MAXIMIZED = zr_true -}; - -enum zr_show_states { - ZR_HIDDEN = zr_false, - ZR_SHOWN = zr_true -}; - -/* widget states */ -enum zr_widget_states { - ZR_WIDGET_STATE_INACTIVE = ZR_FLAG(0), - /* widget is neither active nor hovered */ - ZR_WIDGET_STATE_ENTERED = ZR_FLAG(1), - /* widget has been hovered on the current frame */ - ZR_WIDGET_STATE_HOVERED = ZR_FLAG(2), - /* widget is being hovered */ - ZR_WIDGET_STATE_LEFT = ZR_FLAG(3), - /* widget is from this frame on not hovered anymore */ - ZR_WIDGET_STATE_ACTIVE = ZR_FLAG(4) - /* widget is currently activated */ -}; - -enum zr_widget_layout_states { - ZR_WIDGET_INVALID, - /* The widget cannot be seen and is completly out of view */ - ZR_WIDGET_VALID, - /* The widget is completly inside the window and can be updated and drawn */ - ZR_WIDGET_ROM - /* The widget is partially visible and cannot be updated */ -}; - -/* text alignment */ -enum zr_text_align { - ZR_TEXT_ALIGN_LEFT = 0x01, - ZR_TEXT_ALIGN_CENTERED = 0x02, - ZR_TEXT_ALIGN_RIGHT = 0x04, - ZR_TEXT_ALIGN_TOP = 0x08, - ZR_TEXT_ALIGN_MIDDLE = 0x10, - ZR_TEXT_ALIGN_BOTTOM = 0x20 -}; -enum zr_text_alignment { - ZR_TEXT_CENTERED = ZR_TEXT_ALIGN_MIDDLE|ZR_TEXT_ALIGN_CENTERED, - ZR_TEXT_LEFT = ZR_TEXT_ALIGN_MIDDLE|ZR_TEXT_ALIGN_LEFT, - ZR_TEXT_RIGHT = ZR_TEXT_ALIGN_MIDDLE|ZR_TEXT_ALIGN_RIGHT -}; - -enum zr_button_behavior { - ZR_BUTTON_DEFAULT, - ZR_BUTTON_REPEATER -}; - -enum zr_chart_type { - ZR_CHART_LINES, - ZR_CHART_COLUMN, - ZR_CHART_MAX -}; - -enum zr_chart_event { - ZR_CHART_HOVERING = 0x01, - ZR_CHART_CLICKED = 0x02 -}; - -enum zr_color_picker_format { - ZR_RGB, - ZR_RGBA -}; - -enum zr_popup_type { - ZR_POPUP_STATIC, - ZR_POPUP_DYNAMIC -}; - -enum zr_layout_format { - ZR_DYNAMIC, - ZR_STATIC -}; - -enum zr_layout_node_type { - ZR_LAYOUT_NODE, - ZR_LAYOUT_TAB -}; - -struct zr_chart { - const struct zr_style_chart *style; - enum zr_chart_type type; - float x, y, w, h; - float min, max, range; - struct zr_vec2 last; - int index; - int count; -}; - -enum zr_panel_flags { - ZR_WINDOW_BORDER = ZR_FLAG(0), - /* Draws a border around the window to visually seperate the window - * from the background */ - ZR_WINDOW_BORDER_HEADER = ZR_FLAG(1), - /* Draws a border between window header and body */ - ZR_WINDOW_MOVABLE = ZR_FLAG(2), - /* The moveable flag inidicates that a window can be moved by user input or - * by dragging the window header */ - ZR_WINDOW_SCALABLE = ZR_FLAG(3), - /* The scaleable flag indicates that a window can be scaled by user input - * by dragging a scaler icon at the button of the window */ - ZR_WINDOW_CLOSABLE = ZR_FLAG(4), - /* adds a closeable icon into the header */ - ZR_WINDOW_MINIMIZABLE = ZR_FLAG(5), - /* adds a minimize icon into the header */ - ZR_WINDOW_DYNAMIC = ZR_FLAG(6), - /* special type of window which grows up in height while being filled to a - * certain maximum height. It is mainly used for combo boxes/menus but can - * be used to create perfectly fitting windows as well */ - ZR_WINDOW_NO_SCROLLBAR = ZR_FLAG(7), - /* Removes the scrollbar from the window */ - ZR_WINDOW_TITLE = ZR_FLAG(8) - /* Forces a header at the top at the window showing the title */ -}; - -struct zr_row_layout { - int type; - int index; - float height; - int columns; - const float *ratio; - float item_width, item_height; - float item_offset; - float filled; - struct zr_rect item; - int tree_depth; -}; - -struct zr_popup_buffer { - zr_size begin; - zr_size parent; - zr_size last; - zr_size end; - int active; -}; - -struct zr_menu_state { - float x, y, w, h; - struct zr_scroll offset; -}; - -struct zr_panel { - zr_flags flags; - struct zr_rect bounds; - struct zr_scroll *offset; - float at_x, at_y, max_x; - float width, height; - float footer_h; - float header_h; - float border; - struct zr_rect clip; - struct zr_menu_state menu; - struct zr_row_layout row; - struct zr_chart chart; - struct zr_popup_buffer popup_buffer; - struct zr_command_buffer *buffer; - struct zr_panel *parent; -}; - -/*============================================================== - * WINDOW - * =============================================================*/ -enum zr_window_flags { - ZR_WINDOW_PRIVATE = ZR_FLAG(9), - /* dummy flag which mark the beginning of the private window flag part */ - ZR_WINDOW_ROM = ZR_FLAG(10), - /* sets the window into a read only mode and does not allow input changes */ - ZR_WINDOW_HIDDEN = ZR_FLAG(11), - /* Hiddes the window and stops any window interaction and drawing can be set - * by user input or by closing the window */ - ZR_WINDOW_MINIMIZED = ZR_FLAG(12), - /* marks the window as minimized */ - ZR_WINDOW_SUB = ZR_FLAG(13), - /* Marks the window as subwindow of another window*/ - ZR_WINDOW_GROUP = ZR_FLAG(14), - /* Marks the window as window widget group */ - ZR_WINDOW_POPUP = ZR_FLAG(15), - /* Marks the window as a popup window */ - ZR_WINDOW_NONBLOCK = ZR_FLAG(16), - /* Marks the window as a nonblock popup window */ - ZR_WINDOW_CONTEXTUAL = ZR_FLAG(17), - /* Marks the window as a combo box or menu */ - ZR_WINDOW_COMBO = ZR_FLAG(18), - /* Marks the window as a combo box */ - ZR_WINDOW_MENU = ZR_FLAG(19), - /* Marks the window as a menu */ - ZR_WINDOW_TOOLTIP = ZR_FLAG(20), - /* Marks the window as a menu */ - ZR_WINDOW_REMOVE_ROM = ZR_FLAG(21) - /* Removes the read only mode at the end of the window */ -}; - -struct zr_popup_state { - struct zr_window *win; - enum zr_window_flags type; - zr_hash name; - int active; - unsigned combo_count; - unsigned con_count, con_old; - unsigned active_con; -}; - -struct zr_edit_state { - zr_hash name; - zr_size cursor; - struct zr_text_selection sel; - float scrollbar; - unsigned int seq; - unsigned int old; - int active, prev; -}; - -struct zr_property_state { - int active, prev; - char buffer[ZR_MAX_NUMBER_BUFFER]; - zr_size length; - zr_size cursor; - zr_hash name; - unsigned int seq; - unsigned int old; - int state; -}; - -struct zr_table; -struct zr_window { - zr_hash name; - zr_flags flags; - unsigned int seq; - struct zr_rect bounds; - struct zr_scroll scrollbar; - struct zr_command_buffer buffer; - struct zr_panel *layout; - - /* persistent widget state */ - struct zr_property_state property; - struct zr_popup_state popup; - struct zr_edit_state edit; - - struct zr_table *tables; - unsigned short table_count; - unsigned short table_size; - - /* window list hooks */ - struct zr_window *next; - struct zr_window *prev; - struct zr_window *parent; -}; - -/*============================================================== - * CONTEXT - * =============================================================*/ -struct zr_context { -/* public: can be accessed freely */ - struct zr_input input; - struct zr_style style; - struct zr_buffer memory; - struct zr_clipboard clip; - zr_flags last_widget_state; - -/* private: - should only be accessed if you - know what you are doing */ -#if ZR_COMPILE_WITH_VERTEX_BUFFER - struct zr_draw_list draw_list; -#endif -#if ZR_COMPILE_WITH_COMMAND_USERDATA - zr_handle userdata; -#endif - - /* windows */ - int build; - void *pool; - struct zr_window *begin; - struct zr_window *end; - struct zr_window *active; - struct zr_window *current; - struct zr_window *freelist; - unsigned int count; - unsigned int seq; -}; - -/*-------------------------------------------------------------- - * CONTEXT - * -------------------------------------------------------------*/ -#if ZR_COMPILE_WITH_DEFAULT_ALLOCATOR -int zr_init_default(struct zr_context*, const struct zr_user_font*); -#endif -int zr_init_fixed(struct zr_context*, void *memory, zr_size size, - const struct zr_user_font*); -int zr_init_custom(struct zr_context*, struct zr_buffer *cmds, - struct zr_buffer *pool, const struct zr_user_font*); -int zr_init(struct zr_context*, struct zr_allocator*, - const struct zr_user_font*); -void zr_clear(struct zr_context*); -void zr_free(struct zr_context*); -#if ZR_COMPILE_WITH_COMMAND_USERDATA -void zr_set_user_data(struct zr_context*, zr_handle handle); -#endif - -/*-------------------------------------------------------------- - * WINDOW - * -------------------------------------------------------------*/ -int zr_begin(struct zr_context*, struct zr_panel*, const char *title, - struct zr_rect bounds, zr_flags flags); -void zr_end(struct zr_context*); - -struct zr_window* zr_window_find(struct zr_context *ctx, const char *name); -struct zr_rect zr_window_get_bounds(const struct zr_context*); -struct zr_vec2 zr_window_get_position(const struct zr_context*); -struct zr_vec2 zr_window_get_size(const struct zr_context*); -float zr_window_get_width(const struct zr_context*); -float zr_window_get_height(const struct zr_context*); -struct zr_rect zr_window_get_content_region(struct zr_context*); -struct zr_vec2 zr_window_get_content_region_min(struct zr_context*); -struct zr_vec2 zr_window_get_content_region_max(struct zr_context*); -struct zr_vec2 zr_window_get_content_region_size(struct zr_context*); -struct zr_command_buffer* zr_window_get_canvas(struct zr_context*); -struct zr_panel* zr_window_get_panel(struct zr_context*); -int zr_window_has_focus(const struct zr_context*); -int zr_window_is_hovered(struct zr_context*); -int zr_window_is_any_hovered(struct zr_context*); -int zr_window_is_collapsed(struct zr_context*, const char*); -int zr_window_is_closed(struct zr_context*, const char*); -int zr_window_is_active(struct zr_context*, const char*); - -void zr_window_close(struct zr_context *ctx, const char *name); -void zr_window_set_bounds(struct zr_context*, struct zr_rect); -void zr_window_set_position(struct zr_context*, struct zr_vec2); -void zr_window_set_size(struct zr_context*, struct zr_vec2); -void zr_window_set_focus(struct zr_context*, const char *name); -void zr_window_collapse(struct zr_context*, const char *name, - enum zr_collapse_states); -void zr_window_collapse_if(struct zr_context*, const char *name, - enum zr_collapse_states, int cond); -void zr_window_show(struct zr_context*, const char *name, enum zr_show_states); -void zr_window_show_if(struct zr_context*, const char *name, - enum zr_show_states, int cond); - -/*-------------------------------------------------------------- - * DRAWING - * -------------------------------------------------------------*/ -#define zr_command(t, c) ((const struct zr_command_##t*)c) -#define zr_foreach(c, ctx) for((c)=zr__begin(ctx); (c)!=0; (c)=zr__next(ctx, c)) -const struct zr_command* zr__next(struct zr_context*, const struct zr_command*); -const struct zr_command* zr__begin(struct zr_context*); - -#if ZR_COMPILE_WITH_VERTEX_BUFFER -struct zr_convert_config { - float global_alpha; - /* global alpha value */ - enum zr_anti_aliasing line_AA; - /* line anti-aliasing flag can be turned off if you are thight on memory */ - enum zr_anti_aliasing shape_AA; - /* shape anti-aliasing flag can be turned off if you are thight on memory */ - unsigned int circle_segment_count; - /* number of segments used for circles: default to 22 */ - unsigned int arc_segment_count; - /* number of segments used for arcs: default to 22 */ - unsigned int curve_segment_count; - /* number of segments used for curves: default to 22 */ - struct zr_draw_null_texture null; - /* handle to texture with a white pixel for shape drawing */ -}; -void zr_convert(struct zr_context*, struct zr_buffer *cmds, - struct zr_buffer *vertices, struct zr_buffer *elements, - const struct zr_convert_config*); - -#define zr_draw_foreach(cmd,ctx, b)\ - for((cmd)=zr__draw_begin(ctx, b); (cmd)!=0; (cmd)=zr__draw_next(cmd, b, ctx)) -const struct zr_draw_command* zr__draw_begin(const struct zr_context*, - const struct zr_buffer*); -const struct zr_draw_command* zr__draw_next(const struct zr_draw_command*, - const struct zr_buffer*, - const struct zr_context*); -#endif -/*-------------------------------------------------------------- - * INPUT - * -------------------------------------------------------------*/ -void zr_input_begin(struct zr_context*); -void zr_input_motion(struct zr_context*, int x, int y); -void zr_input_key(struct zr_context*, enum zr_keys, int down); -void zr_input_button(struct zr_context*, enum zr_buttons, int x, int y, int down); -void zr_input_scroll(struct zr_context*, float y); -void zr_input_glyph(struct zr_context*, const zr_glyph); -void zr_input_char(struct zr_context*, char); -void zr_input_unicode(struct zr_context *in, zr_rune); -void zr_input_end(struct zr_context*); - -/*-------------------------------------------------------------- - * STYLE - * -------------------------------------------------------------*/ -void zr_style_default(struct zr_context*); -void zr_style_from_table(struct zr_context*, const struct zr_color*); -const char *zr_style_color_name(enum zr_style_colors); -void zr_style_set_font(struct zr_context*, const struct zr_user_font*); - -/*-------------------------------------------------------------- - * LAYOUT - * -------------------------------------------------------------*/ -/* columns layouting with generated position, width and fixed height */ -void zr_layout_row_dynamic(struct zr_context*, float height, int cols); -void zr_layout_row_static(struct zr_context*, float height, int item_width, int cols); - -/* custom widget width and fixed height */ -void zr_layout_row_begin(struct zr_context*, enum zr_layout_format, - float row_height, int cols); -void zr_layout_row_push(struct zr_context*, float value); -void zr_layout_row_end(struct zr_context*); -void zr_layout_row(struct zr_context*, enum zr_layout_format, float height, - int cols, const float *ratio); - -/* custom position and size of widgets */ -void zr_layout_space_begin(struct zr_context*, enum zr_layout_format, - float height, int widget_count); -void zr_layout_space_push(struct zr_context*, struct zr_rect); -void zr_layout_space_end(struct zr_context*); - -/* utility functions */ -struct zr_rect zr_layout_space_bounds(struct zr_context*); -struct zr_vec2 zr_layout_space_to_screen(struct zr_context*, struct zr_vec2); -struct zr_vec2 zr_layout_space_to_local(struct zr_context*, struct zr_vec2); -struct zr_rect zr_layout_space_rect_to_screen(struct zr_context*, struct zr_rect); -struct zr_rect zr_layout_space_rect_to_local(struct zr_context*, struct zr_rect); - -/* group layout */ -int zr_group_begin(struct zr_context*, struct zr_panel*, const char *title, zr_flags); -void zr_group_end(struct zr_context*); - -/* tree layout */ -#define zr_layout_push(ctx, type, title, state)\ - zr__layout_push(ctx, type, title, state, __FILE__,__LINE__) -int zr__layout_push(struct zr_context*, enum zr_layout_node_type, const char *title, - enum zr_collapse_states initial_state, const char *file, int line); -void zr_layout_pop(struct zr_context*); - -/*-------------------------------------------------------------- - * WIDGETS - * -------------------------------------------------------------*/ -/* base widget calls for custom widgets */ -enum zr_widget_layout_states zr_widget(struct zr_rect*, const struct zr_context*); -enum zr_widget_layout_states zr_widget_fitting(struct zr_rect*, struct zr_context*, - struct zr_vec2 item_padding); -/* utilities (working on the next widget) */ -struct zr_rect zr_widget_bounds(struct zr_context*); -struct zr_vec2 zr_widget_position(struct zr_context*); -struct zr_vec2 zr_widget_size(struct zr_context*); -int zr_widget_is_hovered(struct zr_context*); -int zr_widget_is_mouse_clicked(struct zr_context*, enum zr_buttons); -int zr_widget_has_mouse_click_down(struct zr_context*, enum zr_buttons, int down); -void zr_spacing(struct zr_context*, int cols); - -/* content output widgets */ -void zr_text(struct zr_context*, const char*, zr_size, zr_flags); -void zr_text_colored(struct zr_context*, const char*, zr_size, zr_flags, - struct zr_color); -void zr_text_wrap(struct zr_context*, const char*, zr_size); -void zr_text_wrap_colored(struct zr_context*, const char*, zr_size, struct zr_color); -void zr_label(struct zr_context*, const char*, zr_flags); -void zr_label_colored(struct zr_context*, const char*, zr_flags align, struct zr_color); -void zr_label_wrap(struct zr_context*, const char*); -void zr_label_colored_wrap(struct zr_context*, const char*, struct zr_color); -void zr_image(struct zr_context*, struct zr_image); -#if ZR_COMPILE_WITH_STANDARD_IO -void zr_labelf(struct zr_context*, zr_flags, const char*, ...); -void zr_labelf_colored(struct zr_context*, zr_flags align, struct zr_color, const char*,...); -void zr_labelf_wrap(struct zr_context*, const char*,...); -void zr_labelf_colored_wrap(struct zr_context*, struct zr_color, const char*,...); - -void zr_value_bool(struct zr_context*, const char *prefix, int); -void zr_value_int(struct zr_context*, const char *prefix, int); -void zr_value_uint(struct zr_context*, const char *prefix, unsigned int); -void zr_value_float(struct zr_context*, const char *prefix, float); -void zr_value_color_byte(struct zr_context*, const char *prefix, struct zr_color); -void zr_value_color_float(struct zr_context*, const char *prefix, struct zr_color); -void zr_value_color_hex(struct zr_context*, const char *prefix, struct zr_color); -#endif - -/* buttons */ -int zr_button_text(struct zr_context *ctx, const char *title, zr_size len, enum zr_button_behavior); -int zr_button_label(struct zr_context *ctx, const char *title, enum zr_button_behavior); -int zr_button_color(struct zr_context*, struct zr_color, enum zr_button_behavior); -int zr_button_symbol(struct zr_context*, enum zr_symbol_type, enum zr_button_behavior); -int zr_button_image(struct zr_context*, struct zr_image img, enum zr_button_behavior); -int zr_button_symbol_label(struct zr_context*, enum zr_symbol_type, const char*, - zr_flags text_alignment, enum zr_button_behavior); -int zr_button_symbol_text(struct zr_context*, enum zr_symbol_type, const char*, - zr_size, zr_flags alignment, enum zr_button_behavior); -int zr_button_image_label(struct zr_context*, struct zr_image img, const char*, - zr_flags text_alignment, enum zr_button_behavior); -int zr_button_image_text(struct zr_context*, struct zr_image img, const char*, - zr_size, zr_flags alignment, enum zr_button_behavior); - -/* checkbox */ -int zr_check_label(struct zr_context*, const char*, int active); -int zr_check_text(struct zr_context*, const char*, zr_size,int active); -unsigned int zr_check_flags_label(struct zr_context*, const char*, - unsigned int flags, unsigned int value); -unsigned int zr_check_flags_text(struct zr_context*, const char*, zr_size, - unsigned int flags, unsigned int value); -int zr_checkbox_label(struct zr_context*, const char*, int *active); -int zr_checkbox_text(struct zr_context*, const char*, zr_size, int *active); -int zr_checkbox_flags_label(struct zr_context*, const char*, - unsigned int *flags, unsigned int value); -int zr_checkbox_flags_text(struct zr_context*, const char*, zr_size, - unsigned int *flags, unsigned int value); - -/* radio button */ -int zr_radio_label(struct zr_context*, const char*, int *active); -int zr_radio_text(struct zr_context*, const char*, zr_size, int *active); -int zr_option_label(struct zr_context*, const char*, int active); -int zr_option_text(struct zr_context*, const char*, zr_size, int active); - -/* selectable */ -int zr_selectable_label(struct zr_context*, const char*, zr_flags align, int *value); -int zr_selectable_text(struct zr_context*, const char*, zr_size, zr_flags align, int *value); -int zr_select_label(struct zr_context*, const char*, zr_flags align, int value); -int zr_select_text(struct zr_context*, const char*, zr_size, zr_flags align, int value); - -/* slider */ -float zr_slide_float(struct zr_context*, float min, float val, float max, float step); -int zr_slide_int(struct zr_context*, int min, int val, int max, int step); -int zr_slider_float(struct zr_context*, float min, float *val, float max, float step); -int zr_slider_int(struct zr_context*, int min, int *val, int max, int step); - -/* progressbar */ -int zr_progress(struct zr_context*, zr_size *cur, zr_size max, int modifyable); -zr_size zr_prog(struct zr_context*, zr_size cur, zr_size max, int modifyable); - -/* color picker */ -struct zr_color zr_color_picker(struct zr_context*, struct zr_color, - enum zr_color_picker_format); -int zr_color_pick(struct zr_context*, struct zr_color*, - enum zr_color_picker_format); - -/* property (dragging, increment/decrement and text input) */ -void zr_property_float(struct zr_context *layout, const char *name, - float min, float *val, float max, float step, - float inc_per_pixel); -void zr_property_int(struct zr_context *layout, const char *name, - int min, int *val, int max, int step, int inc_per_pixel); -float zr_propertyf(struct zr_context *layout, const char *name, float min, - float val, float max, float step, float inc_per_pixel); -int zr_propertyi(struct zr_context *layout, const char *name, int min, int val, - int max, int step, int inc_per_pixel); - -/* text manipulation */ -zr_flags zr_edit_string(struct zr_context*, zr_flags, char *buffer, zr_size *len, - zr_size max, zr_filter); -zr_flags zr_edit_buffer(struct zr_context*, zr_flags, struct zr_buffer*, zr_filter); - -/* simple chart */ -int zr_chart_begin(struct zr_context*, enum zr_chart_type, int num, float min, float max); -zr_flags zr_chart_push(struct zr_context*, float); -void zr_chart_end(struct zr_context*); -void zr_plot(struct zr_context*, enum zr_chart_type, const float *values, - int count, int offset); -void zr_plot_function(struct zr_context*, enum zr_chart_type, void *userdata, - float(*value_getter)(void* user, int index), - int count, int offset); - -/*-------------------------------------------------------------- - * POPUPS - * -------------------------------------------------------------*/ -/* normal blocking popups */ -int zr_popup_begin(struct zr_context*, struct zr_panel*, enum zr_popup_type, - const char*, zr_flags, struct zr_rect bounds); -void zr_popup_close(struct zr_context*); -void zr_popup_end(struct zr_context*); - -/* abstract combo box */ -int zr_combo_begin_text(struct zr_context*, struct zr_panel*, - const char *selected, zr_size, int max_height); -int zr_combo_begin_label(struct zr_context*, struct zr_panel*, - const char *selected, int max_height); -int zr_combo_begin_color(struct zr_context*, struct zr_panel*, - struct zr_color color, int max_height); -int zr_combo_begin_symbol(struct zr_context*, struct zr_panel*, - enum zr_symbol_type, int max_height); -int zr_combo_begin_symbol_label(struct zr_context*, struct zr_panel*, - const char *selected, enum zr_symbol_type, int height); -int zr_combo_begin_symbol_text(struct zr_context*, struct zr_panel*, - const char *selected, zr_size, enum zr_symbol_type, int height); -int zr_combo_begin_image(struct zr_context*, struct zr_panel*, - struct zr_image img, int max_height); -int zr_combo_begin_image_label(struct zr_context*, struct zr_panel*, - const char *selected, struct zr_image, int height); -int zr_combo_begin_image_text(struct zr_context*, struct zr_panel*, - const char *selected, zr_size, struct zr_image, int height); -int zr_combo_item_label(struct zr_context*, const char*, zr_flags alignment); -int zr_combo_item_text(struct zr_context*, const char*,zr_size, zr_flags alignment); -int zr_combo_item_image_label(struct zr_context*, struct zr_image, const char*, - zr_flags alignment); -int zr_combo_item_image_text(struct zr_context*, struct zr_image, const char*, - zr_size,zr_flags alignment); -int zr_combo_item_symbol_label(struct zr_context*, enum zr_symbol_type, - const char*, zr_flags alignment); -int zr_combo_item_symbol_text(struct zr_context*, enum zr_symbol_type, - const char*, zr_size, zr_flags alignment); -void zr_combo_close(struct zr_context*); -void zr_combo_end(struct zr_context*); - -/* combobox */ -int zr_combo(struct zr_context*, const char **items, int count, int selected, - int item_height); -int zr_combo_seperator(struct zr_context*, const char *items_seperated_by_seperator, - int seperator, int selected, int count, int item_height); -int zr_combo_string(struct zr_context*, const char *items_seperated_by_zeros, - int selected, int count, int item_height); -int zr_combo_callback(struct zr_context*, - void(item_getter)(void* data, int id, const char **out_text), - void *userdata, int selected, int count, int item_height); - -void zr_combobox(struct zr_context*, const char **items, int count, int *selected, - int item_height); -void zr_combobox_string(struct zr_context*, const char *items_seperated_by_zeros, - int *selected, int count, int item_height); -void zr_combobox_seperator(struct zr_context*, const char *items_seperated_by_seperator, - int seperator,int *selected, int count, int item_height); -void zr_combobox_callback(struct zr_context*, - void(item_getter)(void* data, int id, const char **out_text), - void *userdata, int *selected, int count, int item_height); - -/* contextual */ -int zr_contextual_begin(struct zr_context*, struct zr_panel*, zr_flags, - struct zr_vec2, struct zr_rect trigger_bounds); -int zr_contextual_item_text(struct zr_context*, const char*, zr_size,zr_flags align); -int zr_contextual_item_label(struct zr_context*, const char*, zr_flags align); -int zr_contextual_item_image_label(struct zr_context*, struct zr_image, - const char*, zr_flags alignment); -int zr_contextual_item_image_text(struct zr_context*, struct zr_image, - const char*, zr_size len, zr_flags alignment); -int zr_contextual_item_symbol_label(struct zr_context*, enum zr_symbol_type, - const char*, zr_flags alignment); -int zr_contextual_item_symbol_text(struct zr_context*, enum zr_symbol_type, - const char*, zr_size, zr_flags alignment); -void zr_contextual_close(struct zr_context*); -void zr_contextual_end(struct zr_context*); - -/* tooltip */ -void zr_tooltip(struct zr_context*, const char*); -int zr_tooltip_begin(struct zr_context*, struct zr_panel*, float width); -void zr_tooltip_end(struct zr_context*); - -/*-------------------------------------------------------------- - * MENU - * -------------------------------------------------------------*/ -void zr_menubar_begin(struct zr_context*); -void zr_menubar_end(struct zr_context*); - -int zr_menu_begin_text(struct zr_context*, struct zr_panel*, const char*, - zr_size, zr_flags align, float width); -int zr_menu_begin_label(struct zr_context*, struct zr_panel*, const char*, - zr_flags align, float width); -int zr_menu_begin_image(struct zr_context*, struct zr_panel*, const char*, - struct zr_image, float width); -int zr_menu_begin_image_text(struct zr_context*, struct zr_panel*, const char*, - zr_size,zr_flags align,struct zr_image, float width); -int zr_menu_begin_image_label(struct zr_context*, struct zr_panel*, const char*, - zr_flags align,struct zr_image, float width); -int zr_menu_begin_symbol(struct zr_context*, struct zr_panel*, const char*, - enum zr_symbol_type, float width); -int zr_menu_begin_symbol_text(struct zr_context*, struct zr_panel*, const char*, - zr_size,zr_flags align,enum zr_symbol_type, float width); -int zr_menu_begin_symbol_label(struct zr_context*, struct zr_panel*, const char*, - zr_flags align,enum zr_symbol_type, float width); -int zr_menu_item_text(struct zr_context*, const char*, zr_size,zr_flags align); -int zr_menu_item_label(struct zr_context*, const char*, zr_flags alignment); -int zr_menu_item_image_label(struct zr_context*, struct zr_image, - const char*, zr_flags alignment); -int zr_menu_item_image_text(struct zr_context*, struct zr_image, - const char*, zr_size len, zr_flags alignment); -int zr_menu_item_symbol_text(struct zr_context*, enum zr_symbol_type, - const char*, zr_size, zr_flags alignment); -int zr_menu_item_symbol_label(struct zr_context*, enum zr_symbol_type, - const char*, zr_flags alignment); -void zr_menu_close(struct zr_context*); -void zr_menu_end(struct zr_context*); - -#ifdef __cplusplus -} -#endif - -#endif /* ZR_H_ */