From 4c85a1d85a25c18d619628fe98bac32399b7192d Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Sun, 23 Feb 2020 21:19:37 +0100 Subject: ui work --- src/render.c | 1130 +++++++++++++++++++++++++++++----------------------------- src/ui.c | 134 ++++++- src/ui.h | 438 ++++++++++++----------- 3 files changed, 917 insertions(+), 785 deletions(-) diff --git a/src/render.c b/src/render.c index 11583b1..3a681d9 100644 --- a/src/render.c +++ b/src/render.c @@ -1,566 +1,566 @@ -/* -* BSD 2-Clause “Simplified” License -* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com -* All rights reserved. -*/ - -inline void render_clear() -{ - glClearColor(255/255.0, 255/255.0, 255/255.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -inline void render_set_rotation(float32 rotation, float32 x, float32 y, s32 depth) -{ - glRotatef(rotation, x, y, depth); -} - -inline void set_render_depth(s32 depth) -{ - render_depth = depth; -} - -void render_image(image *image, s32 x, s32 y, s32 width, s32 height) -{ - assert(image); - if (image->loaded) - { - glBindTexture(GL_TEXTURE_2D, image->textureID); - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - glColor4f(1., 1., 1., 1.); - glTexCoord2i(0, 0); glVertex3i(x, y, render_depth); - glTexCoord2i(0, 1); glVertex3i(x, y+height, render_depth); - glTexCoord2i(1, 1); glVertex3i(x+width, y+height, render_depth); - glTexCoord2i(1, 0); glVertex3i(x+width, y, render_depth); - glEnd(); - - glDisable(GL_TEXTURE_2D); - } -} - -void render_image_tint(image *image, s32 x, s32 y, s32 width, s32 height, color tint) -{ - assert(image); - if (image->loaded) - { - glBindTexture(GL_TEXTURE_2D, image->textureID); - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - glTexCoord2i(0, 0); glVertex3i(x, y, render_depth); - glTexCoord2i(0, 1); glVertex3i(x, y+height, render_depth); - glTexCoord2i(1, 1); glVertex3i(x+width, y+height, render_depth); - glTexCoord2i(1, 0); glVertex3i(x+width, y, render_depth); - glEnd(); - - glDisable(GL_TEXTURE_2D); - } -} - -s32 render_text_ellipsed(font *font, s32 x, s32 y, s32 maxw, char *text, color tint) -{ - if (!font->loaded) - return 0; - - glEnable(GL_TEXTURE_2D); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - - char *ellipse = "..."; - bool in_ellipse = false; - - s32 len = utf8len(text); - - s32 x_ = x; - utf8_int32_t ch; - s32 count = 0; - while((text = utf8codepoint(text, &ch)) && ch) - { - count++; - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - - glBindTexture(GL_TEXTURE_2D, g.textureID); - glBegin(GL_QUADS); - - s32 width = g.width; - - s32 y_ = y + font->px_h + g.yoff; - s32 x_to_render = x_ + (g.lsb); - - glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); - glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); - glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); - glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); - - glEnd(); - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x_ += (g.advance); - - if (!in_ellipse && (x_-x) > maxw-(font->glyphs['.'].width*3) && count < len-3) - { - in_ellipse = true; - text = ellipse; - } - } - - glDisable(GL_TEXTURE_2D); - - return maxw; -} - -s32 render_text_with_selection(font *font, s32 x, s32 y, char *text, color tint, s32 selection_start, s32 selection_length) -{ - if (!font->loaded) - return 0; - - glEnable(GL_TEXTURE_2D); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - - s32 x_ = x; - utf8_int32_t ch; - s32 index = 0; - s32 selection_start_x = x_; - s32 selection_end_x = x_; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - - glBindTexture(GL_TEXTURE_2D, g.textureID); - glBegin(GL_QUADS); - - s32 width = g.width; - - s32 y_ = y + font->px_h + g.yoff; - s32 x_to_render = x_ + (g.lsb); - - glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); - glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); - glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); - glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); - - glEnd(); - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x_ += (g.advance); - - index++; - if (index == selection_start) - { - selection_start_x = x_; - } - if (index == selection_start+selection_length) - { - selection_end_x = x_; - } - } - - glDisable(GL_TEXTURE_2D); - - render_rectangle(selection_start_x, y-3, selection_end_x-selection_start_x, TEXTBOX_HEIGHT - 10, rgba(66, 134, 244, 120)); - - return x_ - x; -} - -s32 render_text_with_cursor(font *font, s32 x, s32 y, char *text, color tint, s32 cursor_pos) -{ - if (!font->loaded) - return 0; - - glEnable(GL_TEXTURE_2D); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - - float x_ = x; - utf8_int32_t ch; - s32 index = 0; - s32 cursor_x = x_; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - - glBindTexture(GL_TEXTURE_2D, g.textureID); - glBegin(GL_QUADS); - - s32 y_ = y + font->px_h + g.yoff; - s32 x_to_render = x_ + (g.lsb); - - glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); - glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); - glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); - glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); - - glEnd(); - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x_ += (g.advance); - index++; - - if (index == cursor_pos) - { - cursor_x = x_; - } - } - glDisable(GL_TEXTURE_2D); - - render_rectangle(cursor_x, y-3, 2, TEXTBOX_HEIGHT - 10, global_ui_context.style.textbox_foreground); - - return x_ - x; -} - -s32 render_text(font *font, s32 x, s32 y, char *text, color tint) -{ - if (!font->loaded) - return 0; - - glEnable(GL_TEXTURE_2D); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - - s32 x_ = x; - utf8_int32_t ch; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - - glBindTexture(GL_TEXTURE_2D, g.textureID); - glBegin(GL_QUADS); - - s32 y_ = y + font->px_h + g.yoff; - s32 x_to_render = x_ + (g.lsb); - - glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); - glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); - glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); - glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); - - glEnd(); - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x_ += (g.advance); - } - - glDisable(GL_TEXTURE_2D); - - return x_ - x; -} - -s32 render_text_cutoff(font *font, s32 x, s32 y, char *text, color tint, u16 cutoff_width) -{ - if (!font->loaded) - return 0; - - glEnable(GL_TEXTURE_2D); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - - s32 x_ = x; - s32 y_ = y; - bool is_new_line = false; - utf8_int32_t ch; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - - if (ch == '\n') - { - x_ = x; - y_ += font->size; - is_new_line = true; - continue; - } - - if (is_new_line && ch == ' ') - { - is_new_line = false; - continue; - } - else if (is_new_line && ch != ' ') - { - is_new_line = false; - } - - - glyph g = font->glyphs[ch]; - - glBindTexture(GL_TEXTURE_2D, g.textureID); - glBegin(GL_QUADS); - - s32 y__ = y_ + font->px_h + g.yoff; - s32 x_to_render = x_ + (g.lsb); - - glTexCoord2i(0, 0); glVertex3i(x_to_render,y__, render_depth); - glTexCoord2i(0, 1); glVertex3i(x_to_render,y__+g.height, render_depth); - glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y__+g.height, render_depth); - glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y__, render_depth); - - glEnd(); - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x_ += (g.advance); - - if (x_ > x+cutoff_width) - { - x_ = x; - y_ += font->size; - is_new_line = true; - } - } - - glDisable(GL_TEXTURE_2D); - - return (y_ - y) + font->size; - -} - -s32 calculate_cursor_position(font *font, char *text, s32 click_x) -{ - if (!font->loaded) - return 0; - - s32 x = 0; - s32 index = 0; - utf8_int32_t ch; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - - s32 width = g.width; - s32 width_next = font->glyphs[ch_next].width; - - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x += (g.advance); - if (x - (width_next/5) > click_x) - { - return index; - } - - ++index; - } - - return index; -} - -s32 calculate_text_width_from_upto(font *font, char *text, s32 from, s32 index) -{ - if (!font->loaded) - return 0; - - s32 x = 0; - utf8_int32_t ch; - s32 i = 0; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (index == i) return x; - - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - s32 width = g.width; - - if (i >= from) - { - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x += (g.advance); - } - - i++; - } - - return x; -} - -s32 calculate_text_width_upto(font *font, char *text, s32 index) -{ - if (!font->loaded) - return 0; - - s32 x = 0; - utf8_int32_t ch; - s32 i = 0; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (index == i) return x; - - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - s32 width = g.width; - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x += (g.advance); - - i++; - } - - return x; -} - -s32 calculate_text_width(font *font, char *text) -{ - if (!font->loaded) - return 0; - - float x = 0; - utf8_int32_t ch; - while((text = utf8codepoint(text, &ch)) && ch) - { - if (ch == 9) ch = 32; - utf8_int32_t ch_next; - utf8codepoint(text, &ch_next); - if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) - { - ch = 0x3f; - } - if (ch == '\n') ch = 0xB6; - - glyph g = font->glyphs[ch]; - s32 width = g.width; - - /* add kerning */ - //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); - x += (g.advance); - } - - return x; -} - -void render_triangle(s32 x, s32 y, s32 w, s32 h, color tint) -{ - glBegin(GL_TRIANGLES); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - glVertex3i(x+(w/2), y+h, render_depth); - glVertex3i(x, y, render_depth); - glVertex3i(x+w, y, render_depth); - glEnd(); -} - -void render_rectangle(s32 x, s32 y, s32 width, s32 height, color tint) -{ - glBegin(GL_QUADS); - glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); - glVertex3i(x, y, render_depth); - glVertex3i(x, y+height, render_depth); - glVertex3i(x+width, y+height, render_depth); - glVertex3i(x+width, y, render_depth); - glEnd(); -} - -void render_rectangle_tint(s32 x, s32 y, s32 width, s32 height, color tint[4]) -{ - glBegin(GL_QUADS); - glColor4f(tint[0].r/255.0f, tint[0].g/255.0f, tint[0].b/255.0f, tint[0].a/255.0f); - glVertex3i(x, y, render_depth); - glColor4f(tint[1].r/255.0f, tint[1].g/255.0f, tint[1].b/255.0f, tint[1].a/255.0f); - glVertex3i(x, y+height, render_depth); - glColor4f(tint[2].r/255.0f, tint[2].g/255.0f, tint[2].b/255.0f, tint[2].a/255.0f); - glVertex3i(x+width, y+height, render_depth); - glColor4f(tint[3].r/255.0f, tint[3].g/255.0f, tint[3].b/255.0f, tint[3].a/255.0f); - glVertex3i(x+width, y, render_depth); - glEnd(); -} - -void render_rectangle_outline(s32 x, s32 y, s32 width, s32 height, u16 outline_w, color tint) -{ - // left - render_rectangle(x, y, outline_w, height, tint); - // right - render_rectangle(x+width-outline_w, y, outline_w, height, tint); - // top - render_rectangle(x+outline_w, y, width-(outline_w*2), outline_w, tint); - // bottom - render_rectangle(x+outline_w, y+height-outline_w, width-(outline_w*2), outline_w, tint); -} - -void render_set_scissor(platform_window *window, s32 x, s32 y, s32 w, s32 h) -{ - glEnable(GL_SCISSOR_TEST); - glScissor(x-1, window->height-h-y-1, w+1, h+1); -} - -vec4 render_get_scissor() -{ - vec4 vec; - glGetIntegerv(GL_SCISSOR_BOX, (GLint*)(&vec)); - vec.x += 1; - vec.y += 1; - vec.w -= 1; - vec.h -= 1; - return vec; -} - -void render_reset_scissor() -{ - glDisable(GL_SCISSOR_TEST); +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +inline void render_clear() +{ + glClearColor(255/255.0, 255/255.0, 255/255.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +inline void render_set_rotation(float32 rotation, float32 x, float32 y, s32 depth) +{ + glRotatef(rotation, x, y, depth); +} + +inline void set_render_depth(s32 depth) +{ + render_depth = depth; +} + +void render_image(image *image, s32 x, s32 y, s32 width, s32 height) +{ + assert(image); + if (image->loaded) + { + glBindTexture(GL_TEXTURE_2D, image->textureID); + glEnable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + glColor4f(1., 1., 1., 1.); + glTexCoord2i(0, 0); glVertex3i(x, y, render_depth); + glTexCoord2i(0, 1); glVertex3i(x, y+height, render_depth); + glTexCoord2i(1, 1); glVertex3i(x+width, y+height, render_depth); + glTexCoord2i(1, 0); glVertex3i(x+width, y, render_depth); + glEnd(); + + glDisable(GL_TEXTURE_2D); + } +} + +void render_image_tint(image *image, s32 x, s32 y, s32 width, s32 height, color tint) +{ + assert(image); + if (image->loaded) + { + glBindTexture(GL_TEXTURE_2D, image->textureID); + glEnable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + glTexCoord2i(0, 0); glVertex3i(x, y, render_depth); + glTexCoord2i(0, 1); glVertex3i(x, y+height, render_depth); + glTexCoord2i(1, 1); glVertex3i(x+width, y+height, render_depth); + glTexCoord2i(1, 0); glVertex3i(x+width, y, render_depth); + glEnd(); + + glDisable(GL_TEXTURE_2D); + } +} + +s32 render_text_ellipsed(font *font, s32 x, s32 y, s32 maxw, char *text, color tint) +{ + if (!font->loaded) + return 0; + + glEnable(GL_TEXTURE_2D); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + + char *ellipse = "..."; + bool in_ellipse = false; + + s32 len = utf8len(text); + + s32 x_ = x; + utf8_int32_t ch; + s32 count = 0; + while((text = utf8codepoint(text, &ch)) && ch) + { + count++; + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + + glBindTexture(GL_TEXTURE_2D, g.textureID); + glBegin(GL_QUADS); + + s32 width = g.width; + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); + glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); + glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); + glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); + + glEnd(); + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + + if (!in_ellipse && (x_-x) > maxw-(font->glyphs['.'].width*3) && count < len-3) + { + in_ellipse = true; + text = ellipse; + } + } + + glDisable(GL_TEXTURE_2D); + + return maxw; +} + +s32 render_text_with_selection(font *font, s32 x, s32 y, char *text, color tint, s32 selection_start, s32 selection_length) +{ + if (!font->loaded) + return 0; + + glEnable(GL_TEXTURE_2D); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + + s32 x_ = x; + utf8_int32_t ch; + s32 index = 0; + s32 selection_start_x = x_; + s32 selection_end_x = x_; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + + glBindTexture(GL_TEXTURE_2D, g.textureID); + glBegin(GL_QUADS); + + s32 width = g.width; + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); + glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); + glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); + glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); + + glEnd(); + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + + index++; + if (index == selection_start) + { + selection_start_x = x_; + } + if (index == selection_start+selection_length) + { + selection_end_x = x_; + } + } + + glDisable(GL_TEXTURE_2D); + + render_rectangle(selection_start_x, y-3, selection_end_x-selection_start_x, TEXTBOX_HEIGHT - 10, rgba(66, 134, 244, 120)); + + return x_ - x; +} + +s32 render_text_with_cursor(font *font, s32 x, s32 y, char *text, color tint, s32 cursor_pos) +{ + if (!font->loaded) + return 0; + + glEnable(GL_TEXTURE_2D); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + + float x_ = x; + utf8_int32_t ch; + s32 index = 0; + s32 cursor_x = x_; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + + glBindTexture(GL_TEXTURE_2D, g.textureID); + glBegin(GL_QUADS); + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); + glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); + glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); + glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); + + glEnd(); + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + index++; + + if (index == cursor_pos) + { + cursor_x = x_; + } + } + glDisable(GL_TEXTURE_2D); + + render_rectangle(cursor_x, y-3, 2, TEXTBOX_HEIGHT - 10, global_ui_context.style.textbox_foreground); + + return x_ - x; +} + +s32 render_text(font *font, s32 x, s32 y, char *text, color tint) +{ + if (!font->loaded) + return 0; + + glEnable(GL_TEXTURE_2D); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + + s32 x_ = x; + utf8_int32_t ch; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + + glBindTexture(GL_TEXTURE_2D, g.textureID); + glBegin(GL_QUADS); + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth); + glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth); + glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth); + glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth); + + glEnd(); + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + } + + glDisable(GL_TEXTURE_2D); + + return x_ - x; +} + +s32 render_text_cutoff(font *font, s32 x, s32 y, char *text, color tint, u16 cutoff_width) +{ + if (!font->loaded) + return 0; + + glEnable(GL_TEXTURE_2D); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + + s32 x_ = x; + s32 y_ = y; + bool is_new_line = false; + utf8_int32_t ch; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + + if (ch == '\n') + { + x_ = x; + y_ += font->size; + is_new_line = true; + continue; + } + + if (is_new_line && ch == ' ') + { + is_new_line = false; + continue; + } + else if (is_new_line && ch != ' ') + { + is_new_line = false; + } + + + glyph g = font->glyphs[ch]; + + glBindTexture(GL_TEXTURE_2D, g.textureID); + glBegin(GL_QUADS); + + s32 y__ = y_ + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + glTexCoord2i(0, 0); glVertex3i(x_to_render,y__, render_depth); + glTexCoord2i(0, 1); glVertex3i(x_to_render,y__+g.height, render_depth); + glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y__+g.height, render_depth); + glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y__, render_depth); + + glEnd(); + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + + if (x_ > x+cutoff_width) + { + x_ = x; + y_ += font->size; + is_new_line = true; + } + } + + glDisable(GL_TEXTURE_2D); + + return (y_ - y) + font->size; + +} + +s32 calculate_cursor_position(font *font, char *text, s32 click_x) +{ + if (!font->loaded) + return 0; + + s32 x = 0; + s32 index = 0; + utf8_int32_t ch; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + + s32 width = g.width; + s32 width_next = font->glyphs[ch_next].width; + + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + if (x - (width_next/5) > click_x) + { + return index; + } + + ++index; + } + + return index; +} + +s32 calculate_text_width_from_upto(font *font, char *text, s32 from, s32 index) +{ + if (!font->loaded) + return 0; + + s32 x = 0; + utf8_int32_t ch; + s32 i = 0; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (index == i) return x; + + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + s32 width = g.width; + + if (i >= from) + { + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + } + + i++; + } + + return x; +} + +s32 calculate_text_width_upto(font *font, char *text, s32 index) +{ + if (!font->loaded) + return 0; + + s32 x = 0; + utf8_int32_t ch; + s32 i = 0; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (index == i) return x; + + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + s32 width = g.width; + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + + i++; + } + + return x; +} + +s32 calculate_text_width(font *font, char *text) +{ + if (!font->loaded) + return 0; + + float x = 0; + utf8_int32_t ch; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') ch = 0xB6; + + glyph g = font->glyphs[ch]; + s32 width = g.width; + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + } + + return x; +} + +void render_triangle(s32 x, s32 y, s32 w, s32 h, color tint) +{ + glBegin(GL_TRIANGLES); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + glVertex3i(x+(w/2), y+h, render_depth); + glVertex3i(x, y, render_depth); + glVertex3i(x+w, y, render_depth); + glEnd(); +} + +void render_rectangle(s32 x, s32 y, s32 width, s32 height, color tint) +{ + glBegin(GL_QUADS); + glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f); + glVertex3i(x, y, render_depth); + glVertex3i(x, y+height, render_depth); + glVertex3i(x+width, y+height, render_depth); + glVertex3i(x+width, y, render_depth); + glEnd(); +} + +void render_rectangle_tint(s32 x, s32 y, s32 width, s32 height, color tint[4]) +{ + glBegin(GL_QUADS); + glColor4f(tint[0].r/255.0f, tint[0].g/255.0f, tint[0].b/255.0f, tint[0].a/255.0f); + glVertex3i(x, y, render_depth); + glColor4f(tint[1].r/255.0f, tint[1].g/255.0f, tint[1].b/255.0f, tint[1].a/255.0f); + glVertex3i(x, y+height, render_depth); + glColor4f(tint[2].r/255.0f, tint[2].g/255.0f, tint[2].b/255.0f, tint[2].a/255.0f); + glVertex3i(x+width, y+height, render_depth); + glColor4f(tint[3].r/255.0f, tint[3].g/255.0f, tint[3].b/255.0f, tint[3].a/255.0f); + glVertex3i(x+width, y, render_depth); + glEnd(); +} + +void render_rectangle_outline(s32 x, s32 y, s32 width, s32 height, u16 outline_w, color tint) +{ + // left + render_rectangle(x, y, outline_w, height, tint); + // right + render_rectangle(x+width-outline_w, y, outline_w, height, tint); + // top + render_rectangle(x+outline_w, y, width-(outline_w*2), outline_w, tint); + // bottom + render_rectangle(x+outline_w, y+height-outline_w, width-(outline_w*2), outline_w, tint); +} + +void render_set_scissor(platform_window *window, s32 x, s32 y, s32 w, s32 h) +{ + glEnable(GL_SCISSOR_TEST); + glScissor(x-1, window->height-h-y-1, w+1, h+1); +} + +vec4 render_get_scissor() +{ + vec4 vec; + glGetIntegerv(GL_SCISSOR_BOX, (GLint*)(&vec)); + vec.x += 1; + vec.y += 1; + vec.w -= 1; + vec.h -= 1; + return vec; +} + +void render_reset_scissor() +{ + glDisable(GL_SCISSOR_TEST); } \ No newline at end of file diff --git a/src/ui.c b/src/ui.c index d11d3a7..ae87c8e 100644 --- a/src/ui.c +++ b/src/ui.c @@ -13,6 +13,7 @@ inline void ui_begin(s32 id) global_ui_context.layout.width = global_ui_context.layout.active_window->width; global_ui_context.layout.height = global_ui_context.layout.active_window->height; global_ui_context.layout.active_dropdown_state = 0; + global_ui_context.submenus.count = 0; } inline void ui_end() @@ -20,6 +21,17 @@ inline void ui_end() } +inline submenu_state ui_create_submenu() +{ + submenu_state state; + state.open = false; + state.hovered = false; + state.item_count = 0; + state.w = MENU_ITEM_WIDTH; + + return state; +} + inline checkbox_state ui_create_checkbox(bool selected) { checkbox_state state; @@ -1239,33 +1251,132 @@ inline bool is_shortcut_down(s32 shortcut_keys[2]) keyboard_is_key_pressed(global_ui_context.keyboard, shortcut_keys[1]); } +void ui_begin_menu_submenu(submenu_state *state, char *title) +{ + bool result = ui_push_menu_item(title, ""); + + s32 w = state->w; + s32 h = MENU_BAR_HEIGHT; + s32 x = global_ui_context.layout.prev_offset_x + global_ui_context.camera->x; + s32 y = global_ui_context.layout.offset_y + (global_ui_context.menu_item_count * h)+1 + + global_ui_context.layout.menu_offset_y + global_ui_context.camera->y; + s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x; + s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y; + + state->x = x + MENU_ITEM_WIDTH; + state->y = y; + state->item_count = 0; + + if ((mouse_x >= x && mouse_x < x + MENU_ITEM_WIDTH && mouse_y >= y && mouse_y < y + h)) + { + state->hovered = true; + state->open = true; + } + else + { + state->hovered = false; + } + + assert(global_ui_context.submenus.count <= 4); + global_ui_context.submenus.submenu_stack[global_ui_context.submenus.count++] = state; + + if (result) state->open = false; +} + +void ui_end_menu_submenu() +{ + set_render_depth(30); + + submenu_state *state = global_ui_context.submenus.submenu_stack[global_ui_context.submenus.count-1]; + + s32 w = state->w; + s32 h = MENU_BAR_HEIGHT; + s32 total_h = state->item_count*h; + s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x; + s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y; + + if (state->open) + { + if (!(mouse_x >= state->x && mouse_x < state->x + w && mouse_y >= state->y && mouse_y < state->y + total_h) && !state->hovered) + { + state->open = false; + } + + if (!state->item_count) + { + s32 text_h = global_ui_context.font_small->px_h; + s32 text_y = state->y - (text_h / 2) + (h / 2); + s32 text_x = state->x + MENU_HORIZONTAL_PADDING; + + total_h = h; + render_rectangle(state->x, state->y, w, total_h, global_ui_context.style.widget_background); + render_rectangle_outline(state->x, state->y, w, total_h, 1, global_ui_context.style.border); + + // TODO(Aldrik): localize + render_text(global_ui_context.font_small, text_x, text_y, "No recent projects.", global_ui_context.style.foreground); + } + else + { + render_rectangle(state->x, state->y, w, 1, global_ui_context.style.border); + } + } + + + global_ui_context.submenus.count--; + + set_render_depth(1); +} + bool ui_push_menu_item(char *title, char *shortcut) { bool result = false; set_render_depth(30); - global_ui_context.menu_item_count++; - s32 x = global_ui_context.layout.prev_offset_x + global_ui_context.camera->x; s32 w = MENU_ITEM_WIDTH; s32 text_h = global_ui_context.font_small->px_h; s32 h = MENU_BAR_HEIGHT; - s32 y = global_ui_context.layout.offset_y + (global_ui_context.menu_item_count * h)+1 + + s32 y = global_ui_context.layout.offset_y + ((global_ui_context.menu_item_count+1) * h)+1 + global_ui_context.layout.menu_offset_y + global_ui_context.camera->y; - s32 text_y = y - (text_h / 2) + (h / 2); - s32 text_x = x + MENU_HORIZONTAL_PADDING; - s32 text_2_x = x + w - MENU_HORIZONTAL_PADDING - - calculate_text_width(global_ui_context.font_small, shortcut); s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x; s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y; - if (global_ui_context.layout.block_height < h) - global_ui_context.layout.block_height = h; - color bg_color = global_ui_context.style.menu_background; + s32 text_y = 0; + s32 text_x = 0; + s32 text_2_x = 0; + + submenu_state *state = 0; + if (global_ui_context.submenus.count) + { + state = global_ui_context.submenus.submenu_stack[global_ui_context.submenus.count-1]; + w = state->w; + + if (!state->open) return false; + + x = state->x; + y = state->y + (state->item_count*h); + + text_y = y - (text_h / 2) + (h / 2); + text_x = x + MENU_HORIZONTAL_PADDING; + text_2_x = x + w - MENU_HORIZONTAL_PADDING + - calculate_text_width(global_ui_context.font_small, shortcut); + + state->item_count++; + } + else + { + global_ui_context.menu_item_count++; + + text_y = y - (text_h / 2) + (h / 2); + text_x = x + MENU_HORIZONTAL_PADDING; + text_2_x = x + w - MENU_HORIZONTAL_PADDING + - calculate_text_width(global_ui_context.font_small, shortcut); + } + if ((mouse_x >= x && mouse_x < x + w && mouse_y >= y && mouse_y < y + h)) { platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER); @@ -1274,6 +1385,7 @@ bool ui_push_menu_item(char *title, char *shortcut) if (is_left_clicked(global_ui_context.mouse)) { + if (state) state->open = false; global_ui_context.mouse->left_state &= ~MOUSE_CLICK; result = true; } @@ -1677,7 +1789,7 @@ void ui_scroll_begin(scroll_state *state) void ui_scroll_end() { s32 max_scroll = (global_ui_context.layout.scroll->scroll_start_offset_y - - global_ui_context.layout.offset_y) + global_ui_context.layout.scroll->height; + global_ui_context.layout.offset_y) + global_ui_context.layout.scroll->height - WIDGET_PADDING; //global_ui_context.layout.offset_x -= WIDGET_PADDING; global_ui_context.layout.offset_y = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height; diff --git a/src/ui.h b/src/ui.h index bffbe69..d318af0 100644 --- a/src/ui.h +++ b/src/ui.h @@ -1,210 +1,230 @@ -/* -* BSD 2-Clause “Simplified” License -* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com -* All rights reserved. -*/ - -#ifndef INCLUDE_UI -#define INCLUDE_UI - -#define BLOCK_HEIGHT 25 -#define MENU_BAR_HEIGHT 25 -#define MENU_HORIZONTAL_PADDING 10 -#define WIDGET_PADDING 8 -#define BUTTON_HORIZONTAL_TEXT_PADDING 15 -#define MENU_ITEM_WIDTH 220 -#define CHECKBOX_SIZE BLOCK_HEIGHT - 8 - -static s32 TEXTBOX_WIDTH = 280; - -#define TEXTBOX_HEIGHT BLOCK_HEIGHT -#define BUTTON_HEIGHT BLOCK_HEIGHT -#define BUTTON_IMAGE_PADDING 5 -#define BUTTON_IMAGE_SPACING 8 -#define DROPDOWN_WIDTH 225 -#define DROPDOWN_ITEM_WIDTH 225 -#define TEXTBOX_SCROLL_X_SPEED 32 - -typedef enum t_ui_style_type -{ - UI_STYLE_LIGHT = 1, - UI_STYLE_DARK = 2, -} ui_style_type; - -typedef struct t_ui_style -{ - u16 id; - color foreground; - color background; - color border; - color textbox_background; - color textbox_active_border; - color textbox_foreground; - color image_outline_tint; - color scrollbar_handle_background; - color info_bar_background; - color error_foreground; - color item_hover_background; - color scrollbar_background; - color menu_background; - color menu_hover_background; - color menu_foreground; - color widget_hover_background; - color widget_background; - color widget_confirm_background; - color widget_confirm_hover_background; - color hypertext_foreground; - color hypertext_hover_foreground; - color textbox_placeholder_foreground; - color widget_confirm_border; -} ui_style; - -typedef enum t_layout_direction -{ - LAYOUT_HORIZONTAL, - LAYOUT_VERTICAL, -} layout_direction; - -typedef struct t_dropdown_state -{ - bool state; - int selected_index; -} dropdown_state; - -typedef struct t_scroll_state -{ - s32 height; - s32 width; - s32 x; - s32 y; - s32 scroll; - s32 scroll_start_offset_y; - bool in_scroll; - bool mouse_scrolling; -} scroll_state; - -typedef struct t_ui_layout -{ - s32 dropdown_item_count; - s32 dropdown_x; - s32 offset_x; - s32 offset_y; - platform_window *active_window; - layout_direction layout_direction; - s32 prev_offset_x; - s32 width; - s32 height; - s32 menu_offset_y; - s32 block_height; - s32 start_offset_y; - s32 start_offset_x; - scroll_state *scroll; - s32 padding; - dropdown_state *active_dropdown_state; -} ui_layout; - -typedef struct t_textbox_history_entry -{ - char *text; - s32 cursor_offset; -} textbox_history_entry; - -typedef struct t_textbox_state -{ - bool deselect_on_enter; - bool accept_newline; - char *buffer; - s32 selection_start_index; - bool state; - s32 diff; - bool double_clicked_to_select; - s32 double_clicked_to_select_cursor_index; - s32 max_len; - s32 text_offset_x; - bool attempting_to_select; - array history; - array future; - s32 last_click_cursor_index; -} textbox_state; - -typedef struct t_checkbox_state -{ - bool state; -} checkbox_state; - -typedef struct t_button_state -{ - bool state; -} button_state; - -typedef struct t_ui_context -{ - ui_style style; - ui_layout layout; - keyboard_input *keyboard; - mouse_input *mouse; - camera *camera; - font *font_small; - array active_menus; - u32 next_id; - s32 menu_item_count; - dropdown_state *active_dropdown; - u32 confirming_button_id; - textbox_state *current_active_textbox; - bool item_hovered; -} ui_context; - -ui_context global_ui_context; - -u32 ui_get_id(); -void ui_create(platform_window *window, keyboard_input *keyboard, mouse_input *mouse, camera *camera, font *font_small); -void ui_set_active_window(platform_window *window); -void ui_destroy(); -void ui_begin(); -void ui_end(); -bool ui_is_menu_active(u32 id); -char* name_of_day(s32 day); -char* name_of_month(s32 month); -void ui_set_style(u16 style); -void set_active_textbox(textbox_state *textbox); -void ui_set_textbox_text(textbox_state *textbox, char *text); -void ui_set_textbox_active(textbox_state *textbox); - -// widget initialization -checkbox_state ui_create_checkbox(bool selected); -textbox_state ui_create_textbox(u16 max_len); -button_state ui_create_button(); -scroll_state ui_create_scroll(s32 scroll); -dropdown_state ui_create_dropdown(); - -void ui_destroy_textbox(textbox_state *state); - -// widgets -bool is_shortcut_down(s32 shortcut_keys[2]); -void ui_begin_menu_bar(); -bool ui_push_menu(char *title); -bool ui_push_menu_item(char *title, char *shortcut); -void ui_push_menu_item_separator(); -bool ui_push_dropdown(dropdown_state *state, char *title); -bool ui_push_dropdown_item(image *icon, char *title, s32 index); -void ui_push_separator(); -void ui_push_rect(s32 w, color rec); -void ui_push_vertical_dragbar(); -void ui_block_begin(layout_direction direction); -void ui_block_end(); -void ui_end_menu_bar(); -void ui_push_text(char *text); -bool ui_push_text_width(char *text, s32 maxw, bool active); -void ui_push_textf(font *f, char *text); -void ui_push_textf_width(font *f, char *text, s32 maxw); -bool ui_push_hypertext_link(char *text); -bool ui_push_color_button(char *text, bool selected, color color); -bool ui_push_image(image *img, s32 w, s32 h, s32 outline, color tint); -bool ui_push_checkbox(checkbox_state *state, char *title); -bool ui_push_textbox(textbox_state *state, char *title); -bool ui_push_button(button_state *button, char *title); -bool ui_push_button_image(button_state *button, char *title, image *img); -void ui_scroll_begin(scroll_state *state); -void ui_scroll_end(); - +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#ifndef INCLUDE_UI +#define INCLUDE_UI + +#define BLOCK_HEIGHT 25 +#define MENU_BAR_HEIGHT 25 +#define MENU_HORIZONTAL_PADDING 10 +#define WIDGET_PADDING 8 +#define BUTTON_HORIZONTAL_TEXT_PADDING 15 +#define MENU_ITEM_WIDTH 220 +#define CHECKBOX_SIZE BLOCK_HEIGHT - 8 + +static s32 TEXTBOX_WIDTH = 280; + +#define TEXTBOX_HEIGHT BLOCK_HEIGHT +#define BUTTON_HEIGHT BLOCK_HEIGHT +#define BUTTON_IMAGE_PADDING 5 +#define BUTTON_IMAGE_SPACING 8 +#define DROPDOWN_WIDTH 225 +#define DROPDOWN_ITEM_WIDTH 225 +#define TEXTBOX_SCROLL_X_SPEED 32 + +typedef enum t_ui_style_type +{ + UI_STYLE_LIGHT = 1, + UI_STYLE_DARK = 2, +} ui_style_type; + +typedef struct t_ui_style +{ + u16 id; + color foreground; + color background; + color border; + color textbox_background; + color textbox_active_border; + color textbox_foreground; + color image_outline_tint; + color scrollbar_handle_background; + color info_bar_background; + color error_foreground; + color item_hover_background; + color scrollbar_background; + color menu_background; + color menu_hover_background; + color menu_foreground; + color widget_hover_background; + color widget_background; + color widget_confirm_background; + color widget_confirm_hover_background; + color hypertext_foreground; + color hypertext_hover_foreground; + color textbox_placeholder_foreground; + color widget_confirm_border; +} ui_style; + +typedef enum t_layout_direction +{ + LAYOUT_HORIZONTAL, + LAYOUT_VERTICAL, +} layout_direction; + +typedef struct t_dropdown_state +{ + bool state; + int selected_index; +} dropdown_state; + +typedef struct t_scroll_state +{ + s32 height; + s32 width; + s32 x; + s32 y; + s32 scroll; + s32 scroll_start_offset_y; + bool in_scroll; + bool mouse_scrolling; +} scroll_state; + +typedef struct t_ui_layout +{ + s32 dropdown_item_count; + s32 dropdown_x; + s32 offset_x; + s32 offset_y; + platform_window *active_window; + layout_direction layout_direction; + s32 prev_offset_x; + s32 width; + s32 height; + s32 menu_offset_y; + s32 block_height; + s32 start_offset_y; + s32 start_offset_x; + scroll_state *scroll; + s32 padding; + dropdown_state *active_dropdown_state; +} ui_layout; + +typedef struct t_textbox_history_entry +{ + char *text; + s32 cursor_offset; +} textbox_history_entry; + +typedef struct t_textbox_state +{ + bool deselect_on_enter; + bool accept_newline; + char *buffer; + s32 selection_start_index; + bool state; + s32 diff; + bool double_clicked_to_select; + s32 double_clicked_to_select_cursor_index; + s32 max_len; + s32 text_offset_x; + bool attempting_to_select; + array history; + array future; + s32 last_click_cursor_index; +} textbox_state; + +typedef struct t_checkbox_state +{ + bool state; +} checkbox_state; + +typedef struct t_button_state +{ + bool state; +} button_state; + +typedef struct t_submenu_state +{ + bool open; + bool hovered; + s32 item_count; + s32 w; + s32 x; + s32 y; +} submenu_state; + +typedef struct t_submenus +{ + s32 count; + submenu_state *submenu_stack[5]; +} submenus; + +typedef struct t_ui_context +{ + ui_style style; + ui_layout layout; + keyboard_input *keyboard; + mouse_input *mouse; + camera *camera; + font *font_small; + array active_menus; + u32 next_id; + s32 menu_item_count; + dropdown_state *active_dropdown; + u32 confirming_button_id; + textbox_state *current_active_textbox; + submenus submenus; + bool item_hovered; +} ui_context; + +ui_context global_ui_context; + +u32 ui_get_id(); +void ui_create(platform_window *window, keyboard_input *keyboard, mouse_input *mouse, camera *camera, font *font_small); +void ui_set_active_window(platform_window *window); +void ui_destroy(); +void ui_begin(); +void ui_end(); +bool ui_is_menu_active(u32 id); +char* name_of_day(s32 day); +char* name_of_month(s32 month); +void ui_set_style(u16 style); +void set_active_textbox(textbox_state *textbox); +void ui_set_textbox_text(textbox_state *textbox, char *text); +void ui_set_textbox_active(textbox_state *textbox); + +// widget initialization +checkbox_state ui_create_checkbox(bool selected); +textbox_state ui_create_textbox(u16 max_len); +button_state ui_create_button(); +scroll_state ui_create_scroll(s32 scroll); +dropdown_state ui_create_dropdown(); +submenu_state ui_create_submenu(); + +void ui_destroy_textbox(textbox_state *state); + +// widgets +bool is_shortcut_down(s32 shortcut_keys[2]); +void ui_begin_menu_bar(); +bool ui_push_menu(char *title); +bool ui_push_menu_item(char *title, char *shortcut); +void ui_begin_menu_submenu(submenu_state *state, char *title); +void ui_end_menu_submenu(); +void ui_push_menu_item_separator(); +void ui_end_menu_bar(); +bool ui_push_dropdown(dropdown_state *state, char *title); +bool ui_push_dropdown_item(image *icon, char *title, s32 index); +void ui_push_separator(); +void ui_push_rect(s32 w, color rec); +void ui_push_vertical_dragbar(); +void ui_block_begin(layout_direction direction); +void ui_block_end(); +void ui_push_text(char *text); +bool ui_push_text_width(char *text, s32 maxw, bool active); +void ui_push_textf(font *f, char *text); +void ui_push_textf_width(font *f, char *text, s32 maxw); +bool ui_push_hypertext_link(char *text); +bool ui_push_color_button(char *text, bool selected, color color); +bool ui_push_image(image *img, s32 w, s32 h, s32 outline, color tint); +bool ui_push_checkbox(checkbox_state *state, char *title); +bool ui_push_textbox(textbox_state *state, char *title); +bool ui_push_button(button_state *button, char *title); +bool ui_push_button_image(button_state *button, char *title, image *img); +void ui_scroll_begin(scroll_state *state); +void ui_scroll_end(); + #endif \ No newline at end of file -- cgit v1.2.3-70-g09d2