From efe926594912c96b007f4aff61491a41b0cc2251 Mon Sep 17 00:00:00 2001 From: vurtun Date: Sun, 6 Sep 2015 19:13:07 +0200 Subject: [PATCH] added optional anti-aliased vertex buffer output --- demo/demo.c | 47 +- demo/nanovg.c | 43 +- demo/opengl.c | 769 +++++++++++++++++++++++++++++ demo/xlib.c | 33 +- gui.c | 1298 +++++++++++++++++++++++++++++++++++++++++++------ gui.h | 781 +++++++++++++++++++++-------- 6 files changed, 2541 insertions(+), 430 deletions(-) create mode 100644 demo/opengl.c diff --git a/demo/demo.c b/demo/demo.c index 8494ee8..bf6f0f2 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -1,5 +1,6 @@ #define MAX_BUFFER 64 -#define MAX_MEMORY (16 * 1024) +#define MAX_MEMORY (32 * 1024) +#define MAX_COMMAND_MEMORY (16 * 1024) #define WINDOW_WIDTH 1200 #define WINDOW_HEIGHT 800 @@ -479,11 +480,11 @@ struct state { struct demo_gui { gui_bool running; - void *memory; - const struct gui_input *input; + struct gui_buffer memory; + struct gui_input input; struct gui_command_queue queue; struct gui_style config; - struct gui_font font; + struct gui_user_font font; struct gui_window panel; struct gui_window sub; struct state state; @@ -543,7 +544,7 @@ widget_panel(struct gui_context *panel, struct state *demo) /* combo boxes */ if (!demo->scaleable) gui_layout_row_static(panel, 30, 150, 1); - else gui_layout_row_dynamic(panel, 25, 1); + else gui_layout_row_dynamic(panel, 30, 1); combo_box(panel, &demo->combo, weapons, LEN(weapons)); prog_combo_box(panel, demo->prog_values, LEN(demo->prog_values), &demo->progcom); color_combo_box(panel, &demo->colcom); @@ -566,23 +567,6 @@ widget_panel(struct gui_context *panel, struct state *demo) } } -static void -table_panel(struct gui_context *panel) -{ - gui_size i = 0; - const char *table[] = {"Move forward", "w", "Move back", "s", "Move left", "a", - "Move right", "d", "Jump", "SPACE", "Duck", "CTRL"}; - gui_table_begin(panel, GUI_TABLE_HHEADER, 30, 2); - gui_label_colored(panel, "MOVEMENT", GUI_TEXT_CENTERED, gui_rgba(178, 122, 1, 255)); - gui_label_colored(panel, "KEY/BUTTON", GUI_TEXT_CENTERED, gui_rgba(178, 122, 1, 255)); - for (i = 0; i < LEN(table); i += 2) { - gui_table_row(panel); - gui_label(panel, table[i], GUI_TEXT_LEFT); - gui_label(panel, table[i+1], GUI_TEXT_CENTERED); - } - gui_table_end(panel); -} - /* ----------------------------------------------------------------- * STYLE * ----------------------------------------------------------------- */ @@ -686,24 +670,25 @@ paste(gui_handle handle, struct gui_edit_box *box) * INIT * ----------------------------------------------------------------- */ static void -init_demo(struct demo_gui *gui, struct gui_font *font) +init_demo(struct demo_gui *gui) { + void *memory; struct gui_style *config = &gui->config; struct state *win = &gui->state; struct gui_clipboard clip; - gui->font = *font; gui->running = gui_true; - gui_command_queue_init_fixed(&gui->queue, gui->memory, MAX_MEMORY); - gui_style_default(config, GUI_DEFAULT_ALL, font); + memory = gui_buffer_alloc(&gui->memory, GUI_BUFFER_FRONT, MAX_COMMAND_MEMORY, 0); + gui_command_queue_init_fixed(&gui->queue, memory, MAX_COMMAND_MEMORY); + gui_style_default(config, GUI_DEFAULT_ALL, &gui->font); /* panel */ gui_window_init(&gui->panel, gui_rect(30, 30, 280, 530), GUI_WINDOW_BORDER|GUI_WINDOW_MOVEABLE|GUI_WINDOW_SCALEABLE, - &gui->queue, config, gui->input); + &gui->queue, config, &gui->input); gui_window_init(&gui->sub, gui_rect(400, 50, 220, 180), GUI_WINDOW_BORDER|GUI_WINDOW_MOVEABLE|GUI_WINDOW_SCALEABLE, - &gui->queue, config, gui->input); + &gui->queue, config, &gui->input); /* widget state */ tree_init(&win->test); @@ -854,12 +839,6 @@ run_demo(struct demo_gui *gui) state->shelf = gui_shelf_end(&layout, &tab); } - /* table */ - gui_layout_row_dynamic(&layout, 180, 1); - gui_group_begin(&layout, &tab, "Table", 0, state->table); - table_panel(&tab); - state->table = gui_group_end(&layout, &tab); - { /* tree */ struct gui_tree tree; diff --git a/demo/nanovg.c b/demo/nanovg.c index d5a78ff..af18c43 100644 --- a/demo/nanovg.c +++ b/demo/nanovg.c @@ -98,14 +98,6 @@ draw(NVGcontext *nvg, struct gui_command_queue *queue, int width, int height) nvgFillColor(nvg, nvgRGBA(l->color.r, l->color.g, l->color.b, l->color.a)); nvgFill(nvg); } break; - case GUI_COMMAND_QUAD: { - const struct gui_command_quad *q = gui_command(quad, cmd); - nvgBeginPath(nvg); - nvgMoveTo(nvg, q->begin.x, q->begin.y); - nvgQuadTo(nvg, q->ctrl.x, q->ctrl.y, q->end.x, q->end.y); - nvgStrokeColor(nvg, nvgRGBA(q->color.r, q->color.g, q->color.b, q->color.a)); - nvgStroke(nvg); - } break; case GUI_COMMAND_CURVE: { const struct gui_command_curve *q = gui_command(curve, cmd); nvgBeginPath(nvg); @@ -248,8 +240,6 @@ main(int argc, char *argv[]) unsigned int dt; /* GUI */ - struct gui_input in; - struct gui_font font; struct demo_gui gui; if (argc < 2) { @@ -283,32 +273,31 @@ main(int argc, char *argv[]) nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); /* GUI */ - memset(&in, 0, sizeof in); memset(&gui, 0, sizeof gui); - gui.memory = malloc(MAX_MEMORY); - font.userdata.ptr = vg; - nvgTextMetrics(vg, NULL, NULL, &font.height); - font.width = font_get_width; - gui.input = ∈ - init_demo(&gui, &font); + gui_buffer_init_fixed(&gui.memory, calloc(MAX_MEMORY, 1), MAX_MEMORY); + gui.font.userdata.ptr = vg; + gui.font.width = font_get_width; + nvgTextMetrics(vg, NULL, NULL, &gui.font.height); + init_demo(&gui); while (gui.running) { /* Input */ SDL_Event evt; started = SDL_GetTicks(); - gui_input_begin(&in); + gui_input_begin(&gui.input); while (SDL_PollEvent(&evt)) { if (evt.type == SDL_WINDOWEVENT) resize(&evt); else if (evt.type == SDL_QUIT) goto cleanup; - else if (evt.type == SDL_KEYUP) key(&in, &evt, gui_false); - else if (evt.type == SDL_KEYDOWN) key(&in, &evt, gui_true); - else if (evt.type == SDL_MOUSEBUTTONDOWN) btn(&in, &evt, gui_true); - else if (evt.type == SDL_MOUSEBUTTONUP) btn(&in, &evt, gui_false); - else if (evt.type == SDL_MOUSEMOTION) motion(&in, &evt); - else if (evt.type == SDL_TEXTINPUT) text(&in, &evt); - else if (evt.type == SDL_MOUSEWHEEL) gui_input_scroll(&in, evt.wheel.y); + else if (evt.type == SDL_KEYUP) key(&gui.input, &evt, gui_false); + else if (evt.type == SDL_KEYDOWN) key(&gui.input, &evt, gui_true); + else if (evt.type == SDL_MOUSEBUTTONDOWN) btn(&gui.input, &evt, gui_true); + else if (evt.type == SDL_MOUSEBUTTONUP) btn(&gui.input, &evt, gui_false); + else if (evt.type == SDL_MOUSEMOTION) motion(&gui.input, &evt); + else if (evt.type == SDL_TEXTINPUT) text(&gui.input, &evt); + else if (evt.type == SDL_MOUSEWHEEL) + gui_input_scroll(&gui.input,(float)evt.wheel.y); } - gui_input_end(&in); + gui_input_end(&gui.input); /* GUI */ SDL_GetWindowSize(win, &width, &height); @@ -328,7 +317,7 @@ main(int argc, char *argv[]) cleanup: /* Cleanup */ - free(gui.memory); + free(gui_buffer_memory(&gui.memory)); nvgDeleteGLES2(vg); SDL_GL_DeleteContext(glContext); SDL_DestroyWindow(win); diff --git a/demo/opengl.c b/demo/opengl.c new file mode 100644 index 0000000..ab8b824 --- /dev/null +++ b/demo/opengl.c @@ -0,0 +1,769 @@ +/* + Copyright (c) 2015 + vurtun + MIT licence + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +/* macros */ +#define DTIME 17 +#define FONT_ATLAS_DEPTH 4 + +#define MAX_DRAW_COMMAND_MEMORY (4 * 1024) + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define CLAMP(i,v,x) (MAX(MIN(v,x), i)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) +#define UNUSED(a) ((void)(a)) + +#include "../gui.h" + +static void clipboard_set(const char *text){SDL_SetClipboardText(text);} +static gui_bool clipboard_is_filled(void) {return SDL_HasClipboardText();} +static const char* clipboard_get(void){return SDL_GetClipboardText();} + +#include "demo.c" + +/* ============================================================== + * + * 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; +} + +/* ============================================================== + * + * Font + * + * ===============================================================*/ +struct texCoord { + float u; + float v; +}; + +enum font_atlas_dimension { + FONT_ATLAS_DIM_64 = 64, + FONT_ATLAS_DIM_128 = 128, + FONT_ATLAS_DIM_256 = 256, + FONT_ATLAS_DIM_512 = 512, + FONT_ATLAS_DIM_1024 = 1024, + FONT_ATLAS_DIM_2048 = 2048 +}; + +struct font_atlas { + enum font_atlas_dimension dim; + gui_size range; + gui_size size; + gui_byte *memory; +}; + +struct font_glyph { + unsigned int code; + float xadvance; + short width, height; + float xoff, yoff; + struct texCoord uv[2]; +}; + +struct font { + float height; + float scale; + GLuint texture; + unsigned int glyph_count; + struct font_glyph *glyphes; + const struct font_glyph *fallback; +}; + +static void +font_load_glyph(unsigned int code, struct font_glyph *glyph, FT_GlyphSlot slot) +{ + glyph->code = code; + glyph->width = (short)slot->bitmap.width; + glyph->height = (short)slot->bitmap.rows; + glyph->xoff = (float)slot->bitmap_left; + glyph->yoff = (float)slot->bitmap_top; + glyph->xadvance = (float)(slot->advance.x >> 6); +} + +static void +font_load_glyphes(FT_Face face, struct font *font, size_t range) +{ + size_t i; + int ft_err; + for (i = 0; i < range; ++i) { + unsigned int index = FT_Get_Char_Index(face, i); + if (!index) continue; + ft_err = FT_Load_Glyph(face, index, 0); + if (ft_err) continue; + ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (ft_err) continue; + font_load_glyph(index, &font->glyphes[i], face->glyph); + } +} + +static void +font_pack_glyphes(struct font *font, float width, float height, size_t range) +{ + size_t i; + float xoff = 0, yoff = 0; + float max_height = 0.0f; + for (i = 0; i < range; ++i) { + struct font_glyph *glyph = &font->glyphes[i]; + if ((xoff + glyph->width) > width) { + yoff += max_height; + max_height = 0.0f; + xoff = 0.0f; + } + + glyph->uv[0].u = xoff / width; + glyph->uv[0].v = yoff / height; + glyph->uv[1].u = (xoff + glyph->width) / width; + glyph->uv[1].v = (yoff + glyph->height) / height; + if (glyph->height > max_height) + max_height = glyph->height; + xoff += glyph->width; + } +} + +static void +font_atlas_blit(struct font_atlas *atlas, FT_GlyphSlot glyph, + size_t off_x, size_t off_y) +{ + size_t y, x; + size_t width = glyph->bitmap.width; + size_t height = glyph->bitmap.rows; + const size_t pitch = atlas->dim * FONT_ATLAS_DEPTH; + for (y = 0; y < height; y++) { + size_t x_off = off_x * FONT_ATLAS_DEPTH; + size_t y_off = (off_y + y) * pitch; + unsigned char *dst = &atlas->memory[y_off + x_off]; + for (x = 0; x < width; ++x) { + dst[0] = 255; + dst[1] = 255; + dst[2] = 255; + dst[3] = glyph->bitmap.buffer[y * width + x]; + dst += FONT_ATLAS_DEPTH; + } + } +} + +static void +font_bake_glyphes(FT_Face face, struct font_atlas *atlas, + const struct font *font) +{ + size_t i; + int ft_err; + for (i = 0; i < atlas->range; ++i) { + size_t x, y; + struct font_glyph *glyph = &font->glyphes[i]; + unsigned int index = FT_Get_Char_Index(face, i); + + if (!index) continue; + ft_err = FT_Load_Glyph(face, index, 0); + if (ft_err) continue; + ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (ft_err) continue; + + x = (gui_size)(glyph->uv[0].u * (gui_float)atlas->dim); + y = (gui_size)(glyph->uv[0].v * (gui_float)atlas->dim); + font_atlas_blit(atlas, face->glyph, x, y); + } +} + +static int +font_load(struct font *font, struct font_atlas *atlas, unsigned int height, + const unsigned char *data, size_t size) +{ + int ret = 1; + FT_Library ft_lib; + FT_Face ft_face; + + assert(font); + assert(atlas); + assert(font->glyphes); + assert(atlas->memory); + + if (!font || !atlas) + return gui_false; + if (!font->glyphes || !atlas->memory) + return gui_false; + if (FT_Init_FreeType(&ft_lib)) + return gui_false; + if (FT_New_Memory_Face(ft_lib, data, (FT_Long)size, 0, &ft_face)) + goto cleanup; + if (FT_Select_Charmap(ft_face, FT_ENCODING_UNICODE)) + goto failed; + if (FT_Set_Char_Size(ft_face, height << 6, height << 6, 96, 96)) + goto failed; + + font_load_glyphes(ft_face, font, atlas->range); + font_pack_glyphes(font, atlas->dim, atlas->dim, atlas->range); + font_bake_glyphes(ft_face, atlas, font); + +failed: + FT_Done_Face(ft_face); +cleanup: + FT_Done_FreeType(ft_lib); + return ret; +} + +static gui_size +font_get_text_width(gui_handle handle, const gui_char *t, gui_size l) +{ + long unicode; + size_t text_width = 0; + const struct font_glyph *glyph; + size_t text_len = 0; + size_t glyph_len; + struct font *font = (struct font*)handle.ptr; + assert(font); + if (!t || !l) return 0; + + glyph_len = gui_utf_decode(t, &unicode, l); + while (text_len < l && glyph_len) { + if (unicode == GUI_UTF_INVALID) return 0; + glyph = (unicode < font->glyph_count) ? &font->glyphes[unicode] : font->fallback; + glyph = (glyph->code == 0) ? font->fallback : glyph; + + text_len += glyph_len; + text_width += (gui_size)((float)glyph->xadvance * font->scale); + glyph_len = gui_utf_decode(t + text_len, &unicode, l - text_len); + } + return text_width; +} + +static void +font_query_font_glyph(gui_handle handle, struct gui_user_font_glyph *glyph, + gui_long unicode, gui_long next_codepoint) +{ + const struct font_glyph *g; + const struct font *font = (struct font*)handle.ptr; + UNUSED(next_codepoint); + if (unicode == GUI_UTF_INVALID) return; + g = (unicode < font->glyph_count) ? + &font->glyphes[unicode] : + font->fallback; + g = (g->code == 0) ? font->fallback : g; + + glyph->offset.x = g->xoff * font->scale; + glyph->offset.y = g->yoff * font->scale; + glyph->width = g->width * font->scale; + glyph->height = g->height * font->scale; + glyph->xadvance = g->xadvance * font->scale; + glyph->uv[0] = gui_vec2(g->uv[0].u, g->uv[0].v); + glyph->uv[1] = gui_vec2(g->uv[1].u, g->uv[1].v); +} + +static void +font_del(struct font *font) +{ + glDeleteTextures(1, &font->texture); + free(font->glyphes); + free(font); +} + +static struct font* +font_new(const char *path, unsigned int font_height, unsigned int bake_height, + size_t range, enum font_atlas_dimension dim) +{ + gui_byte *ttf_blob; + gui_size ttf_blob_size; + struct font_atlas atlas; + struct font *font = (struct font*)calloc(sizeof(struct font), 1); + + atlas.dim = dim; + atlas.range = range; + atlas.size = atlas.dim * atlas.dim * FONT_ATLAS_DEPTH; + atlas.memory = (gui_byte*)calloc((gui_size)atlas.size, 1); + + font->glyph_count = (unsigned int)atlas.range; + font->glyphes = (struct font_glyph*)calloc(atlas.range, sizeof(struct font_glyph)); + font->fallback = &font->glyphes['?']; + font->scale = (float)font_height / (gui_float)bake_height; + font->height = (float)font_height; + + ttf_blob = (unsigned char*)file_load(path, &ttf_blob_size); + if (!font_load(font, &atlas, bake_height, ttf_blob, ttf_blob_size)) + goto failed; + + glGenTextures(1, &font->texture); + glBindTexture(GL_TEXTURE_2D, font->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim, dim, 0, + GL_RGBA, GL_UNSIGNED_BYTE, atlas.memory); + + free(atlas.memory); + free(ttf_blob); + return font; + +failed: + free(atlas.memory); + free(ttf_blob); + font_del(font); + return NULL; +} + +/* ============================================================== + * + * Draw + * + * ===============================================================*/ +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 null_tex; + struct gui_draw_list draw_list; + struct gui_draw_null_texture null; + struct gui_buffer cmds; +}; + +#define glerror() glerror_(__FILE__, __LINE__) +static void +glerror_(const char *file, int line) +{ + const GLenum code = glGetError(); + if (code == GL_INVALID_ENUM) + fprintf(stdout, "[GL] Error: (%s:%d) invalid value!\n", file, line); + else if (code == GL_INVALID_OPERATION) + fprintf(stdout, "[GL] Error: (%s:%d) invalid operation!\n", file, line); + else if (code == GL_INVALID_FRAMEBUFFER_OPERATION) + fprintf(stdout, "[GL] Error: (%s:%d) invalid frame op!\n", file, line); + else if (code == GL_OUT_OF_MEMORY) + fprintf(stdout, "[GL] Error: (%s:%d) out of memory!\n", file, line); + else if (code == GL_STACK_UNDERFLOW) + fprintf(stdout, "[GL] Error: (%s:%d) stack underflow!\n", file, line); + else if (code == GL_STACK_OVERFLOW) + fprintf(stdout, "[GL] Error: (%s:%d) stack overflow!\n", file, line); +} + +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"); + glerror(); + + + { + /* buffer setup */ + size_t vs = sizeof(struct gui_draw_vertex); + size_t vp = offsetof(struct gui_draw_vertex, position); + size_t vt = offsetof(struct gui_draw_vertex, uv); + size_t vc = offsetof(struct gui_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); + glerror(); + + 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); + glerror(); + } + + { + /* create default white texture which is needed to draw primitives. Any + texture with a white pixel would do but since I do not have one I have + to create one. */ + void *mem = calloc(64*64*4, 1); + memset(mem, 255, 64*64*4); + glGenTextures(1, &dev->null_tex); + glBindTexture(GL_TEXTURE_2D, dev->null_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_RGBA8, 64, 64, 0, + GL_RGBA, GL_UNSIGNED_BYTE, mem); + free(mem); + glerror(); + } + dev->null.texture.id = (gui_int)dev->null_tex; + dev->null.uv = gui_vec2(0.0f, 0.0f); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glerror(); +} + +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->null_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); +} + +static void +device_draw(struct device *dev, struct gui_command_queue *queue, int width, int 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}, + }; + 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); + glerror(); + + /* setup global state */ + glViewport(0, 0, width, height); + 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); + glerror(); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + glerror(); + + { + /* convert from command queue into draw list and draw to screen */ + struct gui_draw_list draw_list; + const struct gui_draw_command *cmd; + static const GLsizeiptr max_vertex_memory = 128 * 1024; + static const GLsizeiptr max_element_memory = 32 * 1024; + struct gui_buffer vbuf, ebuf; + void *vertexes, *elements; + const gui_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + memset(&draw_list, 0, sizeof(draw_list)); + 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); + glerror(); + + /* load draw vertexes & elements directly into vertex + element buffer */ + vertexes = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + gui_buffer_init_fixed(&vbuf, vertexes, (gui_size)max_vertex_memory); + gui_buffer_init_fixed(&ebuf, elements, (gui_size)max_element_memory); + gui_draw_list_init(&draw_list, &dev->cmds, &vbuf, &ebuf, + sinf, cosf, dev->null, GUI_ANTI_ALIASING_ON); + gui_draw_list_load(&draw_list, queue, 1.0f, 22); + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + glerror(); + + /* iterate and execute each draw command */ + gui_foreach_draw_command(cmd, &draw_list) { + 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; + glerror(); + } + + gui_command_queue_clear(queue); + gui_draw_list_clear(&draw_list); + } + + /* restore old state */ + glUseProgram((GLuint)last_prog); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo); + glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_ebo); + glBindVertexArray((GLuint)last_vao); + glDisable(GL_SCISSOR_TEST); + glerror(); +} + +/* ============================================================== + * + * APP + * + * ===============================================================*/ +static void +key(struct gui_input *in, SDL_Event *evt, gui_bool down) +{ + const Uint8* state = SDL_GetKeyboardState(NULL); + SDL_Keycode sym = evt->key.keysym.sym; + if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT) + gui_input_key(in, GUI_KEY_SHIFT, down); + else if (sym == SDLK_DELETE) + gui_input_key(in, GUI_KEY_DEL, down); + else if (sym == SDLK_RETURN) + gui_input_key(in, GUI_KEY_ENTER, down); + else if (sym == SDLK_SPACE) + gui_input_key(in, GUI_KEY_SPACE, down); + else if (sym == SDLK_BACKSPACE) + gui_input_key(in, GUI_KEY_BACKSPACE, down); + else if (sym == SDLK_LEFT) + gui_input_key(in, GUI_KEY_LEFT, down); + else if (sym == SDLK_RIGHT) + gui_input_key(in, GUI_KEY_RIGHT, down); + else if (sym == SDLK_c) + gui_input_key(in, GUI_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_v) + gui_input_key(in, GUI_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_x) + gui_input_key(in, GUI_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); +} + +static void +motion(struct gui_input *in, SDL_Event *evt) +{ + const gui_int x = evt->motion.x; + const gui_int y = evt->motion.y; + gui_input_motion(in, x, y); +} + +static void +btn(struct gui_input *in, SDL_Event *evt, gui_bool down) +{ + const gui_int x = evt->button.x; + const gui_int y = evt->button.y; + if (evt->button.button == SDL_BUTTON_LEFT) + gui_input_button(in, GUI_BUTTON_LEFT, x, y, down); + if (evt->button.button == SDL_BUTTON_RIGHT) + gui_input_button(in, GUI_BUTTON_RIGHT, x, y, down); +} + +static void +text(struct gui_input *in, SDL_Event *evt) +{ + gui_glyph glyph; + memcpy(glyph, evt->text.text, GUI_UTF_SIZE); + gui_input_glyph(in, glyph); +} + +static void +resize(SDL_Event *evt) +{ + if (evt->window.event != SDL_WINDOWEVENT_RESIZED) return; + glViewport(0, 0, evt->window.data1, evt->window.data2); +} + + +int +main(int argc, char *argv[]) +{ + /* Platform */ + void *mem; + const char *font_path; + SDL_Window *win; + SDL_GLContext glContext; + struct font *glfont; + int win_width, win_height; + unsigned int started; + unsigned int dt; + int width = 0, height = 0; + + /* GUI */ + struct device device; + struct demo_gui gui; + font_path = argv[1]; + if (argc < 2) + die("Missing TTF Font file argument!"); + + /* 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); + glfont = font_new(font_path, 10, 10, 255, FONT_ATLAS_DIM_256); + glewExperimental = 1; + if (glewInit() != GLEW_OK) + die("Failed to setup GLEW\n"); + + /* GUI */ + memset(&gui, 0, sizeof gui); + gui_buffer_init_fixed(&gui.memory, calloc(MAX_MEMORY, 1), MAX_MEMORY); + gui.font.userdata.ptr = glfont; + gui.font.height = glfont->height; + gui.font.width = font_get_text_width; + gui.font.query = font_query_font_glyph; + gui.font.texture.id = (gui_int)glfont->texture; + mem = gui_buffer_alloc(&gui.memory, GUI_BUFFER_FRONT, MAX_DRAW_COMMAND_MEMORY, 0); + gui_buffer_init_fixed(&device.cmds, mem, MAX_DRAW_COMMAND_MEMORY); + + init_demo(&gui); + device_init(&device); + + while (gui.running) { + /* Input */ + SDL_Event evt; + started = SDL_GetTicks(); + gui_input_begin(&gui.input); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_WINDOWEVENT) resize(&evt); + else if (evt.type == SDL_QUIT) goto cleanup; + else if (evt.type == SDL_KEYUP) key(&gui.input, &evt, gui_false); + else if (evt.type == SDL_KEYDOWN) key(&gui.input, &evt, gui_true); + else if (evt.type == SDL_MOUSEBUTTONDOWN) btn(&gui.input, &evt, gui_true); + else if (evt.type == SDL_MOUSEBUTTONUP) btn(&gui.input, &evt, gui_false); + else if (evt.type == SDL_MOUSEMOTION) motion(&gui.input, &evt); + else if (evt.type == SDL_TEXTINPUT) text(&gui.input, &evt); + else if (evt.type == SDL_MOUSEWHEEL) + gui_input_scroll(&gui.input,(float)evt.wheel.y); + } + gui_input_end(&gui.input); + + /* GUI */ + SDL_GetWindowSize(win, &width, &height); + gui.w = (gui_size)width; + gui.h = (gui_size)height; + run_demo(&gui); + + /* Draw */ + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.4f, 0.4f, 0.4f, 1.0f); + device_draw(&device, &gui.queue, width, height); + SDL_GL_SwapWindow(win); + + /* Timing */ + dt = SDL_GetTicks() - started; + if (dt < DTIME) + SDL_Delay(DTIME - dt); + } + +cleanup: + /* Cleanup */ + free(gui_buffer_memory(&gui.memory)); + font_del(glfont); + device_shutdown(&device); + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(win); + SDL_Quit(); + return 0; +} + diff --git a/demo/xlib.c b/demo/xlib.c index f723e82..ccacd15 100644 --- a/demo/xlib.c +++ b/demo/xlib.c @@ -363,8 +363,9 @@ draw(XSurface *surf, struct gui_command_queue *queue) case GUI_COMMAND_TEXT: { const struct gui_command_text *t = gui_command(text, cmd); surface_draw_text(surf, t->x, t->y, t->w, t->h, (const char*)t->string, - t->length, (XFont*)t->font.ptr, t->background, t->foreground); + t->length, (XFont*)t->font->userdata.ptr, t->background, t->foreground); } break; + case GUI_COMMAND_CURVE: case GUI_COMMAND_IMAGE: case GUI_COMMAND_MAX: default: break; @@ -439,8 +440,6 @@ main(int argc, char *argv[]) long started; XWindow xw; - struct gui_input in; - struct gui_font font; struct demo_gui gui; /* Platform */ @@ -467,31 +466,29 @@ main(int argc, char *argv[]) xw.font = font_create(xw.dpy, "fixed"); /* GUI */ - memset(&in, 0, sizeof in); memset(&gui, 0, sizeof gui); - font.userdata.ptr = xw.font; - font.height = (gui_float)xw.font->height; - font.width = font_get_text_width; - gui.memory = calloc(MAX_MEMORY, 1); - gui.input = ∈ - init_demo(&gui, &font); + gui_buffer_init_fixed(&gui.memory, calloc(MAX_MEMORY, 1), MAX_MEMORY); + gui.font.userdata.ptr = xw.font; + gui.font.height = (gui_float)xw.font->height; + gui.font.width = font_get_text_width; + init_demo(&gui); while (gui.running) { /* Input */ XEvent evt; started = timestamp(); - gui_input_begin(&in); + gui_input_begin(&gui.input); while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &evt)) { if (evt.type == KeyPress) - key(&xw, &in, &evt, gui_true); - else if (evt.type == KeyRelease) key(&xw, &in, &evt, gui_false); - else if (evt.type == ButtonPress) btn(&in, &evt, gui_true); - else if (evt.type == ButtonRelease) btn(&in, &evt, gui_false); - else if (evt.type == MotionNotify) motion(&in, &evt); + key(&xw, &gui.input, &evt, gui_true); + else if (evt.type == KeyRelease) key(&xw, &gui.input, &evt, gui_false); + else if (evt.type == ButtonPress) btn(&gui.input, &evt, gui_true); + else if (evt.type == ButtonRelease) btn(&gui.input, &evt, gui_false); + else if (evt.type == MotionNotify) motion(&gui.input, &evt); else if (evt.type == Expose || evt.type == ConfigureNotify) resize(&xw, xw.surf); } - gui_input_end(&in); + gui_input_end(&gui.input); /* GUI */ run_demo(&gui); @@ -509,7 +506,7 @@ main(int argc, char *argv[]) sleep_for(DTIME - dt); } - free(gui.memory); + free(gui_buffer_memory(&gui.memory)); font_del(xw.dpy, xw.font); surface_del(xw.surf); XUnmapWindow(xw.dpy, xw.win); diff --git a/gui.c b/gui.c index ea3c4e4..96dc5b9 100644 --- a/gui.c +++ b/gui.c @@ -34,12 +34,10 @@ #define GUI_CONTAINS(x, y, w, h, bx, by, bw, bh)\ (GUI_INBOX(x,y, bx, by, bw, bh) && GUI_INBOX(x+w,y+h, bx, by, bw, bh)) - #define gui_vec2_mov(to,from) (to).x = (from).x, (to).y = (from).y #define gui_vec2_sub(a, b) gui_vec2((a).x - (b).x, (a).y - (b).y) #define gui_vec2_add(a, b) gui_vec2((a).x + (b).x, (a).y + (b).y) #define gui_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y) -#define gui_vec2_len(a) gui_sqrt(gui_vec2_len_sqr(a)) #define gui_vec2_inv_len(a) gui_inv_sqrt(gui_vec2_len_sqr(a)) #define gui_vec2_muls(a, t) gui_vec2((a).x * (t), (a).y * (t)) #define gui_vec2_lerp(a, b, t) gui_vec2(GUI_LERP((a).x, (b).x, t), GUI_LERP((a).y, (b).y, t)) @@ -65,12 +63,11 @@ template struct gui_alignof{struct Big {T x; char c;}; enum { #endif enum gui_tree_node_symbol {GUI_TREE_NODE_BULLET, GUI_TREE_NODE_TRIANGLE}; -static const struct gui_rect gui_null_rect = {-9999.0f, -9999.0f, 2*9999.0f, 2*9999.0f}; +static const struct gui_rect gui_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; static const gui_byte gui_utfbyte[GUI_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static const gui_byte gui_utfmask[GUI_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static const long gui_utfmin[GUI_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x100000}; static const long gui_utfmax[GUI_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; - /* * ============================================================== * @@ -78,6 +75,19 @@ static const long gui_utfmax[GUI_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0 * * =============================================================== */ +static gui_float +gui_inv_sqrt(gui_float number) +{ + gui_float x2; + const gui_float threehalfs = 1.5f; + union {gui_uint i; gui_float f;} conv; + 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; +} + struct gui_rect gui_get_null_rect(void) { @@ -809,7 +819,6 @@ gui_buffer_reset(struct gui_buffer *buffer, enum gui_buffer_allocation_type type else buffer->size = buffer->memory.size; } else { /* reset front buffer either back to back marker or empty */ - buffer->marker[type].offset = buffer->allocated; if (buffer->marker[type].active) buffer->allocated = buffer->marker[type].offset; else buffer->allocated = 0; @@ -850,6 +859,22 @@ gui_buffer_info(struct gui_memory_status *s, struct gui_buffer *b) s->memory = b->memory.ptr; s->calls = b->calls; } + +void* +gui_buffer_memory(struct gui_buffer *buffer) +{ + GUI_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} + +gui_size +gui_buffer_total(struct gui_buffer *buffer) +{ + GUI_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.size; +} /* * ============================================================== * @@ -959,24 +984,6 @@ gui_command_buffer_push_line(struct gui_command_buffer *b, gui_float x0, gui_flo b->stats.lines++; } -void -gui_command_buffer_push_quad(struct gui_command_buffer *b, gui_float ax, gui_float ay, - gui_float ctrlx, gui_float ctrly, gui_float bx, gui_float by, struct gui_color col) -{ - struct gui_command_quad *cmd; - cmd = (struct gui_command_quad*) - gui_command_buffer_push(b, GUI_COMMAND_QUAD, sizeof(*cmd)); - if (!cmd) return; - cmd->begin.x = (gui_short)ax; - cmd->begin.y = (gui_short)ay; - cmd->end.x = (gui_short)bx; - cmd->end.y = (gui_short)by; - cmd->ctrl.x = (gui_short)ctrlx; - cmd->ctrl.y = (gui_short)ctrly; - cmd->color = col; - b->stats.lines++; -} - void gui_command_buffer_push_curve(struct gui_command_buffer *b, gui_float ax, gui_float ay, gui_float ctrl0x, gui_float ctrl0y, gui_float ctrl1x, gui_float ctrl1y, @@ -991,12 +998,12 @@ gui_command_buffer_push_curve(struct gui_command_buffer *b, gui_float ax, gui_fl if (!cmd) return; cmd->begin.x = (gui_short)ax; cmd->begin.y = (gui_short)ay; - cmd->end.x = (gui_short)bx; - cmd->end.y = (gui_short)by; cmd->ctrl[0].x = (gui_short)ctrl0x; cmd->ctrl[0].y = (gui_short)ctrl0y; cmd->ctrl[1].x = (gui_short)ctrl1x; cmd->ctrl[1].y = (gui_short)ctrl1y; + cmd->end.x = (gui_short)bx; + cmd->end.y = (gui_short)by; cmd->color = col; b->stats.lines++; } @@ -1104,7 +1111,7 @@ gui_command_buffer_push_image(struct gui_command_buffer *b, struct gui_rect r, void gui_command_buffer_push_text(struct gui_command_buffer *b, struct gui_rect r, - const gui_char *string, gui_size length, const struct gui_font *font, + const gui_char *string, gui_size length, const struct gui_user_font *font, struct gui_color bg, struct gui_color fg) { struct gui_command_text *cmd; @@ -1126,7 +1133,7 @@ gui_command_buffer_push_text(struct gui_command_buffer *b, struct gui_rect r, cmd->h = (gui_ushort)r.h; cmd->background = bg; cmd->foreground = fg; - cmd->font = font->userdata; + cmd->font = font; cmd->length = length; gui_memcopy(cmd->string, string, length); cmd->string[length] = '\0'; @@ -1350,6 +1357,7 @@ gui_command_queue_finish(struct gui_command_queue *queue, struct gui_command_buffer *buffer) { void *memory; + gui_size size; gui_size i = 0; struct gui_command_sub_buffer *iter; struct gui_command_sub_buffer_stack *stack; @@ -1362,10 +1370,11 @@ gui_command_queue_finish(struct gui_command_queue *queue, buffer->end = queue->buffer.allocated; if (!stack->count) return; - /* fix buffer command list for subbuffers */ memory = queue->buffer.memory.ptr; - iter = gui_ptr_add(struct gui_command_sub_buffer, memory, (queue->buffer.memory.size - stack->begin)); + size = queue->buffer.memory.size; + iter = gui_ptr_add(struct gui_command_sub_buffer, memory, (size - stack->begin)); + /* fix buffer command list for subbuffers */ for (i = 0; i < stack->count; ++i) { struct gui_command *parent_last, *sublast, *last; parent_last = gui_ptr_add(struct gui_command, memory, iter->parent_last); @@ -1379,7 +1388,7 @@ gui_command_queue_finish(struct gui_command_queue *queue, last->next = iter->begin; buffer->last = iter->last; buffer->end = iter->end; - iter = gui_ptr_add(struct gui_command_sub_buffer, memory, queue->buffer.memory.size - iter->next); + iter = gui_ptr_add(struct gui_command_sub_buffer, memory, size - iter->next); } queue->stack.count = 0; gui_buffer_reset(&queue->buffer, GUI_BUFFER_BACK); @@ -1462,6 +1471,988 @@ gui_command_queue_next(struct gui_command_queue *queue, const struct gui_command next = gui_ptr_add_const(struct gui_command, buffer, cmd->next); return next; } +/* ============================================================== + * + * Draw List + * + * ===============================================================*/ +#if GUI_COMPILE_WITH_VERTEX_BUFFER +void +gui_draw_list_init(struct gui_draw_list *list, struct gui_buffer *cmds, + struct gui_buffer *vertexes, struct gui_buffer *elements, + gui_sin_f sine, gui_cos_f cosine, struct gui_draw_null_texture null, + enum gui_anti_aliasing AA) +{ + gui_zero(list, sizeof(*list)); + list->sin = sine; + list->cos = cosine; + list->null = null; + list->clip_rect = gui_null_rect; + list->vertexes = vertexes; + list->elements = elements; + list->buffer = cmds; + list->AA = AA; +} + +void +gui_draw_list_load(struct gui_draw_list *list, struct gui_command_queue *queue, + gui_float line_thickness, gui_uint curve_segments) +{ + const struct gui_command *cmd; + GUI_ASSERT(list); + GUI_ASSERT(list->vertexes); + GUI_ASSERT(list->elements); + GUI_ASSERT(queue); + line_thickness = MAX(line_thickness, 1.0f); + if (!list || !queue || !list->vertexes || !list->elements) return; + + gui_foreach_command(cmd, queue) { + switch (cmd->type) { + case GUI_COMMAND_NOP: break; + case GUI_COMMAND_SCISSOR: { + const struct gui_command_scissor *s = gui_command(scissor, cmd); + gui_draw_list_add_clip(list, gui_rect(s->x, s->y, s->w, s->h)); + } break; + case GUI_COMMAND_LINE: { + const struct gui_command_line *l = gui_command(line, cmd); + gui_draw_list_add_line(list, gui_vec2(l->begin.x, l->begin.y), + gui_vec2(l->end.x, l->end.y), l->color, line_thickness); + } break; + case GUI_COMMAND_CURVE: { + const struct gui_command_curve *q = gui_command(curve, cmd); + gui_draw_list_add_curve(list, gui_vec2(q->begin.x, q->begin.y), + gui_vec2(q->ctrl[0].x, q->ctrl[0].y), gui_vec2(q->ctrl[1].x, q->ctrl[1].y), + gui_vec2(q->end.x, q->end.y), q->color, curve_segments, line_thickness); + } break; + case GUI_COMMAND_RECT: { + const struct gui_command_rect *r = gui_command(rect, cmd); + gui_draw_list_add_rect_filled(list, gui_rect(r->x, r->y, r->w, r->h), + r->color, (gui_float)r->rounding); + } break; + case GUI_COMMAND_CIRCLE: { + const struct gui_command_circle *c = gui_command(circle, cmd); + gui_draw_list_add_circle_filled(list, gui_vec2((gui_float)c->x + (gui_float)c->w/2, + (gui_float)c->y + c->h/2), c->w/2, c->color, curve_segments); + } break; + case GUI_COMMAND_TRIANGLE: { + const struct gui_command_triangle *t = gui_command(triangle, cmd); + gui_draw_list_add_triangle_filled(list, gui_vec2(t->a.x, t->a.y), + gui_vec2(t->b.x, t->b.y), gui_vec2(t->c.x, t->c.y), t->color); + } break; + case GUI_COMMAND_TEXT: { + const struct gui_command_text *t = gui_command(text, cmd); + gui_draw_list_add_text(list, t->font, gui_rect(t->x, t->y, t->w, t->h), + t->string, t->length, t->background, t->foreground); + } break; + case GUI_COMMAND_IMAGE: { + const struct gui_command_image *i = gui_command(image, cmd); + gui_draw_list_add_image(list, i->img, gui_rect(i->x, i->y, i->w, i->h), + gui_rgb(255, 255, 255)); + } break; + case GUI_COMMAND_MAX: + default: break; + } + } +} + +void +gui_draw_list_clear(struct gui_draw_list *list) +{ + GUI_ASSERT(list); + if (!list) return; + if (list->buffer) + gui_buffer_clear(list->buffer); + if (list->elements) + gui_buffer_clear(list->vertexes); + if (list->vertexes) + gui_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->vertexes = 0; + list->elements = 0; + list->clip_rect = gui_null_rect; +} + +gui_bool +gui_draw_list_is_empty(struct gui_draw_list *list) +{ + GUI_ASSERT(list); + if (!list) return gui_true; + return (list->cmd_count == 0); +} + +const struct gui_draw_command* +gui_draw_list_begin(const struct gui_draw_list *list) +{ + gui_byte *memory; + gui_size offset; + const struct gui_draw_command *cmd; + GUI_ASSERT(list); + GUI_ASSERT(list->buffer); + if (!list || !list->buffer || !list->cmd_count) return 0; + memory = (gui_byte*)list->buffer->memory.ptr; + offset = list->buffer->memory.size - list->cmd_offset; + cmd = gui_ptr_add(const struct gui_draw_command, memory, offset); + return cmd; +} + +const struct gui_draw_command* +gui_draw_list_next(const struct gui_draw_list *list, const struct gui_draw_command *cmd) +{ + gui_byte *memory; + gui_size offset; + const struct gui_draw_command *end; + GUI_ASSERT(list); + if (!list || !list->buffer || !list->cmd_count) return 0; + memory = (gui_byte*)list->buffer->memory.ptr; + offset = list->buffer->memory.size - list->cmd_offset; + end = gui_ptr_add(const struct gui_draw_command, memory, offset); + end -= (list->cmd_count-1); + if (cmd <= end) return 0; + return (cmd - 1); +} + +static struct gui_vec2* +gui_draw_list_alloc_path(struct gui_draw_list *list, gui_size count) +{ + void *memory; + struct gui_vec2 *points; + static const gui_size point_align = GUI_ALIGNOF(struct gui_vec2); + static const gui_size point_size = sizeof(struct gui_vec2); + points = (struct gui_vec2*) + gui_buffer_alloc(list->buffer, GUI_BUFFER_FRONT, point_size * count, point_align); + if (!points) return 0; + if (!list->path_offset) { + memory = gui_buffer_memory(list->buffer); + list->path_offset = (gui_uint)((gui_byte*)points - (gui_byte*)memory); + } + list->path_count += (gui_uint)count; + return points; +} + +static struct gui_vec2 +gui_draw_list_path_last(struct gui_draw_list *list) +{ + void *memory; + struct gui_vec2 *point; + GUI_ASSERT(list->path_count); + memory = gui_buffer_memory(list->buffer); + point = gui_ptr_add(struct gui_vec2, memory, list->path_offset); + point += (list->path_count-1); + return *point; +} + +static struct gui_draw_command* +gui_draw_list_push_command(struct gui_draw_list *list, struct gui_rect clip, + gui_handle texture) +{ + static const gui_size cmd_align = GUI_ALIGNOF(struct gui_draw_command); + static const gui_size cmd_size = sizeof(struct gui_draw_command); + struct gui_draw_command *cmd; + + GUI_ASSERT(list); + cmd = (struct gui_draw_command*) + gui_buffer_alloc(list->buffer, GUI_BUFFER_BACK, cmd_size, cmd_align); + if (!cmd) return 0; + if (!list->cmd_count) { + gui_byte *memory = (gui_byte*)gui_buffer_memory(list->buffer); + gui_size total = gui_buffer_total(list->buffer); + memory = gui_ptr_add(gui_byte, memory, total); + list->cmd_offset = (gui_size)(memory - (gui_byte*)cmd); + } + + cmd->elem_count = 0; + cmd->clip_rect = clip; + cmd->texture = texture; + list->cmd_count++; + list->clip_rect = clip; + return cmd; +} + +static struct gui_draw_command* +gui_draw_list_command_last(struct gui_draw_list *list) +{ + void *memory; + gui_size size; + struct gui_draw_command *cmd; + GUI_ASSERT(list->cmd_count); + memory = gui_buffer_memory(list->buffer); + size = gui_buffer_total(list->buffer); + cmd = gui_ptr_add(struct gui_draw_command, memory, size - list->cmd_offset); + return (cmd - (list->cmd_count-1)); +} + +void +gui_draw_list_add_clip(struct gui_draw_list *list, struct gui_rect rect) +{ + GUI_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + gui_draw_list_push_command(list, rect, list->null.texture); + } else { + struct gui_draw_command *prev = gui_draw_list_command_last(list); + gui_draw_list_push_command(list, rect, prev->texture); + } +} + +static void +gui_draw_list_push_image(struct gui_draw_list *list, gui_handle texture) +{ + GUI_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + gui_draw_list_push_command(list, gui_null_rect, list->null.texture); + } else { + struct gui_draw_command *prev = gui_draw_list_command_last(list); + gui_draw_list_push_command(list, prev->clip_rect, texture); + } +} + +static struct gui_draw_vertex* +gui_draw_list_alloc_vertexes(struct gui_draw_list *list, gui_size count) +{ + struct gui_draw_vertex *vtx; + static const gui_size vtx_align = GUI_ALIGNOF(struct gui_draw_vertex); + static const gui_size vtx_size = sizeof(struct gui_draw_vertex); + GUI_ASSERT(list); + if (!list) return 0; + vtx = (struct gui_draw_vertex*) + gui_buffer_alloc(list->vertexes, GUI_BUFFER_FRONT, vtx_size * count, vtx_align); + if (!vtx) return 0; + list->vertex_count += (gui_uint)count; + return vtx; +} + +static gui_draw_index* +gui_draw_list_alloc_elements(struct gui_draw_list *list, gui_size count) +{ + gui_draw_index *ids; + struct gui_draw_command *cmd; + static const gui_size elem_align = GUI_ALIGNOF(gui_draw_index); + static const gui_size elem_size = sizeof(gui_draw_index); + GUI_ASSERT(list); + if (!list) return 0; + ids = (gui_draw_index*) + gui_buffer_alloc(list->elements, GUI_BUFFER_FRONT, elem_size * count, elem_align); + if (!ids) return 0; + cmd = gui_draw_list_command_last(list); + list->element_count += (gui_uint)count; + cmd->elem_count += (gui_uint)count; + return ids; +} + +static gui_draw_vertex_color +gui_color32(struct gui_color in) +{ + gui_draw_vertex_color out = (gui_draw_vertex_color)in.r; + out |= ((gui_draw_vertex_color)in.g << 8); + out |= ((gui_draw_vertex_color)in.b << 16); + out |= ((gui_draw_vertex_color)in.a << 24); + return out; +} + +static struct gui_draw_vertex +gui_draw_vertex(struct gui_vec2 pos, struct gui_vec2 uv, gui_draw_vertex_color col) +{ + struct gui_draw_vertex out; + out.position = pos; + out.uv = uv; + out.col = col; + return out; +} + +static void +gui_draw_list_add_poly_line(struct gui_draw_list *list, struct gui_vec2 *points, + const gui_uint points_count, struct gui_color color, gui_bool closed, + gui_float thickness, enum gui_anti_aliasing aliasing) +{ + gui_size count; + gui_bool thick_line; + gui_draw_vertex_color col = gui_color32(color); + GUI_ASSERT(list); + if (!list) return; + if (!list || points_count < 2) return; + + count = points_count; + if (!closed) count = points_count-1; + thick_line = thickness > 1.0f; + + if (aliasing == GUI_ANTI_ALIASING_ON) { + /* ANTI-ALIASED STROKE */ + const gui_float AA_SIZE = 1.0f; + static const gui_size pnt_align = GUI_ALIGNOF(struct gui_vec2); + static const gui_size pnt_size = sizeof(struct gui_vec2); + const gui_draw_vertex_color col_trans = col & 0x00ffffff; + + /* allocate vertexes and elements */ + gui_size i1 = 0; + gui_size index = list->vertex_count; + const gui_size idx_count = (thick_line) ? (count * 18) : (count * 12); + const gui_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3); + struct gui_draw_vertex *vtx = gui_draw_list_alloc_vertexes(list, vtx_count); + gui_draw_index *ids = gui_draw_list_alloc_elements(list, idx_count); + + struct gui_vec2 *normals, *temp; + gui_size size; + if (!vtx || !ids) return; + + /* temporary allocate normals + points */ + gui_buffer_mark(list->vertexes, GUI_BUFFER_FRONT); + size = pnt_size * ((thick_line) ? 5 : 3) * points_count; + normals = (struct gui_vec2*) + gui_buffer_alloc(list->vertexes, GUI_BUFFER_FRONT, size, pnt_align); + if (!normals) return; + temp = normals + points_count; + + /* calculate normals */ + for (i1 = 0; i1 < count; ++i1) { + const gui_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + struct gui_vec2 diff = gui_vec2_sub(points[i2], points[i1]); + gui_float len; + + /* vec2 inverted lenth */ + len = gui_vec2_len_sqr(diff); + if (len != 0.0f) + len = gui_inv_sqrt(len); + else len = 1.0f; + + diff = gui_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) { + gui_size idx1, i; + if (!closed) { + struct gui_vec2 d; + temp[0] = gui_vec2_add(points[0], gui_vec2_muls(normals[0], AA_SIZE)); + temp[1] = gui_vec2_sub(points[0], gui_vec2_muls(normals[0], AA_SIZE)); + d = gui_vec2_muls(normals[points_count-1], AA_SIZE); + temp[(points_count-1) * 2 + 0] = gui_vec2_add(points[points_count-1], d); + temp[(points_count-1) * 2 + 1] = gui_vec2_sub(points[points_count-1], d); + } + + /* fill elements */ + idx1 = index; + for (i1 = 0; i1 < count; i1++) { + struct gui_vec2 dm; + gui_float dmr2; + gui_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + gui_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3); + + /* average normals */ + dm = gui_vec2_muls(gui_vec2_add(normals[i1], normals[i2]), 0.5f); + dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + gui_float scale = 1.0f/dmr2; + scale = MIN(100.0f, scale); + dm = gui_vec2_muls(dm, scale); + } + + dm = gui_vec2_muls(dm, AA_SIZE); + temp[i2*2+0] = gui_vec2_add(points[i2], dm); + temp[i2*2+1] = gui_vec2_sub(points[i2], dm); + + ids[0] = (gui_draw_index)(idx2 + 0); ids[1] = (gui_draw_index)(idx1+0); + ids[2] = (gui_draw_index)(idx1 + 2); ids[3] = (gui_draw_index)(idx1+2); + ids[4] = (gui_draw_index)(idx2 + 2); ids[5] = (gui_draw_index)(idx2+0); + ids[6] = (gui_draw_index)(idx2 + 1); ids[7] = (gui_draw_index)(idx1+1); + ids[8] = (gui_draw_index)(idx1 + 0); ids[9] = (gui_draw_index)(idx1+0); + ids[10]= (gui_draw_index)(idx2 + 0); ids[11]= (gui_draw_index)(idx2+1); + ids += 12; + idx1 = idx2; + } + + /* fill vertexes */ + for (i = 0; i < points_count; ++i) { + const struct gui_vec2 uv = list->null.uv; + vtx[0] = gui_draw_vertex(points[i], uv, col); + vtx[1] = gui_draw_vertex(temp[i*2+0], uv, col_trans); + vtx[2] = gui_draw_vertex(temp[i*2+1], uv, col_trans); + vtx += 3; + } + } else { + gui_size idx1, i; + const gui_float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + if (!closed) { + struct gui_vec2 d1, d2; + d1 = gui_vec2_muls(normals[0], half_inner_thickness + AA_SIZE); + d2 = gui_vec2_muls(normals[0], half_inner_thickness); + + temp[0] = gui_vec2_add(points[0], d1); + temp[1] = gui_vec2_add(points[0], d2); + temp[2] = gui_vec2_sub(points[0], d2); + temp[3] = gui_vec2_sub(points[0], d1); + + d1 = gui_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE); + d2 = gui_vec2_muls(normals[points_count-1], half_inner_thickness); + + temp[(points_count-1)*4+0] = gui_vec2_add(points[points_count-1], d1); + temp[(points_count-1)*4+1] = gui_vec2_add(points[points_count-1], d2); + temp[(points_count-1)*4+2] = gui_vec2_sub(points[points_count-1], d2); + temp[(points_count-1)*4+3] = gui_vec2_sub(points[points_count-1], d1); + } + + /* add all elements */ + idx1 = index; + for (i1 = 0; i1 < count; ++i1) { + gui_float dmr2; + struct gui_vec2 dm_out, dm_in, dm; + const gui_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1); + gui_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4); + + /* average normals */ + dm = gui_vec2_muls(gui_vec2_add(normals[i1], normals[i2]), 0.5f); + dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + gui_float scale = 1.0f/dmr2; + scale = MIN(100.0f, scale); + dm = gui_vec2_muls(dm, scale); + } + + dm_out = gui_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE)); + dm_in = gui_vec2_muls(dm, half_inner_thickness); + temp[i2*4+0] = gui_vec2_add(points[i2], dm_out); + temp[i2*4+1] = gui_vec2_add(points[i2], dm_in); + temp[i2*4+2] = gui_vec2_sub(points[i2], dm_in); + temp[i2*4+3] = gui_vec2_sub(points[i2], dm_out); + + /* add indexes */ + ids[0] = (gui_draw_index)(idx2 + 1); ids[1] = (gui_draw_index)(idx1+1); + ids[2] = (gui_draw_index)(idx1 + 2); ids[3] = (gui_draw_index)(idx1+2); + ids[4] = (gui_draw_index)(idx2 + 2); ids[5] = (gui_draw_index)(idx2+1); + ids[6] = (gui_draw_index)(idx2 + 1); ids[7] = (gui_draw_index)(idx1+1); + ids[8] = (gui_draw_index)(idx1 + 0); ids[9] = (gui_draw_index)(idx1+0); + ids[10]= (gui_draw_index)(idx2 + 0); ids[11] = (gui_draw_index)(idx2+1); + ids[12]= (gui_draw_index)(idx2 + 2); ids[13] = (gui_draw_index)(idx1+2); + ids[14]= (gui_draw_index)(idx1 + 3); ids[15] = (gui_draw_index)(idx1+3); + ids[16]= (gui_draw_index)(idx2 + 3); ids[17] = (gui_draw_index)(idx2+2); + ids += 18; + idx1 = idx2; + } + + /* add vertexes */ + for (i = 0; i < points_count; ++i) { + const struct gui_vec2 uv = list->null.uv; + vtx[0] = gui_draw_vertex(temp[i*4+0], uv, col_trans); + vtx[1] = gui_draw_vertex(temp[i*4+1], uv, col); + vtx[2] = gui_draw_vertex(temp[i*4+2], uv, col); + vtx[3] = gui_draw_vertex(temp[i*4+3], uv, col_trans); + vtx += 4; + } + } + + /* free temporary normals + points */ + gui_buffer_reset(list->vertexes, GUI_BUFFER_FRONT); + } else { + /* NON ANTI-ALIASED STROKE */ + gui_size i1 = 0; + gui_size idx = list->vertex_count; + const gui_size idx_count = count * 6; + const gui_size vtx_count = count * 4; + struct gui_draw_vertex *vtx = gui_draw_list_alloc_vertexes(list, vtx_count); + gui_draw_index *ids = gui_draw_list_alloc_elements(list, idx_count); + if (!vtx || !ids) return; + + for (i1 = 0; i1 < count; ++i1) { + gui_float dx, dy; + const struct gui_vec2 uv = list->null.uv; + const gui_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; + const struct gui_vec2 p1 = points[i1]; + const struct gui_vec2 p2 = points[i2]; + struct gui_vec2 diff = gui_vec2_sub(p2, p1); + gui_float len; + + /* vec2 inverted lenth */ + len = gui_vec2_len_sqr(diff); + if (len != 0.0f) + len = gui_inv_sqrt(len); + else len = 1.0f; + diff = gui_vec2_muls(diff, len); + + /* add vertexes */ + dx = diff.x * (thickness * 0.5f); + dy = diff.y * (thickness * 0.5f); + + vtx[0] = gui_draw_vertex(gui_vec2(p1.x + dy, p1.y - dx), uv, col); + vtx[1] = gui_draw_vertex(gui_vec2(p2.x + dy, p2.y - dx), uv, col); + vtx[2] = gui_draw_vertex(gui_vec2(p2.x - dy, p2.y + dx), uv, col); + vtx[3] = gui_draw_vertex(gui_vec2(p1.x - dy, p1.y + dx), uv, col); + vtx += 4; + + ids[0] = (gui_draw_index)(idx+0); ids[1] = (gui_draw_index)(idx+1); + ids[2] = (gui_draw_index)(idx+2); ids[3] = (gui_draw_index)(idx+0); + ids[4] = (gui_draw_index)(idx+2); ids[5] = (gui_draw_index)(idx+3); + ids += 6; + idx += 4; + } + } +} + +static void +gui_draw_list_add_poly_convex(struct gui_draw_list *list, struct gui_vec2 *points, + const gui_uint points_count, struct gui_color color, enum gui_anti_aliasing aliasing) +{ + static const gui_size pnt_align = GUI_ALIGNOF(struct gui_vec2); + static const gui_size pnt_size = sizeof(struct gui_vec2); + gui_draw_vertex_color col = gui_color32(color); + GUI_ASSERT(list); + if (!list || points_count < 3) return; + + if (aliasing == GUI_ANTI_ALIASING_ON) { + gui_size i = 0; + gui_size i0 = 0, i1 = 0; + + const gui_float AA_SIZE = 1.0f; + const gui_draw_vertex_color col_trans = col & 0x00ffffff; + gui_size index = list->vertex_count; + const gui_size idx_count = (points_count-2)*3 + points_count*6; + const gui_size vtx_count = (points_count*2); + struct gui_draw_vertex *vtx = gui_draw_list_alloc_vertexes(list, vtx_count); + gui_draw_index *ids = gui_draw_list_alloc_elements(list, idx_count); + + gui_uint vtx_inner_idx = (gui_uint)(index + 0); + gui_uint vtx_outer_idx = (gui_uint)(index + 1); + struct gui_vec2 *normals; + gui_size size; + if (!vtx || !ids) return; + + /* temporary allocate normals */ + gui_buffer_mark(list->vertexes, GUI_BUFFER_FRONT); + size = pnt_size * points_count; + normals = (struct gui_vec2*) + gui_buffer_alloc(list->vertexes, GUI_BUFFER_FRONT, size, pnt_align); + if (!normals) return; + + /* add elements */ + for (i = 2; i < points_count; i++) { + ids[0] = (gui_draw_index)(vtx_inner_idx); + ids[1] = (gui_draw_index)(vtx_inner_idx + ((i-1) << 1)); + ids[2] = (gui_draw_index)(vtx_inner_idx + (i << 1)); + ids += 3; + } + + /* compute normals */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + struct gui_vec2 p0 = points[i0]; + struct gui_vec2 p1 = points[i1]; + struct gui_vec2 diff = gui_vec2_sub(p1, p0); + gui_float len; + + /* vec2 inverted lenth */ + len = gui_vec2_len_sqr(diff); + if (len != 0.0f) + len = gui_inv_sqrt(len); + else len = 1.0f; + diff = gui_vec2_muls(diff, len); + + normals[i0].x = diff.y; + normals[i0].y = -diff.x; + } + + /* add vertexes + indexes */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + const struct gui_vec2 uv = list->null.uv; + struct gui_vec2 n0 = normals[i0]; + struct gui_vec2 n1 = normals[i1]; + struct gui_vec2 dm = gui_vec2_muls(gui_vec2_add(n0, n1), 0.5f); + + gui_float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) { + gui_float scale = 1.0f / dmr2; + scale = MIN(scale, 100.0f); + dm = gui_vec2_muls(dm, scale); + } + dm = gui_vec2_muls(dm, AA_SIZE * 0.5f); + + /* add vertexes */ + vtx[0] = gui_draw_vertex(gui_vec2_sub(points[i1], dm), uv, col); + vtx[1] = gui_draw_vertex(gui_vec2_add(points[i1], dm), uv, col_trans); + vtx += 2; + + /* add indexes */ + ids[0] = (gui_draw_index)(vtx_inner_idx+(i1<<1)); + ids[1] = (gui_draw_index)(vtx_inner_idx+(i0<<1)); + ids[2] = (gui_draw_index)(vtx_outer_idx+(i0<<1)); + ids[3] = (gui_draw_index)(vtx_outer_idx+(i0<<1)); + ids[4] = (gui_draw_index)(vtx_outer_idx+(i1<<1)); + ids[5] = (gui_draw_index)(vtx_inner_idx+(i1<<1)); + ids += 6; + } + /* free temporary normals + points */ + gui_buffer_reset(list->vertexes, GUI_BUFFER_FRONT); + } else { + gui_size i; + gui_size index = list->vertex_count; + const gui_size idx_count = (points_count-2)*3; + const gui_size vtx_count = points_count; + struct gui_draw_vertex *vtx = gui_draw_list_alloc_vertexes(list, vtx_count); + gui_draw_index *ids = gui_draw_list_alloc_elements(list, idx_count); + if (!vtx || !ids) return; + for (i = 0; i < vtx_count; ++i) { + vtx[0] = gui_draw_vertex(points[i], list->null.uv, col); + vtx++; + } + for (i = 2; i < points_count; ++i) { + ids[0] = (gui_draw_index)index; + ids[1] = (gui_draw_index)(index+ i - 1); + ids[2] = (gui_draw_index)(index+i); + ids += 3; + } + } +} + +void +gui_draw_list_add_line(struct gui_draw_list *list, struct gui_vec2 a, + struct gui_vec2 b, struct gui_color col, gui_float thickness) +{ + GUI_ASSERT(list); + if (!list || !col.a) return; + gui_draw_list_path_line_to(list, gui_vec2_add(a, gui_vec2(0.5f, 0.5f))); + gui_draw_list_path_line_to(list, gui_vec2_add(b, gui_vec2(0.5f, 0.5f))); + gui_draw_list_path_stroke(list, col, GUI_STROKE_OPEN, thickness); +} + +void +gui_draw_list_add_rect(struct gui_draw_list *list, struct gui_rect rect, + struct gui_color col, gui_float rounding, gui_float line_thickness) +{ + GUI_ASSERT(list); + if (!list || !col.a) return; + gui_draw_list_path_rect_to(list, gui_vec2(rect.x + 0.5f, rect.y + 0.5f), + gui_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); + gui_draw_list_path_stroke(list, col, GUI_STROKE_CLOSED, line_thickness); +} + +void +gui_draw_list_add_rect_filled(struct gui_draw_list *list, struct gui_rect rect, + struct gui_color col, gui_float rounding) +{ + GUI_ASSERT(list); + if (!list || !col.a) return; + gui_draw_list_path_rect_to(list, gui_vec2(rect.x + 0.5f, rect.y + 0.5f), + gui_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); + gui_draw_list_path_fill(list, col); +} + +void +gui_draw_list_add_triangle(struct gui_draw_list *list, struct gui_vec2 a, struct gui_vec2 b, + struct gui_vec2 c, struct gui_color col, gui_float line_thickness) +{ + GUI_ASSERT(list); + if (!list) return; + if (!col.a) return; + gui_draw_list_path_line_to(list, a); + gui_draw_list_path_line_to(list, b); + gui_draw_list_path_line_to(list, c); + gui_draw_list_path_stroke(list, col, GUI_STROKE_CLOSED, line_thickness); +} + +void +gui_draw_list_add_triangle_filled(struct gui_draw_list *list, struct gui_vec2 a, + struct gui_vec2 b, struct gui_vec2 c, struct gui_color col) +{ + GUI_ASSERT(list); + if (!list || !col.a) return; + gui_draw_list_path_line_to(list, a); + gui_draw_list_path_line_to(list, b); + gui_draw_list_path_line_to(list, c); + gui_draw_list_path_fill(list, col); +} + +void +gui_draw_list_add_circle(struct gui_draw_list *list, struct gui_vec2 center, + gui_float radius, struct gui_color col, gui_uint segs, gui_float line_thickness) +{ + gui_float a_max; + GUI_ASSERT(list); + if (!list || !col.a) return; + a_max = GUI_PI * 2.0f * ((gui_float)segs - 1.0f) / (gui_float)segs; + gui_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + gui_draw_list_path_stroke(list, col, GUI_STROKE_CLOSED, line_thickness); +} + +void +gui_draw_list_add_circle_filled(struct gui_draw_list *list, struct gui_vec2 center, + gui_float radius, struct gui_color col, gui_uint segs) +{ + gui_float a_max; + GUI_ASSERT(list); + if (!list || !col.a) return; + a_max = GUI_PI * 2.0f * ((gui_float)segs - 1.0f) / (gui_float)segs; + gui_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + gui_draw_list_path_fill(list, col); +} + +void +gui_draw_list_add_curve(struct gui_draw_list *list, struct gui_vec2 p0, + struct gui_vec2 cp0, struct gui_vec2 cp1, struct gui_vec2 p1, + struct gui_color col, gui_uint segments, gui_float thickness) +{ + GUI_ASSERT(list); + if (!list || !col.a) return; + gui_draw_list_path_line_to(list, p0); + gui_draw_list_path_curve_to(list, cp0, cp1, p1, segments); + gui_draw_list_path_stroke(list, col, GUI_STROKE_OPEN, thickness); +} + +static void +gui_draw_list_push_rect_uv(struct gui_draw_list *list, struct gui_vec2 a, + struct gui_vec2 c, struct gui_vec2 uva, struct gui_vec2 uvc, struct gui_color color) +{ + gui_draw_vertex_color col = gui_color32(color); + struct gui_draw_vertex *vtx; + struct gui_vec2 uvb, uvd; + struct gui_vec2 b,d; + gui_draw_index *idx; + gui_draw_index index; + GUI_ASSERT(list); + if (!list) return; + + uvb = gui_vec2(uvc.x, uva.y); + uvd = gui_vec2(uva.x, uvc.y); + b = gui_vec2(c.x, a.y); + d = gui_vec2(a.x, c.y); + + index = (gui_draw_index)list->vertex_count; + vtx = gui_draw_list_alloc_vertexes(list, 4); + idx = gui_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (gui_draw_index)(index+0); idx[1] = (gui_draw_index)(index+1); + idx[2] = (gui_draw_index)(index+2); idx[3] = (gui_draw_index)(index+0); + idx[4] = (gui_draw_index)(index+2); idx[5] = (gui_draw_index)(index+3); + + vtx[0] = gui_draw_vertex(a, uva, col); + vtx[1] = gui_draw_vertex(b, uvb, col); + vtx[2] = gui_draw_vertex(c, uvc, col); + vtx[3] = gui_draw_vertex(d, uvd, col); +} + +void +gui_draw_list_add_image(struct gui_draw_list *list, struct gui_image texture, + struct gui_rect rect, struct gui_color color) +{ + GUI_ASSERT(list); + if (!list) return; + /* push new command with given texture */ + gui_draw_list_push_image(list, texture.handle); + if (gui_image_is_subimage(&texture)) { + /* add region inside of the texture */ + struct gui_vec2 uv[2]; + uv[0].x = (gui_float)texture.region[0]/(gui_float)texture.w; + uv[0].y = (gui_float)texture.region[1]/(gui_float)texture.h; + uv[1].x = (gui_float)(texture.region[0] + texture.region[2])/(gui_float)texture.w; + uv[1].y = (gui_float)(texture.region[1] + texture.region[3])/(gui_float)texture.h; + gui_draw_list_push_rect_uv(list, gui_vec2(rect.x, rect.y), + gui_vec2(rect.x + rect.w, rect.y + rect.h), uv[0], uv[1], color); + } else gui_draw_list_push_rect_uv(list, gui_vec2(rect.x, rect.y), + gui_vec2(rect.x + rect.w, rect.y + rect.h), + gui_vec2(0.0f, 0.0f), gui_vec2(1.0f, 1.0f),color); +} + +void +gui_draw_list_add_text(struct gui_draw_list *list, const struct gui_user_font *font, + struct gui_rect rect, const gui_char *text, gui_size len, + struct gui_color bg, struct gui_color fg) +{ + gui_float x; + gui_size text_len; + gui_long unicode, next; + gui_size glyph_len, next_glyph_len; + struct gui_user_font_glyph g; + GUI_ASSERT(list); + if (!list || !len || !text) return; + + /* draw text background */ + gui_draw_list_add_rect_filled(list, rect, bg, 0.0f); + gui_draw_list_push_image(list, font->texture); + + /* draw every glyph image */ + x = rect.x; + glyph_len = text_len = gui_utf_decode(text, &unicode, len); + if (!glyph_len) return; + while (text_len <= len && glyph_len) { + gui_float gx, gy, gh, gw; + gui_float char_width = 0; + if (unicode == GUI_UTF_INVALID) break; + + /* query currently drawn glyph information */ + next_glyph_len = gui_utf_decode(text + text_len, &next, len - text_len); + font->query(font->userdata, &g, unicode, (next == GUI_UTF_INVALID) ? '\0' : next); + + /* calculate and draw glyph drawing rectangle and image */ + gx = x + g.offset.x; + gy = rect.y + (font->height/2) + (rect.h/2) - g.offset.y; + gw = g.width; gh = g.height; + char_width = g.xadvance; + gui_draw_list_push_rect_uv(list, gui_vec2(gx,gy), gui_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 +gui_draw_list_path_clear(struct gui_draw_list *list) +{ + GUI_ASSERT(list); + if (!list) return; + gui_buffer_reset(list->buffer, GUI_BUFFER_FRONT); + list->path_count = 0; + list->path_offset = 0; +} + +void +gui_draw_list_path_line_to(struct gui_draw_list *list, struct gui_vec2 pos) +{ + struct gui_vec2 *points; + struct gui_draw_command *cmd; + GUI_ASSERT(list); + if (!list) return; + if (!list->cmd_count) + gui_draw_list_add_clip(list, gui_null_rect); + cmd = gui_draw_list_command_last(list); + if (cmd->texture.ptr != list->null.texture.ptr) + gui_draw_list_push_image(list, list->null.texture); + + points = gui_draw_list_alloc_path(list, 1); + if (!points) return; + points[0] = pos; +} + +static void +gui_draw_list_path_arc_to_fast(struct gui_draw_list *list, struct gui_vec2 center, + gui_float radius, gui_int a_min, gui_int a_max) +{ + static struct gui_vec2 circle_vtx[12]; + static gui_bool circle_vtx_builds = gui_false; + static const gui_int circle_vtx_count = GUI_LEN(circle_vtx); + + GUI_ASSERT(list); + if (!list) return; + if (!circle_vtx_builds) { + gui_int i = 0; + for (i = 0; i < circle_vtx_count; ++i) { + const gui_float a = ((gui_float)i / (gui_float)circle_vtx_count) * 2 * GUI_PI; + circle_vtx[i].x = list->cos(a); + circle_vtx[i].y = list->sin(a); + } + circle_vtx_builds = gui_true; + } + + if (a_min <= a_max) { + gui_int a = 0; + for (a = a_min; a <= a_max; a++) { + const struct gui_vec2 c = circle_vtx[a % circle_vtx_count]; + const gui_float x = center.x + c.x * radius; + const gui_float y = center.y + c.y * radius; + gui_draw_list_path_line_to(list, gui_vec2(x, y)); + } + } +} + +void +gui_draw_list_path_arc_to(struct gui_draw_list *list, struct gui_vec2 center, + gui_float radius, gui_float a_min, gui_float a_max, gui_uint segments) +{ + gui_uint i = 0; + GUI_ASSERT(list); + if (!list) return; + if (radius == 0.0f) return; + for (i = 0; i <= segments; ++i) { + const gui_float a = a_min + ((gui_float)i / (gui_float)segments) * (a_max - a_min); + const gui_float x = center.x + list->cos(a) * radius; + const gui_float y = center.y + list->sin(a) * radius; + gui_draw_list_path_line_to(list, gui_vec2(x, y)); + } +} + +void +gui_draw_list_path_rect_to(struct gui_draw_list *list, struct gui_vec2 a, + struct gui_vec2 b, gui_float rounding) +{ + gui_float r; + GUI_ASSERT(list); + if (!list) return; + r = rounding; + r = MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x)); + r = MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y)); + if (r == 0.0f) { + gui_draw_list_path_line_to(list, a); + gui_draw_list_path_line_to(list, gui_vec2(b.x,a.y)); + gui_draw_list_path_line_to(list, b); + gui_draw_list_path_line_to(list, gui_vec2(a.x,b.y)); + } else { + gui_draw_list_path_arc_to_fast(list, gui_vec2(a.x + r, a.y + r), r, 6, 9); + gui_draw_list_path_arc_to_fast(list, gui_vec2(b.x - r, a.y + r), r, 9, 12); + gui_draw_list_path_arc_to_fast(list, gui_vec2(b.x - r, b.y - r), r, 0, 3); + gui_draw_list_path_arc_to_fast(list, gui_vec2(a.x + r, b.y - r), r, 3, 6); + } +} + +void +gui_draw_list_path_curve_to(struct gui_draw_list *list, struct gui_vec2 p2, + struct gui_vec2 p3, struct gui_vec2 p4, gui_uint num_segments) +{ + gui_uint i_step; + gui_float t_step; + struct gui_vec2 p1; + + GUI_ASSERT(list); + GUI_ASSERT(list->path_count); + if (!list || !list->path_count) return; + num_segments = MAX(num_segments, 1); + + p1 = gui_draw_list_path_last(list); + t_step = 1.0f/(gui_float)num_segments; + for (i_step = 1; i_step <= num_segments; ++i_step) { + gui_float t = t_step * (gui_float)i_step; + gui_float u = 1.0f - t; + gui_float w1 = u*u*u; + gui_float w2 = 3*u*u*t; + gui_float w3 = 3*u*t*t; + gui_float w4 = t * t *t; + gui_float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; + gui_float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; + gui_draw_list_path_line_to(list, gui_vec2(x,y)); + } +} + +void +gui_draw_list_path_fill(struct gui_draw_list *list, struct gui_color color) +{ + struct gui_vec2 *points; + GUI_ASSERT(list); + if (!list) return; + points = (struct gui_vec2*)gui_buffer_memory(list->buffer); + gui_draw_list_add_poly_convex(list, points, list->path_count, color, list->AA); + gui_draw_list_path_clear(list); +} + +void +gui_draw_list_path_stroke(struct gui_draw_list *list, struct gui_color color, + gui_bool closed, gui_float thickness) +{ + struct gui_vec2 *points; + GUI_ASSERT(list); + if (!list) return; + points = (struct gui_vec2*)gui_buffer_memory(list->buffer); + gui_draw_list_add_poly_line(list, points, list->path_count, color, + closed, thickness, list->AA); + gui_draw_list_path_clear(list); +} +#endif /* * ============================================================== * @@ -1835,6 +2826,14 @@ gui_edit_box_len(struct gui_edit_box *eb) return eb->glyphes; } +/* + * ============================================================== + * + * Font + * + * =============================================================== + */ + /* * ============================================================== * @@ -1845,7 +2844,7 @@ gui_edit_box_len(struct gui_edit_box *eb) void gui_widget_text(struct gui_command_buffer *o, struct gui_rect b, const char *string, gui_size len, const struct gui_text *t, - enum gui_text_align a, const struct gui_font *f) + enum gui_text_align a, const struct gui_user_font *f) { struct gui_rect label; gui_size text_width; @@ -1918,7 +2917,7 @@ gui_bool gui_widget_button_text(struct gui_command_buffer *o, struct gui_rect r, const char *string, enum gui_button_behavior behavior, const struct gui_button_text *b, const struct gui_input *i, - const struct gui_font *f) + const struct gui_user_font *f) { struct gui_text t; struct gui_rect content; @@ -1947,7 +2946,7 @@ gui_widget_button_text(struct gui_command_buffer *o, struct gui_rect r, static void gui_draw_symbol(struct gui_command_buffer *out, enum gui_symbol symbol, struct gui_rect content, struct gui_color background, struct gui_color foreground, - gui_float border_width, const struct gui_font *font) + gui_float border_width, const struct gui_user_font *font) { switch (symbol) { case GUI_SYMBOL_X: @@ -2004,7 +3003,7 @@ gui_bool gui_widget_button_symbol(struct gui_command_buffer *out, struct gui_rect r, enum gui_symbol symbol, enum gui_button_behavior bh, const struct gui_button_symbol *b, const struct gui_input *in, - const struct gui_font *font) + const struct gui_user_font *font) { gui_bool ret; struct gui_color background; @@ -2050,7 +3049,7 @@ gui_bool gui_widget_button_text_symbol(struct gui_command_buffer *out, struct gui_rect r, enum gui_symbol symbol, const char *text, enum gui_text_align align, enum gui_button_behavior behavior, const struct gui_button_text *button, - const struct gui_font *f, const struct gui_input *i) + const struct gui_user_font *f, const struct gui_input *i) { gui_bool ret; struct gui_rect tri; @@ -2087,7 +3086,7 @@ gui_bool gui_widget_button_text_image(struct gui_command_buffer *out, struct gui_rect r, struct gui_image img, const char* text, enum gui_text_align align, enum gui_button_behavior behavior, const struct gui_button_text *button, - const struct gui_font *f, const struct gui_input *i) + const struct gui_user_font *f, const struct gui_input *i) { gui_bool pressed; struct gui_rect icon; @@ -2111,7 +3110,7 @@ void gui_widget_toggle(struct gui_command_buffer *out, struct gui_rect r, gui_bool *active, const char *string, enum gui_toggle_type type, const struct gui_toggle *toggle, const struct gui_input *in, - const struct gui_font *font) + const struct gui_user_font *font) { gui_bool toggle_active; struct gui_rect select; @@ -2254,7 +3253,6 @@ gui_widget_slider(struct gui_command_buffer *out, struct gui_rect slider, } { - /* NOTE: this is a shitty hack since I am to stupid for math */ struct gui_rect fill; cursor.w = cursor.h; cursor.x = (slider_value <= slider_min) ? cursor.x: @@ -2324,7 +3322,7 @@ gui_widget_progress(struct gui_command_buffer *out, struct gui_rect r, } static gui_size -gui_font_glyph_index_at_pos(const struct gui_font *font, const char *text, +gui_user_font_glyph_index_at_pos(const struct gui_user_font *font, const char *text, gui_size text_len, gui_float xoff) { gui_long unicode; @@ -2346,7 +3344,7 @@ gui_font_glyph_index_at_pos(const struct gui_font *font, const char *text, } static gui_size -gui_font_glyphes_fitting_in_space(const struct gui_font *font, const char *text, +gui_user_font_glyphes_fitting_in_space(const struct gui_user_font *font, const char *text, gui_size text_len, gui_float space, gui_size *glyphes, gui_float *text_width) { gui_size glyph_len; @@ -2378,7 +3376,7 @@ gui_font_glyphes_fitting_in_space(const struct gui_font *font, const char *text, void gui_widget_editbox(struct gui_command_buffer *out, struct gui_rect r, struct gui_edit_box *box, const struct gui_edit *field, - const struct gui_input *in, const struct gui_font *font) + const struct gui_input *in, const struct gui_user_font *font) { gui_char *buffer; gui_size len; @@ -2399,7 +3397,8 @@ gui_widget_editbox(struct gui_command_buffer *out, struct gui_rect r, field->rounding, field->background); /* check if the editbox is activated/deactivated */ - if (in && in->mouse.buttons[GUI_BUTTON_LEFT].clicked && in->mouse.buttons[GUI_BUTTON_LEFT].down) + if (in && in->mouse.buttons[GUI_BUTTON_LEFT].clicked && + in->mouse.buttons[GUI_BUTTON_LEFT].down) box->active = GUI_INBOX(in->mouse.pos.x,in->mouse.pos.y,r.x,r.y,r.w,r.h); /* input handling */ @@ -2493,7 +3492,7 @@ gui_widget_editbox(struct gui_command_buffer *out, struct gui_rect r, while (text_len) { frames++; offset += frame_len; - frame_len = gui_font_glyphes_fitting_in_space(font, + frame_len = gui_user_font_glyphes_fitting_in_space(font, &buffer[offset], text_len, space, &glyphes, &text_width); glyph_off += glyphes; if (glyph_off > box->cursor) @@ -2514,7 +3513,7 @@ gui_widget_editbox(struct gui_command_buffer *out, struct gui_rect r, { /* text selection in the current text frame */ gui_size glyph_index; - gui_size glyph_pos=gui_font_glyph_index_at_pos(font,visible,text_len,xoff); + gui_size glyph_pos=gui_user_font_glyph_index_at_pos(font,visible,text_len,xoff); if (glyph_cnt + glyph_off >= box->glyphes) glyph_index = glyph_off + MIN(glyph_pos, glyph_cnt); else glyph_index = glyph_off + MIN(glyph_pos, glyph_cnt-1); @@ -2655,7 +3654,7 @@ gui_size gui_widget_edit_filtered(struct gui_command_buffer *out, struct gui_rect r, gui_char *buffer, gui_size len, gui_size max, gui_state *active, gui_size *cursor, const struct gui_edit *field, gui_filter filter, - const struct gui_input *in, const struct gui_font *font) + const struct gui_input *in, const struct gui_user_font *font) { struct gui_edit_box box; gui_edit_box_init_fixed(&box, buffer, max, 0, filter); @@ -2675,7 +3674,7 @@ gui_size gui_widget_edit(struct gui_command_buffer *out, struct gui_rect r, gui_char *buffer, gui_size len, gui_size max, gui_state *active, gui_size *cursor, const struct gui_edit *field, enum gui_input_filter f, - const struct gui_input *in, const struct gui_font *font) + const struct gui_input *in, const struct gui_user_font *font) { static const gui_filter filter[] = { gui_filter_default, @@ -2737,7 +3736,7 @@ gui_widget_scrollbarv(struct gui_command_buffer *out, struct gui_rect scroll, const gui_float delta = (pixel / scroll.h) * target; scroll_offset = CLAMP(0, scroll_offset + delta, target - scroll.h); col = s->active; - } else if (s->has_scrolling && ((i->mouse.scroll_delta < 0) || (i->mouse.scroll_delta>0))) { + } else if (s->has_scrolling && ((i->mouse.scroll_delta<0) || (i->mouse.scroll_delta>0))) { /* update cursor by mouse scrolling */ scroll_offset = scroll_offset + scroll_step * (-i->mouse.scroll_delta); scroll_offset = CLAMP(0, scroll_offset, target - scroll.h); @@ -2802,7 +3801,7 @@ gui_widget_scrollbarh(struct gui_command_buffer *out, struct gui_rect scroll, const gui_float delta = (pixel / scroll.w) * target; scroll_offset = CLAMP(0, scroll_offset + delta, target - scroll.w); col = s->active; - } else if (s->has_scrolling && ((i->mouse.scroll_delta < 0) || (i->mouse.scroll_delta>0))) { + } else if (s->has_scrolling && ((i->mouse.scroll_delta<0) || (i->mouse.scroll_delta>0))) { /* update cursor by mouse scrolling */ scroll_offset = scroll_offset + scroll_step * (-i->mouse.scroll_delta); scroll_offset = CLAMP(0, scroll_offset, target - scroll.w); @@ -2821,7 +3820,7 @@ static gui_int gui_widget_spinner_base(struct gui_command_buffer *out, struct gui_rect r, const struct gui_spinner *s, gui_char *buffer, gui_size *len, enum gui_input_filter filter, gui_state *active, - const struct gui_input *in, const struct gui_font *font) + const struct gui_input *in, const struct gui_user_font *font) { gui_int ret = 0; struct gui_rect bounds; @@ -2871,7 +3870,7 @@ gui_int gui_widget_spinner(struct gui_command_buffer *out, struct gui_rect r, const struct gui_spinner *s, gui_int min, gui_int value, gui_int max, gui_int step, gui_state *active, const struct gui_input *in, - const struct gui_font *font) + const struct gui_user_font *font) { gui_int res; gui_char string[GUI_MAX_NUMBER_BUFFER]; @@ -2981,7 +3980,7 @@ gui_style_default_color(struct gui_style *style) void gui_style_default(struct gui_style *style, gui_flags flags, - const struct gui_font *font) + const struct gui_user_font *font) { GUI_ASSERT(style); if (!style) return; @@ -2997,7 +3996,7 @@ gui_style_default(struct gui_style *style, gui_flags flags, } void -gui_style_set_font(struct gui_style *style, const struct gui_font *font) +gui_style_set_font(struct gui_style *style, const struct gui_user_font *font) { GUI_ASSERT(style); if (!style) return; @@ -3141,6 +4140,79 @@ gui_window_set_config(struct gui_window *panel, const struct gui_style *config) panel->style = config; } +void +gui_window_unlink(struct gui_window *window) +{ + GUI_ASSERT(window); + GUI_ASSERT(window->queue); + if (!window || !window->queue) return; + gui_command_queue_remove(window->queue, &window->buffer); + window->queue = 0; +} + +void +gui_window_set_queue(struct gui_window *panel, struct gui_command_queue *queue) +{ + GUI_ASSERT(panel); + GUI_ASSERT(queue); + if (!panel || !queue) return; + if (panel->queue) + gui_window_unlink(panel); + gui_command_queue_insert_back(queue, &panel->buffer); + panel->queue = queue; +} + +void +gui_window_set_input(struct gui_window *window, const struct gui_input *in) +{ + GUI_ASSERT(window); + GUI_ASSERT(in); + if (!window || !in) return; + window->input = in; +} + +struct gui_vec2 +gui_window_get_scrollbar(struct gui_window *window) +{GUI_ASSERT(window); return window->offset;} + +void +gui_window_set_scrollbar(struct gui_window *window, struct gui_vec2 offset) +{GUI_ASSERT(window); if (!window) return; window->offset = offset;} + +void +gui_window_set_position(struct gui_window *window, struct gui_vec2 offset) +{ + GUI_ASSERT(window); + if (!window) return; + window->bounds.x = offset.x; + window->bounds.y = offset.y; +} + +struct gui_vec2 +gui_window_get_position(struct gui_window *window) +{GUI_ASSERT(window);return gui_vec2(window->bounds.x, window->bounds.y);} + +void +gui_window_set_size(struct gui_window *window, struct gui_vec2 size) +{ + GUI_ASSERT(window); + if (!window) return; + window->bounds.w = size.x; + window->bounds.h = size.y; +} + +struct gui_vec2 +gui_window_get_size(struct gui_window *window) +{GUI_ASSERT(window);return gui_vec2(window->bounds.w, window->bounds.h);} + +void +gui_window_set_bounds(struct gui_window *window, struct gui_rect bounds) +{GUI_ASSERT(window); if (!window) return; window->bounds = bounds;} + +struct gui_rect +gui_window_get_bounds(struct gui_window *window) +{GUI_ASSERT(window);return window->bounds;} + void gui_window_add_flag(struct gui_window *panel, gui_flags f) {panel->flags |= f;} @@ -3207,7 +4279,6 @@ gui_begin(struct gui_context *context, struct gui_window *window) gui_command_queue_start(window->queue, &window->buffer); { gui_bool inpanel; - gui_float x, y, w, h; struct gui_command_buffer_list *s = &window->queue->list; inpanel = gui_input_mouse_clicked(in, GUI_BUTTON_LEFT, window->bounds); if (inpanel && (&window->buffer != s->end)) { @@ -3358,6 +4429,8 @@ gui_end(struct gui_context *layout, struct gui_window *window) /* update the current 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->valid && (layout->flags & GUI_WINDOW_DYNAMIC) && !(layout->valid & GUI_WINDOW_NO_SCROLLBAR)) { /* calculate the dynamic window footer bounds */ @@ -3397,7 +4470,7 @@ gui_end(struct gui_context *layout, struct gui_window *window) { /* vertical scollbar */ bounds.x = layout->bounds.x + layout->width; - bounds.y = (layout->flags & GUI_WINDOW_BORDER) ? layout->bounds.y + 1 : layout->bounds.y; + bounds.y = (layout->flags & GUI_WINDOW_BORDER) ? layout->bounds.y+1:layout->bounds.y; bounds.y += layout->header.h + layout->menu.h; bounds.w = scrollbar_size; bounds.h = layout->height; @@ -3498,6 +4571,8 @@ gui_end(struct gui_context *layout, struct gui_window *window) /* window is visible and not tab */ else gui_command_queue_finish(window->queue, &window->buffer); } + + /* the remove ROM flag was set so remove ROM FLAG*/ if (layout->flags & GUI_WINDOW_REMOVE_ROM) { layout->flags &= ~(gui_flags)GUI_WINDOW_ROM; layout->flags &= ~(gui_flags)GUI_WINDOW_REMOVE_ROM; @@ -4191,7 +5266,7 @@ gui_panel_alloc_space(struct gui_rect *bounds, struct gui_context *layout) spacing = gui_style_property(config, GUI_PROPERTY_ITEM_SPACING); padding = gui_style_property(config, GUI_PROPERTY_PADDING); - /* check if the end of the row was hit and begin new row if so */ + /* check if the end of the row has been hit and begin new row if so */ if (layout->row.index >= layout->row.columns) gui_panel_alloc_row(layout); @@ -4335,7 +5410,7 @@ gui_layout_push(struct gui_context *layout, sym.y = header.y + item_padding.y + config->font.height/2; sym.x = header.x + panel_padding.x; - /* calculate the triangle vertexes and draw triangle */ + /* calculate the triangle points and draw triangle */ gui_triangle_from_direction(points, sym, 0, 0, heading); gui_command_buffer_push_triangle(layout->buffer, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y, config->colors[GUI_COLOR_TEXT]); @@ -4623,7 +5698,8 @@ gui_button_symbol(struct gui_context *layout, enum gui_symbol symbol, button.normal = config->colors[GUI_COLOR_TEXT]; button.hover = config->colors[GUI_COLOR_TEXT_HOVERING]; button.active = config->colors[GUI_COLOR_TEXT_ACTIVE]; - return gui_widget_button_symbol(layout->buffer, bounds, symbol, behavior, &button, i, &config->font); + return gui_widget_button_symbol(layout->buffer, bounds, symbol, + behavior, &button, i, &config->font); } gui_bool @@ -4723,7 +5799,7 @@ gui_button_text_image(struct gui_context *layout, struct gui_image img, } gui_bool -gui_button_invisible(struct gui_context *layout, const char *text, +gui_button_fitting(struct gui_context *layout, const char *text, enum gui_text_align align, enum gui_button_behavior behavior) { struct gui_rect bounds; @@ -5274,101 +6350,6 @@ gui_graph_callback(struct gui_context *layout, enum gui_graph_type type, return index; } -/* - * ------------------------------------------------------------- - * - * TABLE - * - * -------------------------------------------------------------- - */ -static void -gui_table_horizontal_line(struct gui_context *l, gui_size row_height) -{ - gui_float x, y, w; - struct gui_command_buffer *out = l->buffer; - const struct gui_style *c = l->style; - const struct gui_vec2 item_padding = gui_style_property(c, GUI_PROPERTY_ITEM_PADDING); - const struct gui_vec2 item_spacing = gui_style_property(c, GUI_PROPERTY_ITEM_SPACING); - if (!l->valid) return; - - /* draws a horizontal line under the current item */ - w = MAX(l->width, (2 * item_spacing.x + 2 * item_padding.x)); - y = (l->at_y + (gui_float)row_height + item_padding.y) - l->offset.y; - x = l->at_x + item_spacing.x + item_padding.x - l->offset.x; - w = w - (2 * item_spacing.x + 2 * item_padding.x); - gui_command_buffer_push_line(out, x, y, x + w, y, c->colors[GUI_COLOR_TABLE_LINES]); -} - -static void -gui_table_vertical_line(struct gui_context *layout, gui_size cols) -{ - gui_size i; - struct gui_command_buffer *out; - const struct gui_style *config; - - GUI_ASSERT(layout); - GUI_ASSERT(cols); - if (!layout || !cols) return; - if (!layout->valid) return; - - out = layout->buffer; - config = layout->style; - for (i = 0; i < cols - 1; ++i) { - gui_float y, h; - struct gui_rect bounds; - - /* draw a line between every item in the current row */ - gui_panel_alloc_space(&bounds, layout); - y = layout->at_y + layout->row.height; - h = layout->row.height; - gui_command_buffer_push_line(out, bounds.x + bounds.w, y, - bounds.x + bounds.w, h, config->colors[GUI_COLOR_TABLE_LINES]); - } - layout->row.index -= i; -} - -void -gui_table_begin(struct gui_context *layout, gui_flags flags, - gui_size row_height, gui_size cols) -{ - GUI_ASSERT(layout); - if (!layout || !layout->valid) return; - - layout->is_table = gui_true; - layout->tbl_flags = flags; - gui_layout_row_dynamic(layout, (gui_float)row_height, cols); - if (layout->tbl_flags & GUI_TABLE_HHEADER) - gui_table_horizontal_line(layout, row_height); - if (layout->tbl_flags & GUI_TABLE_VHEADER) - gui_table_vertical_line(layout, cols); -} - -void -gui_table_row(struct gui_context *layout) -{ - const struct gui_style *config; - struct gui_vec2 item_spacing; - GUI_ASSERT(layout); - if (!layout) return; - if (!layout->valid) return; - - config = layout->style; - item_spacing = gui_style_property(config, GUI_PROPERTY_ITEM_SPACING); - gui_layout_row_dynamic(layout, layout->row.height-item_spacing.y,layout->row.columns); - if (layout->tbl_flags & GUI_TABLE_HBODY) - gui_table_horizontal_line(layout, - (gui_size)(layout->row.height - item_spacing.y)); - if (layout->tbl_flags & GUI_TABLE_VBODY) - gui_table_vertical_line(layout, layout->row.columns); -} - -void -gui_table_end(struct gui_context *layout) -{ - /* I personally don't like the flag but it is the easiest way to achieve a table */ - layout->is_table = gui_false; -} - /* * ------------------------------------------------------------- * @@ -5540,7 +6521,6 @@ gui_popup_nonblock_end(struct gui_context *parent, struct gui_context *popup) gui_popup_nonblocking_end(parent, popup); return; } - /* * ------------------------------------------------------------- * @@ -5625,8 +6605,8 @@ gui_combo_begin(struct gui_context *parent, struct gui_context *combo, body.w = header.w; body.y = header.y + header.h; body.h = gui_null_rect.h; - if (!gui_popup_nonblocking_begin(parent,combo,GUI_WINDOW_NO_SCROLLBAR, active,is_active,body)) - goto failed; + if (!gui_popup_nonblocking_begin(parent, combo, GUI_WINDOW_NO_SCROLLBAR, + active, is_active, body)) goto failed; combo->flags |= GUI_WINDOW_COMBO_MENU; } return; @@ -5679,7 +6659,7 @@ gui_combo(struct gui_context *layout, const char **entries, gui_layout_row_dynamic(&combo, (gui_float)row_height, 1); for (i = 0; i < count; ++i) { if (i == *current) continue; - if (gui_button_invisible(&combo,entries[i], GUI_TEXT_LEFT, GUI_BUTTON_DEFAULT)) { + if (gui_button_fitting(&combo,entries[i], GUI_TEXT_LEFT, GUI_BUTTON_DEFAULT)) { gui_combo_close(&combo); *active = gui_false; *current = i; @@ -5791,7 +6771,7 @@ gui_menu(struct gui_context *layout, const char *title, gui_menu_begin(layout, &menu, title, width, active); gui_layout_row_dynamic(&menu, (gui_float)row_height, 1); for (i = 0; i < count; ++i) { - if (gui_button_invisible(&menu, entries[i], GUI_TEXT_LEFT, GUI_BUTTON_DEFAULT)) { + if (gui_button_fitting(&menu, entries[i], GUI_TEXT_LEFT, GUI_BUTTON_DEFAULT)) { gui_combo_close(&menu); *active = gui_false; sel = (gui_int)i; @@ -6119,7 +7099,7 @@ gui_shelf_begin(struct gui_context *parent, struct gui_context *shelf, { const struct gui_style *config; struct gui_command_buffer *out; - const struct gui_font *font; + const struct gui_user_font *font; struct gui_vec2 item_padding; struct gui_vec2 panel_padding; diff --git a/gui.h b/gui.h index ed1253e..ca65807 100644 --- a/gui.h +++ b/gui.h @@ -47,6 +47,17 @@ extern "C" { #define GUI_COMPILE_WITH_ASSERT 1 /* setting this define to 1 adds the header for the assert macro IMPORTANT: it also adds clib so only use it if wanted */ +#define GUI_COMPILE_WITH_VERTEX_BUFFER 1 +/* setting this define 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 GUI_COMPILE_WITH_FONT 1 +/* setting this define to 1 adds the `stb_truetype` and `stb_rect_pack` header + to this library and provided a default font for font loading and rendering. + If you already have a font or do not want to use this font you can just set this + define to zero and the font module will not be compiled and the two header + will not be needed. */ /* * ============================================================== * @@ -106,13 +117,15 @@ enum gui_widget_states {GUI_INACTIVE = gui_false, GUI_ACTIVE = gui_true}; enum gui_collapse_states {GUI_MINIMIZED = gui_false, GUI_MAXIMIZED = gui_true}; /* Callbacks */ -struct gui_font; +struct gui_user_font; struct gui_edit_box; -struct gui_font_glyph; +struct gui_user_font_glyph; typedef gui_bool(*gui_filter)(gui_long unicode); typedef void(*gui_paste_f)(gui_handle, struct gui_edit_box*); typedef void(*gui_copy_f)(gui_handle, const char*, gui_size size); typedef gui_size(*gui_text_width_f)(gui_handle, const gui_char*, gui_size); +typedef void(*gui_query_font_glyph_f)(gui_handle, struct gui_user_font_glyph*, + gui_long codepoint, gui_long next_codepoint); /* * ============================================================== * @@ -387,6 +400,10 @@ gui_bool gui_input_is_key_down(const struct gui_input*, enum gui_keys); gui_buffer_alloc -- allocates a block of memory from the buffer gui_buffer_clear -- resets the buffer back to an empty state gui_buffer_free -- frees all memory if the buffer is dynamic + gui_buffer_mark -- marks the current buffer size for later resetting + gui_buffer_reset -- resets the buffer either back to zero or up to marker if set + gui_buffer_memory -- returns the internal memory + gui_buffer_total -- returns the current total size of the memory */ struct gui_memory_status { void *memory; @@ -469,7 +486,7 @@ void gui_buffer_init(struct gui_buffer*, const struct gui_allocator*, - dynamically growing buffer */ void gui_buffer_init_fixed(struct gui_buffer*, void *memory, gui_size size); -/* this function initializes a fixed size buffer +/* this function initializes a fixed sized buffer Input: - fixed size previously allocated memory block - size of the memory block @@ -494,17 +511,21 @@ void *gui_buffer_alloc(struct gui_buffer*, enum gui_buffer_allocation_type, - memory block with given size and alignment requirement */ void gui_buffer_mark(struct gui_buffer*, enum gui_buffer_allocation_type); -/* sets a marker either for the back or front buffer */ +/* sets a marker either for the back or front buffer for later resetting */ void gui_buffer_reset(struct gui_buffer*, enum gui_buffer_allocation_type); /* resets the buffer back to the previously set marker or if not set the begining */ void gui_buffer_clear(struct gui_buffer*); /* this functions resets the buffer back into an empty state */ void gui_buffer_free(struct gui_buffer*); /* this functions frees all memory inside a dynamically growing buffer */ +void *gui_buffer_memory(struct gui_buffer*); +/* returns the memory inside the buffer */ +gui_size gui_buffer_total(struct gui_buffer*); +/* returns the total size of the buffer */ /* * ============================================================== * - * Command Buffer + * Command Buffer * * =============================================================== */ @@ -518,7 +539,7 @@ void gui_buffer_free(struct gui_buffer*); widget output. The actual draw command execution is done by the user and is build up in a interpreter like fashion by iterating over all commands and executing each - command differently depending on the command type. + command depending on the command type. USAGE ---------------------------- @@ -538,25 +559,25 @@ void gui_buffer_free(struct gui_buffer*); command queue function API gui_command_buffer_push -- pushes and enqueues a command into the buffer - gui_command_buffer_push_scissor -- pushes a clipping rectangle into the command queue - gui_command_buffer_push_line -- pushes a line into the command queue - gui_command_buffer_push_rect -- pushes a rectange into the command queue - gui_command_buffer_push_circle -- pushes a circle into the command queue - gui_command_buffer_push_triangle-- pushes a triangle command into the queue - gui_command_buffer_push_image -- pushes a image draw command into the queue - gui_command_buffer_push_text -- pushes a text draw command into the queue + gui_command_buffer_push_scissor -- pushes a clipping rectangle into the command buffer + gui_command_buffer_push_line -- pushes a line into the command buffer + gui_command_buffer_push_rect -- pushes a rectange into the command buffer + gui_command_buffer_push_circle -- pushes a circle into the command buffer + gui_command_buffer_push_triangle-- pushes a triangle command into the buffer + gui_command_buffer_push_cruve -- pushes a bezier cruve command into the buffer + gui_command_buffer_push_image -- pushes a image draw command into the buffer + gui_command_buffer_push_text -- pushes a text draw command into the buffer command iterator function API - gui_command_buffer_begin -- returns the first command in a queue - gui_command_buffer_next -- returns the next command in a queue - gui_foreach_buffer_command -- iterates over all commands in a queue + gui_command_buffer_begin -- returns the first command in a buffer + gui_command_buffer_next -- returns the next command in a buffer + gui_foreach_buffer_command -- iterates over all commands in a buffer */ /* command type of every used drawing primitive */ enum gui_command_type { GUI_COMMAND_NOP, GUI_COMMAND_SCISSOR, GUI_COMMAND_LINE, - GUI_COMMAND_QUAD, GUI_COMMAND_CURVE, GUI_COMMAND_RECT, GUI_COMMAND_CIRCLE, @@ -587,14 +608,6 @@ struct gui_command_line { struct gui_color color; }; -struct gui_command_quad { - struct gui_command header; - struct gui_vec2i begin; - struct gui_vec2i end; - struct gui_vec2i ctrl; - struct gui_color color; -}; - struct gui_command_curve { struct gui_command header; struct gui_vec2i begin; @@ -618,13 +631,6 @@ struct gui_command_circle { struct gui_color color; }; -struct gui_command_image { - struct gui_command header; - gui_short x, y; - gui_ushort w, h; - struct gui_image img; -}; - struct gui_command_triangle { struct gui_command header; struct gui_vec2i a; @@ -633,9 +639,16 @@ struct gui_command_triangle { struct gui_color color; }; +struct gui_command_image { + struct gui_command header; + gui_short x, y; + gui_ushort w, h; + struct gui_image img; +}; + struct gui_command_text { struct gui_command header; - gui_handle font; + const struct gui_user_font *font; struct gui_color background; struct gui_color foreground; gui_short x, y; @@ -716,17 +729,6 @@ void gui_command_buffer_push_line(struct gui_command_buffer*, gui_float, gui_flo - ending point of the line - color of the line to draw */ -void gui_command_buffer_push_quad(struct gui_command_buffer*, gui_float, gui_float, - gui_float, gui_float, gui_float, gui_float, - struct gui_color); -/* this function pushes a quad bezier line draw command into the buffer - Input: - - buffer to push the clip rectangle command into - - starting point (x,y) of the line - - control point (x,y) of the line - - ending point (x,y) of the line - - color of the line to draw -*/ void gui_command_buffer_push_curve(struct gui_command_buffer*, gui_float, gui_float, gui_float, gui_float, gui_float, gui_float, gui_float, gui_float, struct gui_color); @@ -777,7 +779,7 @@ void gui_command_buffer_push_image(struct gui_command_buffer*, struct gui_rect, - color of the triangle to draw */ void gui_command_buffer_push_text(struct gui_command_buffer*, struct gui_rect, - const gui_char*, gui_size, const struct gui_font*, + const gui_char*, gui_size, const struct gui_user_font*, struct gui_color, struct gui_color); /* this function pushes a text draw command into the buffer Input: @@ -807,15 +809,13 @@ const struct gui_command *gui_command_buffer_next(struct gui_command_buffer*, ---------------------------- The command queue extends the command buffer with the possiblity to use more than one command buffer on one memory buffer and still only need - to iterate over one command list. Therefore it is possible to have mutliple + to iterate over one command list. Therefore it is possible to have multiple windows without having to manage each windows individual memory. This greatly simplifies and reduces the amount of code needed with just using command buffers. Internally the command queue has a list of command buffers which can be modified to create a certain sequence, for example the `gui_window_begin` - function changes the list to create overlapping windows, while the - `gui_window_begin_tiled` function makes sure that its command buffers will - always be drawn first since window in tiled layouts are always in the background. + function changes the list to create overlapping windows. USAGE ---------------------------- @@ -947,7 +947,7 @@ void gui_command_queue_free(struct gui_command_queue*); void gui_command_queue_clear(struct gui_command_queue*); /* this function reset the internal buffer and has to be called every frame */ #define gui_foreach_command(i, q)\ - for((i)=gui_command_queue_begin(q); (i)!=NULL; (i)=gui_command_queue_next(q,i)) + for((i)=gui_command_queue_begin(q); (i)!=0; (i)=gui_command_queue_next(q,i)) /* this function iterates over each command inside the command queue Input: - iterator gui_command pointer to iterate over all commands @@ -961,6 +961,381 @@ const struct gui_command *gui_command_queue_begin(struct gui_command_queue*); const struct gui_command* gui_command_queue_next(struct gui_command_queue*, const struct gui_command*); /* this function returns the next command of a given command*/ +/* + * =============================================================== + * + * Draw List + * + * =============================================================== + */ +#if GUI_COMPILE_WITH_VERTEX_BUFFER +/* DRAW LIST + ---------------------------- + The optional draw list provides a way to convert the gui library + drawing command output into a hardware accessible format. + This consists of vertex, element and draw batch command buffer output which + can be interpreted by render frameworks (OpenGL, DirectX, ...). + In addition to just provide a way to convert commands the draw list has + a primitives and stateful path drawing API, which allows to draw into the + draw list as well. The actuall drawing support in addition Anti-aliasing. + + The draw list consist internaly of three user provided buffers that will be + filled with data. The first buffer is the the draw command and temporary + path buffer which permanetly stores all draw batch commands. The vertex and + element buffer are the same buffers as their hardware renderer counter-parts, + in fact it is even possible to directly map one of these buffers and fill + them with data. + + The reason why the draw list is optional or is not the default library output + is that basic commands provide an easy way to abstract over other libraries + which already provide a drawing API and do not need or want the output the + draw list provides. + + USAGE + ---------------------------- + To actually use the draw list you first need the initialize the draw list + by providing three filled buffers to be filled while drawing. The reason + buffers need to be provided and not memory or an allocator is to provide + more fine grained control over the memory inside the draw list, which in term + requires more work from the user. + + After the draw list has been initialized you can fill the draw list by + a.) converting the content of a command queue into the draw list format + b.) adding primtive filled shapes or only the outline of rectangles, circle, etc. + c.) using the stateful path API for fine grained drawing control + + Finaly for the drawing process you have to iterate over each draw command + inside the `gui_draw_list` by using the function `gui_foreach_draw_command` + which contains drawing state like clip rectangle, current textue a number + of element to draw with the current state. + + draw list buffer functions + gui_draw_list_init - initializes a command buffer with memory + gui_draw_list_clear - clears and resets the buffer + gui_draw_list_load - load the draw buffer from a command queue + gui_draw_list_begin - returns the first command in the draw buffer + gui_draw_list_next - returns the next command in the draw buffer or NULL + gui_draw_list_is_empty - returns if the buffer has no vertexes or commands + gui_foreach_draw_command - iterates over all commands in the draw buffer + + draw list primitives drawing functions + gui_draw_list_add_line - pushes a line into the draw list + gui_draw_list_add_rect - pushes a rectangle into the draw list + gui_draw_list_add_rect_filled - pushes a filled rectangle into the draw list + gui_draw_list_add_triangle - pushes a triangle into the draw list + gui_draw_list_add_triangle_filled -pushes a filled triangle into the draw list + gui_draw_list_add_circle - pushes circle into the draw list + gui_draw_list_add_circle_filled - pushes a filled circle into the draw list + gui_draw_list_add_text - pushes text into the draw list + gui_draw_list_add_image - pushes an image into the draw list + + stateful path functions + gui_draw_list_path_clear - resets the current path + gui_draw_list_path_line_to - adds a point into the path + gui_draw_list_path_arc_to - adds a arc into the path + gui_draw_list_path_curve_to - adds a bezier curve into the path + gui_draw_list_path_rect_to - adds a rectangle into the path + gui_draw_list_path_fill - fills the path as a convex polygon + gui_draw_list_path_stroke - connects each point in the path +*/ +typedef gui_ushort gui_draw_index; +typedef gui_uint gui_draw_vertex_color; +typedef gui_float(*gui_sin_f)(gui_float); +typedef gui_float(*gui_cos_f)(gui_float); + +enum gui_anti_aliasing { + GUI_ANTI_ALIASING_OFF = gui_false, + /* renderes all primitives without anit-aliasing */ + GUI_ANTI_ALIASING_ON, + /* renderes all primitives with anit-aliasing */ + GUI_ANTI_ALIASING_MAX +}; + +struct gui_draw_vertex { + struct gui_vec2 position; + struct gui_vec2 uv; + gui_draw_vertex_color col; +}; + +enum gui_draw_list_stroke { + GUI_STROKE_OPEN = gui_false, + GUI_STROKE_CLOSED = gui_true +}; + +struct gui_draw_command { + gui_uint elem_count; + /* number of elements in the current draw batch */ + struct gui_rect clip_rect; + /* current screen clipping rectangle */ + gui_handle texture; + /* current texture to set */ +}; + +struct gui_draw_null_texture { + gui_handle texture; + /* texture handle to a texture with a white pixel */ + struct gui_vec2 uv; + /* coordinates to the white pixel in the texture */ +}; + +struct gui_draw_list { + enum gui_anti_aliasing AA; + /* flag indicating if anti-aliasing should be used to render primtives */ + struct gui_draw_null_texture null; + /* texture with white pixel for easy primitive drawing */ + struct gui_rect clip_rect; + /* current clipping rectangle */ + gui_cos_f cos; gui_sin_f sin; + /* cosine/sine calculation callback since this library does not use clib */ + struct gui_buffer *buffer; + /* buffer to store draw commands and temporarily store path */ + struct gui_buffer *vertexes; + /* buffer to store each draw vertex */ + struct gui_buffer *elements; + /* buffer to store each draw element index */ + gui_uint element_count; + /* total number of elements inside the elements buffer */ + gui_uint vertex_count; + /* total number of vertexes inside the vertex buffer */ + gui_size cmd_offset; + /* offset to the first command in the buffer */ + gui_uint cmd_count; + /* number of commands inside the buffer */ + gui_uint path_count; + /* current number of points inside the path */ + gui_uint path_offset; + /* offset to the first point in the buffer */ +}; +/* --------------------------------------------------------------- + * MAIN + * ---------------------------------------------------------------*/ +void gui_draw_list_init(struct gui_draw_list*, struct gui_buffer *cmds, + struct gui_buffer *vertexes, struct gui_buffer *elements, + gui_sin_f, gui_cos_f, struct gui_draw_null_texture, + enum gui_anti_aliasing AA); +/* this function initializes the draw list + Input: + - command memory buffer to fill with commands and provided temporary memory for path + - vertex memory buffer to fill with draw vertexeses + - element memory buffer to fill with draw indexes + - sine function callback since this library does not use clib (default: just use sinf) + - cosine function callback since this library does not use clib (default: just use cosf) + - special null structure which holds a texture with a white pixel + - Anti-aliasing flag for turning on/off anti-aliased primitives drawing +*/ +void gui_draw_list_load(struct gui_draw_list*, struct gui_command_queue *queue, + gui_float line_thickness, gui_uint curve_segments); +/* this function loads the draw list from command queue commands + Input: + - queue with draw commands to fill the draw list with + - line thickness for all lines and non-filled primitives + - number of segments used for drawing circles and curves +*/ +void gui_draw_list_clear(struct gui_draw_list *list); +/* this function clears all buffers inside the draw list */ +gui_bool gui_draw_list_is_empty(struct gui_draw_list *list); +/* this function returns if the draw list is empty */ +#define gui_foreach_draw_command(i, q)\ + for((i)=gui_draw_list_begin(q); (i)!=NULL; (i)=gui_draw_list_next(q,i)) +/* this function iterates over each draw command inside the draw list + Input: + - iterator gui_draw_command pointer to iterate over all commands + - draw list to iterate over +*/ +const struct gui_draw_command *gui_draw_list_begin(const struct gui_draw_list *list); +/* this function returns the first draw command in the draw list */ +const struct gui_draw_command* gui_draw_list_next(const struct gui_draw_list *list, + const struct gui_draw_command*); +/* this function returns the next draw command of a given draw command*/ +/* --------------------------------------------------------------- + * PRIMITIVES + * ---------------------------------------------------------------*/ +void gui_draw_list_add_clip(struct gui_draw_list*, struct gui_rect); +/* this function pushes a new clipping rectangle into the draw list + Input: + - new clipping rectangle +*/ +void gui_draw_list_add_line(struct gui_draw_list*, struct gui_vec2 a, + struct gui_vec2 b, struct gui_color col, + gui_float thickness); +/* this function pushes a new clipping rectangle into the draw list + Input: + - beginning point of the line + - end point of the line + - used line color + - line thickness in pixel +*/ +void gui_draw_list_add_rect(struct gui_draw_list*, struct gui_rect rect, + struct gui_color col, gui_float rounding, + gui_float line_thickness); +/* this function pushes a rectangle into the draw list + Input: + - rectangle to render into the draw list + - rectangle outline color + - rectangle edge rounding (0 == no edge rounding) + - rectangle outline thickness +*/ +void gui_draw_list_add_rect_filled(struct gui_draw_list*, struct gui_rect rect, + struct gui_color col, gui_float rounding); +/* this function pushes a filled rectangle into the draw list + Input: + - rectangle to render into the draw list + - color to fill the rectangle with + - rectangle edge rounding (0 == no edge rounding) +*/ +void gui_draw_list_add_triangle(struct gui_draw_list*, struct gui_vec2 a, + struct gui_vec2 b, struct gui_vec2 c, + struct gui_color, gui_float line_thickness); +/* this function pushes a triangle into the draw list + Input: + - first point of the triangle + - second point of the triangle + - third point of the triangle + - color of the triangle outline + - triangle outline line thickness +*/ +void gui_draw_list_add_triangle_filled(struct gui_draw_list*, struct gui_vec2 a, + struct gui_vec2 b, struct gui_vec2 c, + struct gui_color col); +/* this function pushes a filled triangle into the draw list + Input: + - first point of the triangle + - second point of the triangle + - third point of the triangle + - color to fill the triangle with +*/ +void gui_draw_list_add_circle(struct gui_draw_list*, struct gui_vec2 center, + gui_float radius, struct gui_color col, gui_uint segs, + gui_float line_thickness); +/* this function pushes a circle outline into the draw list + Input: + - center point of the circle + - circle radius + - circle outlone color + - number of segement that make up the circle +*/ +void gui_draw_list_add_circle_filled(struct gui_draw_list*, struct gui_vec2 center, + gui_float radius, struct gui_color col, gui_uint); +/* this function pushes a filled circle into the draw list + Input: + - center point of the circle + - circle radius + - color to fill the circle with + - number of segement that make up the circle +*/ +void gui_draw_list_add_curve(struct gui_draw_list*, struct gui_vec2 p0, + struct gui_vec2 cp0, struct gui_vec2 cp1, struct gui_vec2 p1, + struct gui_color col, gui_uint segments, gui_float thickness); +/* this function pushes a bezier curve into the draw list + Input: + - beginning point of the curve + - first curve control point + - second curve control point + - end point of the curve + - color of the curve + - number of segement that make up the circle + - line thickness of the curve +*/ +void gui_draw_list_add_text(struct gui_draw_list*, const struct gui_user_font*, + struct gui_rect, const gui_char*, gui_size length, + struct gui_color bg, struct gui_color fg); +/* this function renders text + Input: + - user font to draw the text with + - the rectangle the text will be drawn into + - string text to draw + - length of the string to draw + - text background color + - text color +*/ +void gui_draw_list_add_image(struct gui_draw_list *list, struct gui_image texture, + struct gui_rect rect, struct gui_color color); +/* this function renders an image + Input: + - image texture handle to draw + - rectangle with position and size of the image + - color to blend with the image +*/ +/* --------------------------------------------------------------- + * PATH + * ---------------------------------------------------------------*/ +void gui_draw_list_path_clear(struct gui_draw_list*); +/* clears the statefull drawing path previously build */ +void gui_draw_list_path_line_to(struct gui_draw_list*, struct gui_vec2 pos); +/* adds a point into the path that will make up a line or a convex polygon */ +void gui_draw_list_path_arc_to(struct gui_draw_list*, struct gui_vec2 center, + gui_float radius, gui_float a_min, gui_float a_max, + gui_uint segments); +/* adds an arc made up of a number of segments into the path */ +void gui_draw_list_path_rect_to(struct gui_draw_list*, struct gui_vec2 a, + struct gui_vec2 b, gui_float rounding); +/* adds a rectangle into the path */ +void gui_draw_list_path_curve_to(struct gui_draw_list*, struct gui_vec2 p1, + struct gui_vec2 p2, struct gui_vec2 p3, + gui_uint num_segments); +/* adds a bezier curve into the path where the first point has to be already in the path */ +void gui_draw_list_path_fill(struct gui_draw_list*, struct gui_color); +/* uses all points inside the path to draw a convex polygon */ +void gui_draw_list_path_stroke(struct gui_draw_list*, struct gui_color, + gui_bool closed, gui_float thickness); +/* uses all points inside the path to draw a line/outline */ +#endif +/* + * ============================================================== + * + * Font + * + * =============================================================== + */ +struct gui_user_font_glyph { + struct gui_vec2 uv[2]; + struct gui_vec2 offset; + gui_float width, height; + gui_float xadvance; +}; + +struct gui_user_font { + gui_handle userdata; + /* user provided font handle */ + gui_float height; + /* max height of the font */ + gui_text_width_f width; + /* font string width in pixel callback */ +#if GUI_COMPILE_WITH_VERTEX_BUFFER + gui_query_font_glyph_f query; + /* font glyph callback to query drawing info */ + gui_handle texture; + /* texture handle to the used font atlas or texture */ +#endif +}; + +#ifdef GUI_COMPILE_WITH_FONT +struct gui_font_atlas { + gui_byte *pixels; + gui_int tex_width; + gui_int tex_height; + struct gui_vec2 white_pixel; +}; + +struct gui_font_glyph { + gui_long codepoint; + gui_float xadvance; + struct gui_vec2 point[2]; + struct gui_vec2 uv[2]; +}; + +struct gui_font { + gui_float size; + gui_float scale; + struct gui_vec2 offset; + gui_float ascent, descent; + struct gui_buffer glyphes; + gui_long fallback_code; + struct gui_font_glyph *fallback_glyph; + gui_float fallback_xadvance; + struct gui_user_font handle; +}; +#endif /* * =============================================================== * @@ -983,14 +1358,24 @@ const struct gui_command* gui_command_queue_next(struct gui_command_queue*, added and removed with `gui_edit_box_add` and `gui_edit_box_remove`. Widget function API - gui_edit_box_init -- initialize a dynamically growing edit box - gui_edit_box_init_fixed -- initialize a statically edit box - gui_edit_box_reset -- resets the edit box back to the beginning - gui_edit_box_clear -- frees all memory of a dynamic edit box - gui_edit_box_add -- adds a symbol to the editbox - gui_edit_box_remove -- removes a symbol from the editbox - gui_edit_box_get -- returns the string inside the editbox - gui_edit_box_len -- returns the length of the string inside the edditbox + gui_edit_box_init -- initialize a dynamically growing edit box + gui_edit_box_init_fixed -- initialize a fixed size edit box + gui_edit_box_reset -- resets the edit box back to the beginning + gui_edit_box_clear -- frees all memory of a dynamic edit box + gui_edit_box_add -- adds a symbol to the editbox + gui_edit_box_remove -- removes a symbol from the editbox + gui_edit_box_get -- returns the string inside the editbox + gui_edit_box_get_const -- returns the const string inside the editbox + gui_edit_box_len -- returns the length of the string inside the edditbox + gui_edit_box_free -- frees all memory in a dynamic editbox + gui_edit_box_info -- fills a memory info struct with data + gui_edit_box_at -- returns the glyph at the given position + gui_edit_box_at_cursor -- returns the glyph at the cursor position + gui_edit_box_at_char -- returns the char at the given position + gui_edit_box_set_cursor -- sets the cursor to a given glyph + gui_edit_box_get_cursor -- returns the position of the cursor + gui_edit_box_len_char -- returns the length of the string in byte + gui_edit_box_len -- returns the length of the string in glyphes */ struct gui_clipboard { gui_handle userdata; @@ -1157,15 +1542,6 @@ gui_size gui_edit_box_len(struct gui_edit_box*); gui_scrollbarv -- vertical scrollbar widget imeplementation gui_scrollbarh -- horizontal scrollbar widget imeplementation */ -struct gui_font { - gui_handle userdata; - /* user provided font handle */ - gui_float height; - /* max height of the font */ - gui_text_width_f width; - /* font string width in pixel callback */ -}; - enum gui_text_align { GUI_TEXT_LEFT, GUI_TEXT_CENTERED, @@ -1379,7 +1755,7 @@ struct gui_spinner { void gui_widget_text(struct gui_command_buffer*, struct gui_rect, const char*, gui_size, const struct gui_text*, - enum gui_text_align, const struct gui_font*); + enum gui_text_align, const struct gui_user_font*); /* this function executes a text widget with text alignment Input: - output command buffer for drawing @@ -1393,7 +1769,7 @@ void gui_widget_text(struct gui_command_buffer*, struct gui_rect, gui_bool gui_widget_button_text(struct gui_command_buffer*, struct gui_rect, const char*, enum gui_button_behavior, const struct gui_button_text*, - const struct gui_input*, const struct gui_font*); + const struct gui_input*, const struct gui_user_font*); /* this function executes a text button widget Input: - output command buffer for drawing @@ -1424,7 +1800,7 @@ gui_bool gui_widget_button_image(struct gui_command_buffer*, struct gui_rect, gui_bool gui_widget_button_symbol(struct gui_command_buffer*, struct gui_rect, enum gui_symbol, enum gui_button_behavior, const struct gui_button_symbol*, const struct gui_input*, - const struct gui_font*); + const struct gui_user_font*); /* this function executes a triangle button widget Input: - output command buffer for drawing @@ -1438,8 +1814,10 @@ gui_bool gui_widget_button_symbol(struct gui_command_buffer*, struct gui_rect, */ gui_bool gui_widget_button_text_symbol(struct gui_command_buffer*, struct gui_rect, enum gui_symbol, const char*, enum gui_text_align, - enum gui_button_behavior, const struct gui_button_text*, - const struct gui_font*, const struct gui_input*); + enum gui_button_behavior, + const struct gui_button_text*, + const struct gui_user_font*, + const struct gui_input*); /* this function executes a button with text and a triangle widget Input: - output command buffer for drawing @@ -1456,8 +1834,9 @@ gui_bool gui_widget_button_text_symbol(struct gui_command_buffer*, struct gui_re */ gui_bool gui_widget_button_text_image(struct gui_command_buffer*, struct gui_rect, struct gui_image, const char*, enum gui_text_align, - enum gui_button_behavior, const struct gui_button_text*, - const struct gui_font*, const struct gui_input*); + enum gui_button_behavior, + const struct gui_button_text*, + const struct gui_user_font*, const struct gui_input*); /* this function executes a button widget with text and an icon Input: - output command buffer for drawing @@ -1475,7 +1854,7 @@ gui_bool gui_widget_button_text_image(struct gui_command_buffer*, struct gui_rec void gui_widget_toggle(struct gui_command_buffer*, struct gui_rect, gui_bool*, const char *string, enum gui_toggle_type, const struct gui_toggle*, const struct gui_input*, - const struct gui_font*); + const struct gui_user_font*); /* this function executes a toggle (checkbox, radiobutton) widget Input: - output command buffer for drawing @@ -1521,7 +1900,7 @@ gui_size gui_widget_progress(struct gui_command_buffer*, struct gui_rect, */ void gui_widget_editbox(struct gui_command_buffer*, struct gui_rect, struct gui_edit_box*, const struct gui_edit*, - const struct gui_input*, const struct gui_font*); + const struct gui_input*, const struct gui_user_font*); /* this function executes a editbox widget Input: - output command buffer for drawing @@ -1535,7 +1914,7 @@ void gui_widget_editbox(struct gui_command_buffer*, struct gui_rect, gui_size gui_widget_edit(struct gui_command_buffer*, struct gui_rect, gui_char*, gui_size, gui_size max, gui_state*, gui_size *cursor, const struct gui_edit*, enum gui_input_filter filter, const struct gui_input*, - const struct gui_font*); + const struct gui_user_font*); /* this function executes a editbox widget Input: - output command buffer for drawing @@ -1557,7 +1936,7 @@ gui_size gui_widget_edit_filtered(struct gui_command_buffer*, struct gui_rect, gui_char*, gui_size, gui_size max, gui_state*, gui_size *cursor, const struct gui_edit*, gui_filter filter, const struct gui_input*, - const struct gui_font*); + const struct gui_user_font*); /* this function executes a editbox widget Input: - output command buffer for drawing @@ -1578,7 +1957,7 @@ gui_size gui_widget_edit_filtered(struct gui_command_buffer*, struct gui_rect, gui_int gui_widget_spinner(struct gui_command_buffer*, struct gui_rect, const struct gui_spinner*, gui_int min, gui_int value, gui_int max, gui_int step, gui_state *active, - const struct gui_input*, const struct gui_font*); + const struct gui_input*, const struct gui_user_font*); /* this function executes a integer spinner widget Input: - output command buffer for draw commands @@ -1769,7 +2148,7 @@ struct gui_style_mod_stack { }; struct gui_style { - struct gui_font font; + struct gui_user_font font; /* the from the user provided font */ gui_float rounding[GUI_ROUNDING_MAX]; /* rectangle widget rounding */ @@ -1781,7 +2160,7 @@ struct gui_style { /* modification stack */ }; -void gui_style_default(struct gui_style*, gui_flags, const struct gui_font*); +void gui_style_default(struct gui_style*, gui_flags, const struct gui_user_font*); /* this function load the window configuration with default values Input: - config flags which part of the configuration should be loaded with default values @@ -1789,7 +2168,7 @@ void gui_style_default(struct gui_style*, gui_flags, const struct gui_font*); Output: - configuration structure holding the default window style */ -void gui_style_set_font(struct gui_style*, const struct gui_font*); +void gui_style_set_font(struct gui_style*, const struct gui_user_font*); /* this function changes the used font and can be used even inside a frame Input: - user font reference structure describing the font used inside the window @@ -1859,35 +2238,55 @@ void gui_style_reset(struct gui_style*); * Window * * =============================================================== - Window - The window function API is used to initialize a window, create - a stack based window layout, control the build up process and to - modify the window state. The modification of the window is only allowed - outside both sequence points `gui_window_begin` and `gui_window_end`, therefore - all modification inside them is undefined behavior. + WINDOW + The window groups widgets together and allows collective operation + on these widgets like movement, scrolling, window minimizing and closing. + Windows are divided into a persistent state window struct and a temporary + context which is used each frame to fill the window. All direct build up + function therefore work on the context and not on the actual window. + Each window is linked inside a queue which in term allows for an easy + way to buffer output commands but requires that the window is unlinked + from the queue if removed. USAGE - To work a window needs to be initialized by calling `gui_window_init` with - a reference to a valid command buffer and configuration structure. - The references have to be valid over the life time of the window and can be - changed by setter functions. In addition to being initialized - the window needs a window layout in every frame to fill and use as temporary - state to fill the window with widgets. - The window layout hereby does NOT have to be kept around outside of the build - up process and multiple windows can share one window layout. The reason why - window and layout are split is to seperate the temporary changing state - of the window layout from the persistent state of the window. In addition - the window only needs a fraction of the needed space and state of the window layout. + The window needs to be initialized by `gui_window_init` and can be updated + by all the `gui_window_set_xxx` function. Important to note is that each + window is linked inside a queue by an internal memory buffer. So if you want + to remove the window you first have to remove the window from the queue + or if you want to change to queue use `gui_window_queue_set`. window function API - gui_window_init -- initializes the window with position, size and flags - gui_window_begin -- begin sequence point in the window layout build up process - gui_window_end -- end squeunce point which finializes the window build up - gui_window_set_config -- updates the used window configuration - gui_window_add_flag -- adds a behavior flag to the window - gui_window_remove_flag -- removes a behavior flag from the window - gui_window_has_flag -- check if a given behavior flag is set in the window - gui_window_is_minimized -- return wether the window is minimized + ------------------ + gui_window_init -- initializes the window with position, size and flags + gui_window_unlink -- remove the window from the command queue + gui_window_set_config -- updates the used window configuration + gui_window_add_flag -- adds a behavior flag to the window + gui_window_remove_flag -- removes a behavior flag from the window + gui_window_has_flag -- check if a given behavior flag is set in the window + gui_window_is_minimized -- return wether the window is minimized + gui_window_set_position -- set the position of the window + gui_window_get_position -- returns the currnet position of the window + gui_window_set_size -- update thes size of the window + gui_window_get_size -- returns the size of the window + gui_window_set_bounds -- updates position and size of the window + gui_window_get_bounds -- returns position and size of the window + gui_window_set_queue -- removes the window from the old queue and insert it into another + gui_window_set_input -- changes the input source of the window + gui_window_get_scrollbar-- returns the current X-/Y- offset of the scrollbar + gui_window_set_scrollbar-- updates the offset of the scrollbar + + APIs + ----------------- + Window Context API -- The context is temporary state that is used every frame to build a window + Window Header API -- Responsible for creating a header at the top of a window + Window Layout API -- The window layout is responsible for placing widget in the window + Window Widget API -- Different widget that can be placed inside the window + Window Tree API -- Tree widget that allows to visualize and mofify a tree + Window Combobox API -- Combobox widget for collapsable popup content + Window Group API -- Create a subwindow inside a window which again can be filled with widgets + Window Shelf API -- Group window with tabs which can be filled with widget + Window Popup API -- Popup window with either non-blocking or blocking capabilities + Window Menu API -- Popup menus with currently one single depth */ enum gui_window_flags { GUI_WINDOW_HIDDEN = 0x01, @@ -1915,7 +2314,7 @@ enum gui_window_flags { GUI_WINDOW_ACTIVE = 0x10000, /* INTERNAL ONLY!: marks the window as active, used by the window stack */ GUI_WINDOW_TAB = 0x20000, - /* INTERNAL ONLY!: Marks the window as an subwindow of another window(Groups/Tabs/Shelf)*/ + /* INTERNAL ONLY!: Marks the window as subwindow of another window(Groups/Tabs/Shelf)*/ GUI_WINDOW_COMBO_MENU = 0x40000, /* INTERNAL ONLY!: Marks the window as an combo box or menu */ GUI_WINDOW_REMOVE_ROM = 0x80000, @@ -1953,43 +2352,79 @@ void gui_window_init(struct gui_window*, struct gui_rect bounds, Output: - a newly initialized window */ +void gui_window_unlink(struct gui_window*); +/* this function unlinks the window from its queue */ +void gui_window_set_position(struct gui_window*, struct gui_vec2); +/* this function updates the window position on screen */ +struct gui_vec2 gui_window_get_position(struct gui_window*); +/* this function returns the window position on screen */ +void gui_window_set_size(struct gui_window*, struct gui_vec2); +/* this function returns the window size */ +struct gui_vec2 gui_window_get_size(struct gui_window*); +/* this function updates the window size */ +void gui_window_set_bounds(struct gui_window*, struct gui_rect); +/* this function updates the window bounds */ +struct gui_rect gui_window_get_bounds(struct gui_window*); +/* this function returns the window bounds */ void gui_window_set_config(struct gui_window*, const struct gui_style*); /* this function updateds the window configuration pointer */ -void gui_window_set_buffer(struct gui_window*, struct gui_command_buffer*); +void gui_window_set_queue(struct gui_window*, struct gui_command_queue*); /* this function updateds the used window command buffer */ +void gui_window_set_input(struct gui_window*, const struct gui_input*); +/* this function updateds the used window input structure */ +struct gui_vec2 gui_window_get_scrollbar(struct gui_window*); +/* returns the window scrollbar offset */ +void gui_window_set_scrollbar(struct gui_window*, struct gui_vec2); +/* updates the window scrollbar offset */ void gui_window_add_flag(struct gui_window*, gui_flags); -/* this function adds window flags to the window - Input: - - window flags to add the window -*/ +/* this function adds window flags to the window */ void gui_window_remove_flag(struct gui_window*, gui_flags); -/* this function removes window flags from the window - Input: - - window flags to remove from the window -*/ +/* this function removes window flags from the window */ gui_bool gui_window_has_flag(struct gui_window*, gui_flags); -/* this function checks if a window has given flag(s) - Input: - - window flags to check for -*/ +/* this function checks if a window has given flag(s) */ gui_bool gui_window_is_minimized(struct gui_window*); /* this function checks if the window is minimized */ - /* - * ============================================================== - * + * -------------------------------------------------------------- * Context - * - * =============================================================== - */ + * -------------------------------------------------------------- + CONTEXT + The context is temporary window state that is used in the window drawing + and build up process. The reason the window and context are divided is that + the context has far more state which is not needed outside of the build up + phase. The context is not only useful for normal windows. It is used for + more complex widget or layout as well. This includes a window inside a window, + popup windows, menus, comboboxes, etc. In each case the context allows to + fill a space with widgets. Therefore the context provides the base + and key stone of the flexibility in the library. The context is used + for all APIs for the window. + + USAGE + The context from a window is created by `gui_begin` and finilized by + `gui_end`. Between these two sequence points the context can be used + to setup the window with widgets. Other widgets which also use a context + have each their own `gui_xxx_begin` and `gui_xxx_end` function pair and act + the same as a window context. + + context function API + ------------------ + gui_begin -- starts the window build up process by filling a context with window state + gui_end -- ends the window build up process and update the window state + gui_canvas -- returns the currently used drawing command buffer + gui_input -- returns the from the context used input struct + gui_queue -- returns the queue of the window +*/ enum gui_widget_state { - GUI_WIDGET_INVALID, /* The widget cannot be seen and is completly out of view */ - GUI_WIDGET_VALID, /* The widget is completly inside the window can be updated + drawn */ - GUI_WIDGET_ROM /* The widget is partially visible and cannot be updated */ + GUI_WIDGET_INVALID, + /* The widget cannot be seen and is completly out of view */ + GUI_WIDGET_VALID, + /* The widget is completly inside the window can be updated */ + GUI_WIDGET_ROM + /* The widget is partially visible and cannot be updated */ }; enum gui_row_layout_type { - /* ----------------- INTERNAL ------------------------------ */ + /* INTERNAL */ GUI_LAYOUT_DYNAMIC_FIXED, /* fixed widget ratio width window layout */ GUI_LAYOUT_DYNAMIC_ROW, @@ -2091,7 +2526,7 @@ struct gui_context { }; void gui_begin(struct gui_context*, struct gui_window*); -/* this function begins the window build up process +/* this function begins the window build up process by creating context to fill Input: - input structure holding all user generated state changes Output: @@ -2104,7 +2539,7 @@ struct gui_command_buffer* gui_canvas(struct gui_context*); const struct gui_input *gui_input(struct gui_context*); /* this functions returns the currently used input */ struct gui_command_queue *gui_queue(struct gui_context*); -/* this functions returns the currently used input */ +/* this functions returns the currently used queue */ /* * -------------------------------------------------------------- * HEADER @@ -2161,8 +2596,7 @@ enum gui_header_align { }; gui_flags gui_header(struct gui_context*, const char *title, - gui_flags show, gui_flags notify, - enum gui_header_align); + gui_flags show, gui_flags notify, enum gui_header_align); /* this function is a shorthand for the header build up process flag by the user Input: @@ -2172,9 +2606,8 @@ gui_flags gui_header(struct gui_context*, const char *title, */ void gui_header_begin(struct gui_context*); /* this function begins the window header build up process */ -gui_bool gui_header_button(struct gui_context *layout, - enum gui_symbol symbol, - enum gui_header_align); +gui_bool gui_header_button(struct gui_context *layout, enum gui_symbol symbol, + enum gui_header_align); /* this function adds a header button icon Input: - @@ -2190,10 +2623,9 @@ gui_bool gui_header_button_icon(struct gui_context*, struct gui_image, Output: - gui_true if the button was pressed gui_false otherwise */ -gui_bool gui_header_toggle(struct gui_context*, - enum gui_symbol inactive, - enum gui_symbol active, - enum gui_header_align, gui_bool state); +gui_bool gui_header_toggle(struct gui_context*, enum gui_symbol inactive, + enum gui_symbol active, enum gui_header_align, + gui_bool state); /* this function adds a header toggle button Input: - symbol that will be drawn if the toggle is inactive @@ -2202,11 +2634,9 @@ gui_bool gui_header_toggle(struct gui_context*, Output: - updated state of the toggle */ -gui_bool gui_header_flag(struct gui_context *layout, - enum gui_symbol inactive, - enum gui_symbol active, - enum gui_header_align, - enum gui_window_flags flag); +gui_bool gui_header_flag(struct gui_context *layout, enum gui_symbol inactive, + enum gui_symbol active, enum gui_header_align, + enum gui_window_flags flag); /* this function adds a header toggle button for modifing a certain window flag Input: - symbol that will be drawn if the flag is inactive @@ -2262,8 +2692,8 @@ void gui_menubar_end(struct gui_context*); gui_layout_row_space_end -- finishes the free drawingp process window tree layout function API - gui_context_push -- pushes a new node/collapseable header/tab - gui_context_pop -- pops the the previously added node + gui_layout_push -- pushes a new node/collapseable header/tab + gui_layout_pop -- pops the the previously added node */ enum gui_row_layout_format { @@ -2408,7 +2838,7 @@ void gui_layout_pop(struct gui_context*); gui_button_toggle -- toggle button with either active or inactive state gui_button_text_image -- button widget with text and icon gui_button_text_symbol -- button widget with text and a triangle - gui_button_invisible -- button widget without border and fitting space + gui_button_fitting -- button widget without border and fitting space gui_image -- image widget for outputing a image to a window gui_check -- add a checkbox widget gui_option -- radiobutton widget @@ -2564,7 +2994,7 @@ gui_bool gui_button_text_image(struct gui_context *layout, struct gui_image img, - gui_true if the button was transistioned from unpressed to pressed with default button behavior or pressed if repeater behavior. */ -gui_bool gui_button_invisible(struct gui_context *layout, const char *text, +gui_bool gui_button_fitting(struct gui_context *layout, const char *text, enum gui_text_align align, enum gui_button_behavior behavior); /* this function creates a fitting button without border Input: @@ -2757,12 +3187,15 @@ struct gui_vec2 gui_shelf_end(struct gui_context*, struct gui_context*); gui_popup_menu_end -- ends the popup building process */ enum gui_popup_type { - GUI_POPUP_STATIC, /* static fixed height non growing popup */ - GUI_POPUP_DYNAMIC /* dynamically growing popup with maximum height */ + GUI_POPUP_STATIC, + /* static fixed height non growing popup */ + GUI_POPUP_DYNAMIC + /* dynamically growing popup with maximum height */ }; gui_flags gui_popup_begin(struct gui_context *parent, struct gui_context *popup, enum gui_popup_type, - gui_flags, struct gui_rect bounds, struct gui_vec2 offset); + gui_flags, struct gui_rect bounds, + struct gui_vec2 offset); /* this function adds a overlapping blocking popup menu Input: - type of the popup as either growing or static @@ -2949,6 +3382,8 @@ void gui_combo_end(struct gui_context *parent, struct gui_context *combo); be opened/closed by clicking on the menu button. It is normally placed at the top of the window and is independent of the parent scrollbar offset. But if needed the menu can even be placed inside the window. + At the moment the menu only allows a single depth but that will change + in the future. menu widget API gui_menu_begin -- begins the menu item build up processs @@ -3098,45 +3533,7 @@ enum gui_tree_node_operation gui_tree_leaf_icon(struct gui_tree*, */ struct gui_vec2 gui_tree_end(struct gui_context*, struct gui_tree*); /* this function ends a the tree building process */ -/* - * ------------------------------------------------------------- - * TABLE - * -------------------------------------------------------------- - TABLE - Temporary table widget. Needs to be rewritten to be actually useful. - Table widget API - gui_table_begin -- begin table build up process - gui_table_row -- seperates tables rows - gui_table_end -- ends the table build up process -*/ -enum gui_table_lines { - GUI_TABLE_HHEADER = 0x01, - /* Horizontal table header lines */ - GUI_TABLE_VHEADER = 0x02, - /* Vertical table header lines */ - GUI_TABLE_HBODY = 0x04, - /* Horizontal table body lines */ - GUI_TABLE_VBODY = 0x08 - /* Vertical table body lines */ -}; - -void gui_table_begin(struct gui_context*, gui_flags flags, - gui_size row_height, gui_size cols); -/* this function set the window to a table state which enable you to create a - table with the standart window row layout - Input: - - table row and column line seperator flags - - height of each table row - - number of columns inside the table -*/ -void gui_table_row(struct gui_context*); -/* this function add a row with line seperator into asa table marked table -*/ -void gui_table_end(struct gui_context*); -/* this function finished the table build up process and reverts the window back - to its normal state. -*/ #ifdef __cplusplus } #endif