summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/render.c1130
-rw-r--r--src/ui.c134
-rw-r--r--src/ui.h438
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