bad char
[vg.git] / vg_imgui.h
index d843cdc6cfadfedc99bebf5da1201aaedd8c0120..8ec033009859b9ac44fe180a945a44eb27fcaeeb 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */
+/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
 
 /*
  *  Principles:
@@ -6,20 +6,25 @@
  *    1. layout is defined by subdividing
  *    2. a parent node should never be resized by the content after creation
  *    3. when the ui is in an interactive state, no controls should ever move
+ *    4. controls directly reference a memory location and use that as their
+ *       unique id
+ *    5. a maximum of ONE control per memory location can be drawn at any given
+ *       point.
  */
 
+#pragma once
 
-#ifndef VG_IMGUI_H
-#define VG_IMGUI_H
+#include "vg_engine.h"
+#include "vg_tex.h"
+#include "vg_shader.h"
+#include "vg_font.h"
 
-#define VG_GAME
-#include "vg/vg.h"
-#include "vg/vg_tex.h"
-#include "vg/vg_shader.h"
+extern vg_font_sheet vg_default_font_sheet;
+extern vg_font_face vgf_default_small, vgf_default_large, vgf_default_title;
 
 typedef i16                            ui_px;
-typedef u32                            ui_colour;
 typedef ui_px                          ui_rect[4];
+typedef ui_px           ui_point[2];
 typedef struct ui_vert  ui_vert;
 
 enum ui_axis {
@@ -30,6 +35,7 @@ enum ui_axis {
 /* Relative to cursor p0 */
 enum ui_align
 {  /*                         DC      BA */
+   k_ui_align_lwr           =         0xff,
    k_ui_align_left          = 0x0000| 0x00,
    k_ui_align_right         = 0x0000| 0x01,
    k_ui_align_center        = 0x0000| 0x02,
@@ -49,784 +55,264 @@ enum ui_align
 struct ui_vert
 {
    ui_px co[2];
-   u8 uv[2];
+   u16 uv[2];
    u32 colour;
 };
 #pragma pack(pop)
 
-struct
-{
-   struct ui_vert *vertex_buffer;
-   u16            *indice_buffer;
-       u32 max_verts, max_indices, 
-       cur_vert,  cur_indice,
-       vert_start, indice_start;
-       
-       GLuint tex_glyphs, vao, vbo, ebo;
-
-       ui_px mouse[2], mouse_click[2];
-   u32 mouse_state[2];
-   u32 ignore_input_frames;
-
-   ui_rect click_fader;
-   float click_fade_opacity;
-} 
-
-static vg_uictx;
-static struct vg_shader _shader_ui =
-{
-   .name = "[vg] ui",
-   .link = NULL,
-   .vs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "layout (location=0) in vec2 a_co;"
-       "layout (location=1) in vec2 a_uv;"
-       "layout (location=2) in vec4 a_colour;"
-       "uniform mat3 uPv;"
-       ""
-       "out vec2 aTexCoords;"
-       "out vec4 aColour;"
-       "out vec2 aWsp;"
-       ""
-       "void main()"
-       "{"
-               "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
-               "aTexCoords = a_uv * 0.0078125;"
-               "aColour = a_colour;"
-               
-               "aWsp = a_co;"
-       "}",
-   },
-   .fs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "uniform sampler2D uTexGlyphs;"
-       "out vec4 FragColor;"
-       ""
-       "in vec2 aTexCoords;"
-       "in vec4 aColour;"
-       ""
-       "in vec2 aWsp;"
-       ""
-       "void main()"
-       "{"
-
-               "vec4 glyph = vec4(1.0,1.0,1.0,1.0);"
-               
-               "if( aColour.a == 0.0 )"
-               "{"
-                       "glyph = texture( uTexGlyphs, aTexCoords );"
-                       "glyph.a = smoothstep( 0.47, 0.53, glyph.r );"
-               "}"
-               "else"
-               "{"
-                       "glyph.a = aColour.a;"
-               "}"
-               
-               "FragColor = vec4( aColour.rgb, glyph.a );"
-       "}"
-   }
+enum ui_scheme_colour{
+   k_ui_bg      = 0,
+   k_ui_fg      = 8,
+   k_ui_hue     = 16,
+   k_ui_red     = 16,
+   k_ui_orange,
+   k_ui_yellow,
+   k_ui_green,
+   k_ui_aqua,
+   k_ui_blue,
+   k_ui_purple,
+   k_ui_gray,
+   k_ui_brighter = 8
 };
 
-static struct vg_shader _shader_ui_image =
-{
-   .name = "[vg] ui_image",
-   .link = NULL,
-   .vs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "layout (location=0) in vec2 a_co;"
-       "layout (location=1) in vec2 a_uv;"
-       "layout (location=2) in vec4 a_colour;"
-       "uniform mat3 uPv;"
-
-       "out vec2 aTexCoords;"
-       "out vec4 aColour;"
-       "out vec2 aWsp;"
-
-       "void main()"
-       "{"
-               "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
-               "aTexCoords = a_uv * 0.0078125;"
-               "aColour = a_colour;"
-               
-               "aWsp = a_co;"
-       "}",
-   },
-   .fs = 
-   {
-      .orig_file = NULL,
-      .static_src = 
-       "uniform sampler2D uTexImage;"
-       "out vec4 FragColor;"
-
-       "in vec2 aTexCoords;"
-       "in vec4 aColour;"
-       "in vec2 aWsp;"
-
-       "void main()"
-       "{"
-               "vec4 colour = texture( uTexImage, aTexCoords );"
-
-      /* wtf is this?? */
-#if 0
-      "float value = dot(vec4(1.0),colour)*0.25;"
-
-      "vec3 col = vec3(pow(cos(value*3.14159265*2.0)*0.5+0.5,0.5))"
-                  "* vec3(step(value,0.5),0.3,step(1.0-value,0.5));"
-               "FragColor = vec4( col*4.0, 1.0 );"
-#endif
-
-               "FragColor = colour;"
-       "}"
-   }
-};
-#define UI_GLYPH_SPACING_X 8
-
-VG_STATIC void _vg_ui_init(void)
-{
-   if( !vg_shader_compile( &_shader_ui ) ||
-       !vg_shader_compile( &_shader_ui_image ) ) 
-      vg_fatal_error( "Failed to compile ui shader" );
-
-   /*
-    * Vertex buffer
-    * ----------------------------------------
-    */
-
-   vg_uictx.max_indices = 20000;
-   vg_uictx.max_verts = 30000;
-       
-       /* Generate the buffer we are gonna be drawing to */
-   glGenVertexArrays( 1, &vg_uictx.vao );
-   glGenBuffers( 1, &vg_uictx.vbo );
-   glGenBuffers( 1, &vg_uictx.ebo );
-
-   glBindVertexArray( vg_uictx.vao );
-   glBindBuffer( GL_ARRAY_BUFFER, vg_uictx.vbo );
-   
-   glBufferData( GL_ARRAY_BUFFER, 
-         vg_uictx.max_verts * sizeof( struct ui_vert ), 
-         NULL, GL_DYNAMIC_DRAW );
-   glBindVertexArray( vg_uictx.vao );
-   
-   glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_uictx.ebo );
-   glBufferData( GL_ELEMENT_ARRAY_BUFFER, 
-         vg_uictx.max_indices * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
-
-   VG_CHECK_GL_ERR();
-
-   /* Set pointers */
-   u32 const stride = sizeof( struct ui_vert );
-   
-   /* XY */
-   glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, stride, 
-                          (void *)offsetof( struct ui_vert, co ) );
-   glEnableVertexAttribArray( 0 );
-   
-   /* UV */
-   glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, stride, 
-                          (void *)offsetof( struct ui_vert, uv ) );
-   glEnableVertexAttribArray( 1 );
-   
-   /* COLOUR */
-   glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, 
-                          (void *)offsetof( struct ui_vert, colour ) );
-   glEnableVertexAttribArray( 2 );
-   
-   VG_CHECK_GL_ERR();
-
-       /* Alloc RAM default context */
-   u32 vert_size = vg_uictx.max_verts*sizeof(struct ui_vert),
-       inds_size = vg_align8( vg_uictx.max_indices*sizeof(u16) );
-   
-   vg_uictx.vertex_buffer = vg_linear_alloc( vg_mem.rtmemory, vert_size );
-   vg_uictx.indice_buffer = vg_linear_alloc( vg_mem.rtmemory, inds_size );
-
-   /* font
-    * -----------------------------------------------------
-    */
-   
-       /* Load default font */
-   u32 compressed[] = {
-      #include "vg/vg_pxfont_thin.h"
-   };
-
-   u32 pixels = 0, total = 256*256, data = 0;
-   u8 image[256*256];
-   
-   while( pixels < total ){
-      for( int b = 31; b >= 0; b-- ){
-         image[ pixels ++ ] = (compressed[data] & (0x1u << b))? 0xffu: 0x00u;
-         
-         if( pixels >= total ){
-            total = 0;
-            break;
-         }
-      }
-      data++;
-   }
-   
-   glGenTextures( 1, &vg_uictx.tex_glyphs );
-   glBindTexture( GL_TEXTURE_2D, vg_uictx.tex_glyphs );
-   glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 256, 256, 0, 
-                 GL_RED, GL_UNSIGNED_BYTE, image );
-
-   VG_CHECK_GL_ERR();
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
-}
-
 enum ui_shader {
    k_ui_shader_colour,
-   k_ui_shader_image
+   k_ui_shader_image,
+   k_ui_shader_hsv,
 };
 
-static void rect_copy( ui_rect a, ui_rect b )
-{
-   for( int i=0; i<4; i++ )
-      b[i] = a[i];
-}
-
-VG_STATIC void ui_flush( enum ui_shader shader )
-{
-   u32 vertex_offset = vg_uictx.vert_start*sizeof(ui_vert),
-       vertex_count  = vg_uictx.cur_vert-vg_uictx.vert_start,
-       vertex_size   = vertex_count*sizeof(ui_vert),
-
-       indice_offset = vg_uictx.indice_start*sizeof(u16),
-       indice_count  = vg_uictx.cur_indice-vg_uictx.indice_start,
-       indice_size   = indice_count * sizeof(u16);
-
-   if( !vertex_size || !indice_size )
-      return;
-       
-       glBindVertexArray( vg_uictx.vao );
-       glBindBuffer( GL_ARRAY_BUFFER, vg_uictx.vbo );
-       glBufferSubData( GL_ARRAY_BUFFER, vertex_offset, vertex_size,
-                    vg_uictx.vertex_buffer+vg_uictx.vert_start );
-       
-       glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_uictx.ebo );
-       glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, indice_offset, indice_size,
-                    vg_uictx.indice_buffer+vg_uictx.indice_start );
-       
-       glEnable( GL_BLEND );
-       glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
-       glBlendEquation( GL_FUNC_ADD );
-       
-       m3x3f view = M3X3_IDENTITY;
-   m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
-   m3x3_scale( view, (v3f){ 1.0f/((float)vg.window_x*0.5f), 
-                           -1.0f/((float)vg.window_y*0.5f), 1.0f } );
-       
-   if( shader == k_ui_shader_colour ){
-      glUseProgram( _shader_ui.id );
-      
-      glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1, 
-                          GL_FALSE, (float *)view );
-       
-      glActiveTexture( GL_TEXTURE0 );
-      glBindTexture( GL_TEXTURE_2D, vg_uictx.tex_glyphs );
-      glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 );
-   }
-   else if( shader == k_ui_shader_image ){
-      glUseProgram( _shader_ui_image.id );
-      glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1, 
-                          GL_FALSE, (float *)view );
-      glUniform1i( glGetUniformLocation(_shader_ui_image.id,"uTexImage"), 0 );
-   }
-   else
-      vg_fatal_error( "Invalid UI shader (%d)\n", shader );
-       
-       glDrawElements( GL_TRIANGLES, indice_count, GL_UNSIGNED_SHORT, 
-                   (void *)(vg_uictx.indice_start*sizeof(u16)) );
-
-       glDisable( GL_BLEND );
-
-   vg_uictx.indice_start = vg_uictx.cur_indice;
-   vg_uictx.vert_start = vg_uictx.cur_vert;
-}
-
-static void ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] )
-{
-   /* this if far from ideal but stops us from crashing */
-   if( (vg_uictx.cur_vert + 4 > vg_uictx.max_verts) || 
-       (vg_uictx.cur_indice + 6 > vg_uictx.max_indices))
-      return;
-
-   struct ui_vert *vertices = &vg_uictx.vertex_buffer[ vg_uictx.cur_vert ];
-       u16            *indices  = &vg_uictx.indice_buffer[ vg_uictx.cur_indice ];
-
-   for( int i=0; i<4; i++ ){
-      vertices[i].colour = colour;
-   }
-
-       vertices[0].co[0] = rect[0];
-       vertices[0].co[1] = rect[1];
-       vertices[0].uv[0] = uv[0];
-       vertices[0].uv[1] = uv[1];
-       vertices[1].co[0] = rect[0]+rect[2];
-       vertices[1].co[1] = rect[1];
-       vertices[1].uv[0] = uv[2];
-       vertices[1].uv[1] = uv[1];
-       vertices[2].co[0] = rect[0]+rect[2];
-       vertices[2].co[1] = rect[1]+rect[3];
-       vertices[2].uv[0] = uv[2];
-       vertices[2].uv[1] = uv[3];
-       vertices[3].co[0] = rect[0];
-       vertices[3].co[1] = rect[1]+rect[3];
-       vertices[3].uv[0] = uv[0];
-       vertices[3].uv[1] = uv[3];
-       u16 ind_start = vg_uictx.cur_vert;
-       
-       u16 start = vg_uictx.cur_vert;
-   u32 mesh[] = { 0,2,1, 0,3,2 };
-
-   for( u32 i=0; i<vg_list_size(mesh); i++ ){
-      indices[i] = start+mesh[i];
-   }
-
-       vg_uictx.cur_indice += 6;
-       vg_uictx.cur_vert += 4;
-}
-
-static void ui_fill( ui_rect rect, u32 colour )
-{
-   ui_fill_rect( rect, colour, (ui_px[4]){ 4,4,4,4 } );
-}
-
-static void ui_outline( ui_rect rect, ui_px thickness, u32 colour )
-{
-   /* this if far from ideal but stops us from crashing */
-   if( (vg_uictx.cur_vert + 8 > vg_uictx.max_verts) || 
-       (vg_uictx.cur_indice + 24 > vg_uictx.max_indices))
-      return;
-
-   struct ui_vert *vertices = &vg_uictx.vertex_buffer[ vg_uictx.cur_vert ];
-       u16            *indices  = &vg_uictx.indice_buffer[ vg_uictx.cur_indice ];
-
-   for( int i=0; i<8; i++ ){
-      vertices[i].uv[0] = 4;
-      vertices[i].uv[1] = 4;
-      vertices[i].colour = colour;
-   }
-
-       vertices[0].co[0] = rect[0];
-       vertices[0].co[1] = rect[1];
-       vertices[1].co[0] = rect[0]+rect[2];
-       vertices[1].co[1] = rect[1];
-       vertices[2].co[0] = rect[0]+rect[2];
-       vertices[2].co[1] = rect[1]+rect[3];
-       vertices[3].co[0] = rect[0];
-       vertices[3].co[1] = rect[1]+rect[3];
-   vertices[4].co[0] = vertices[0].co[0]-thickness;
-   vertices[4].co[1] = vertices[0].co[1]-thickness;
-   vertices[5].co[0] = vertices[1].co[0]+thickness;
-   vertices[5].co[1] = vertices[1].co[1]-thickness;
-   vertices[6].co[0] = vertices[2].co[0]+thickness;
-   vertices[6].co[1] = vertices[2].co[1]+thickness;
-   vertices[7].co[0] = vertices[3].co[0]-thickness;
-   vertices[7].co[1] = vertices[3].co[1]+thickness;
-
-       u16 start = vg_uictx.cur_vert;
-   u32 mesh[] = { 0,5,4, 0,1,5, 1,6,5, 1,2,6, 2,7,6, 2,3,7, 3,4,7, 3,0,4 };
-
-   for( u32 i=0; i<vg_list_size(mesh); i++ ){
-      indices[i] = start+mesh[i];
-   }
-       
-       vg_uictx.cur_indice += 24;
-       vg_uictx.cur_vert += 8;
-}
-
-static void ui_split_px( ui_rect rect, 
-                         enum ui_axis other, ui_px width, ui_px pad,
-                         ui_rect l, ui_rect r )
-{
-   enum ui_axis dir = other ^ 0x1;
-
-   ui_rect temp;
-   rect_copy( rect, temp );
-
-   l[ dir ]     = temp[ dir ]   + pad;
-   r[ dir ]     = temp[ dir ]   + width + (pad/2);
-   l[ other ]   = temp[ other ] + pad;
-   r[ other ]   = temp[ other ] + pad;
-   l[ 2+dir ]   = width                   - ((3*pad)/2);
-   r[ 2+dir ]   = temp[ 2+dir ]   - width - ((3*pad)/2);
-   l[ 2+other ] = temp[ 2+other ] - pad*2;
-   r[ 2+other ] = temp[ 2+other ] - pad*2;
-}
-
-static void ui_rect_center( ui_rect parent, ui_rect rect )
-{
-   rect[0] = parent[0] + (parent[2]-rect[2])/2;
-   rect[1] = parent[1] + (parent[3]-rect[3])/2;
-}
-
-static void ui_fit_item( ui_rect rect, ui_px size[2], ui_rect d )
-{
-   i32 rp = (i32)rect[2] * (i32)size[1],
-       rc = (i32)size[0] * (i32)rect[3];
-
-   enum ui_axis dir, other;
-   if( rc > rp ) dir = k_ui_axis_h;
-   else          dir = k_ui_axis_v;
-   other = dir ^ 0x1;
-
-   d[2+dir] = rect[2+dir];
-   d[2+other] = (rect[2+dir] * size[other]) / size[dir];
-
-   ui_rect_center( rect, d );
-}
-
-static void ui_split_ratio( ui_rect rect, enum ui_axis dir, float ratio, 
-                            ui_px pad, ui_rect l, ui_rect r )
-{
-   ui_px width = (float)rect[ 2+(dir^0x1) ] * ratio;
-   ui_split_px( rect, dir, width, pad, l, r );
-}
-
-static void ui_rect_pad( ui_rect rect, ui_px pad )
-{
-   rect[0] += pad;
-   rect[1] += pad;
-   rect[2] -= pad*2;
-   rect[3] -= pad*2;
-}
-
-static ui_px ui_text_line_width( const char *str )
-{
-   int length = 0;
-   const char *_c = str;
-   char c;
-
-   while( (c = *(_c ++)) ){
-      if( c >= 32 && c <= 126 )
-         length ++;
-      else if( c == '\n' )
-         break;
-   }
-
-   return length * 8;
-}
-
-static ui_px ui_text_string_height( const char *str )
-{
-   int height = 1;
-   const char *_c = str;
-   char c;
-
-   while( (c = *(_c ++)) ){
-      if( c == '\n' ) height ++;
-   }
-
-   return height * 14;
-}
-
-static ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale,
-                                enum ui_align align )
-{
-   if( align == k_ui_align_left ){
-      return rect[0];
-   }
-   else{
-      ui_px width = ui_text_line_width( str ) * scale;
+extern ui_px k_ui_widget_height,
+             k_ui_scale,
+             k_ui_padding;
 
-      if( align == k_ui_align_right )
-         return rect[0] + rect[2]-width;
-      else
-         return rect[0] + (rect[2]-width)/2;
-   }
-}
-
-static ui_px ui_min( ui_px a, ui_px b ){ return a<b?a:b; }
-static ui_px ui_max( ui_px a, ui_px b ){ return a>b?a:b; }
-static ui_px ui_clamp( ui_px a, ui_px min, ui_px max )
-{
-   return ui_min( max, ui_max( a, min ) );
-}
-
-static int ui_clip( ui_rect parent, ui_rect child, ui_rect clipped )
-{
-   ui_px parent_max[2], child_max[2];
-   parent_max[0] = parent[0]+parent[2];
-   parent_max[1] = parent[1]+parent[3];
-   child_max[0] = child[0]+child[2];
-   child_max[1] = child[1]+child[3];
-
-   clipped[0] = ui_clamp( child[0], parent[0], parent_max[0] );
-   clipped[1] = ui_clamp( child[1], parent[1], parent_max[1] );
-   clipped[2] = ui_clamp( child_max[0], parent[0], parent_max[0] );
-   clipped[3] = ui_clamp( child_max[1], parent[1], parent_max[1] );
-
-   if( clipped[0] == clipped[2] ||
-       clipped[1] == clipped[3] )
-      return 0;
-
-   clipped[2] -= clipped[0];
-   clipped[3] -= clipped[1];
-   
-   return 1;
-}
-
-static int ui_inside_rect( ui_rect rect, ui_px co[2] )
-{
-   if( co[0] >= rect[0] &&
-       co[1] >= rect[1] &&
-       co[0] <= rect[0]+rect[2] &&
-       co[1] <= rect[1]+rect[3] )
-   {
-      return 1;
-   }
-   else 
-      return 0;
-}
+typedef u32 ui_scheme[8*4];
 
-static int ui_click_down(void)
-{
-   if( vg_uictx.ignore_input_frames ) return 0;
+#define UI_RGB( STDHEX )          0xff000000       |\
+                         ((STDHEX&0x000000ff)<<16) |\
+                         ((STDHEX&0x0000ff00)    ) |\
+                         ((STDHEX&0x00ff0000)>>16) 
 
-   if( (vg_uictx.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT)) &&
-      !(vg_uictx.mouse_state[1] & SDL_BUTTON(SDL_BUTTON_LEFT)) )
-      return 1;
-   else
-      return 0;
-}
+#define UI_TEXTBOX_MULTILINE 0x1
+#define UI_TEXTBOX_WRAP      0x2
+#define UI_TEXTBOX_AUTOFOCUS 0x4
 
-static int ui_clicking(void)
-{
-   if( vg_uictx.ignore_input_frames ) return 0;
-   return vg_uictx.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT);
-}
+#define UI_MODAL_OK        0x0
+#define UI_MODAL_GOOD      0x1
+#define UI_MODAL_BAD       0x2
+#define UI_MODAL_WARN      0x3
+#define UI_MODAL_TYPE_BITS 0x3
 
-static int ui_click_up(void)
-{
-   if( vg_uictx.ignore_input_frames ) return 0;
-   if( (vg_uictx.mouse_state[1] & SDL_BUTTON(SDL_BUTTON_LEFT)) &&
-      !(vg_uictx.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT)) )
-      return 1;
-   else
-      return 0;
-}
-
-static void ui_prerender(void)
-{
-   int x, y;
-   vg_uictx.mouse_state[1] = vg_uictx.mouse_state[0];
-   vg_uictx.mouse_state[0] = SDL_GetMouseState( &x, &y );
-   vg_uictx.mouse[0] = x;
-   vg_uictx.mouse[1] = y;
-
-       vg_uictx.cur_vert = 0;
-       vg_uictx.cur_indice = 0;
-   vg_uictx.vert_start = 0;
-   vg_uictx.indice_start = 0;
-
-   if( vg_uictx.ignore_input_frames ){
-      vg_uictx.ignore_input_frames --;
-      return;
-   }
+#define UI_MOUSE_LEFT   (SDL_BUTTON(SDL_BUTTON_LEFT))
+#define UI_MOUSE_RIGHT  (SDL_BUTTON(SDL_BUTTON_RIGHT))
+#define UI_MOUSE_MIDDLE (SDL_BUTTON(SDL_BUTTON_MIDDLE))
 
-   if( ui_click_down() ){
-      vg_uictx.mouse_click[0] = vg_uictx.mouse[0];
-      vg_uictx.mouse_click[1] = vg_uictx.mouse[1];
-   }
-}
+#define UI_TOP 0x1
+#define UI_LEFT 0x2
+#define UI_BOTTOM 0x4
+#define UI_RIGHT 0x8
 
-static void ui_text( ui_rect rect, const char *str, ui_px scale, 
-                        enum ui_align align )
+struct vg_imgui
 {
-       ui_rect text_cursor;
-       u32 current_colour = 0x00ffffff;
-       
-   const char *_c = str;
-       u8 c;
-
-   text_cursor[0] = ui_text_aligned_x( str, rect, scale, align );
-       text_cursor[1] = rect[1];
-       text_cursor[2] = 8*scale;
-       text_cursor[3] = 14*scale;
+   struct ui_vert *vertex_buffer;
+   u16            *indice_buffer;
+       u32 max_verts, max_indices, 
+       cur_vert,  cur_indice,
+       vert_start, indice_start;
 
-   if( align & (k_ui_align_middle|k_ui_align_bottom) ){
-      ui_px height = ui_text_string_height( str ) * scale;
+   union {
+   void *focused_control_id; /* uses the memory location of various locking
+                                controls as an id */
+   char *textbuf;
+   i32 *ptr_enum;
+   };
 
-      if( align & k_ui_align_bottom )
-         text_cursor[1] += rect[3]-height;
-      else
-         text_cursor[1] += (rect[3]-height)/2;
+   u32 focused_control_hit;
+   enum ui_control_type{
+      k_ui_control_none,
+      k_ui_control_textbox,
+      k_ui_control_enum,
+               k_ui_control_modal
    }
+   focused_control_type;
+
+   union{ /* controls that can be focused */
+      struct ui_textbuf{
+         int cursor_user, cursor_pos;
+         u32 len;
+         u32 flags;
+
+         struct ui_textbox_callbacks{
+            void (*enter)( char *, u32 ),  
+                 (*up)( char *, u32 ), 
+                 (*down)( char *, u32 ),
+                 (*change)( char *, u32 ),
+                 (*escape)( void );
+         }
+         callbacks;
+      }
+      textbox;
 
-       while( (c = *(_c ++)) ){
-               if( c == '\n' ){
-                       text_cursor[1] += 14*scale;
-         text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align );
-                       continue;
-               }
-               else if( c >= 33 ){
-                       u8 glyph_base[2];
-                       u8 glyph_index = c;
-                       glyph_base[0] = glyph_index & 0xf;
-                       glyph_base[1] = (glyph_index-glyph_base[0])>>4;
-                       
-                       glyph_base[0] *= 8;
-                       glyph_base[1] *= 8;
-
-         ui_rect rect_char;
-
-         if( ui_clip( rect, text_cursor, rect_char ) ){
-            ui_fill_rect( rect_char, current_colour, (ui_px[4])
-            {
-               glyph_base[0]+2,
-               glyph_base[1]+1,
-               glyph_base[0]+6,
-               glyph_base[1]+8
-            });
+      struct ui_enum{
+         struct ui_enum_opt{
+            i32 value;
+            const char *alias;
          }
-               }
-               else if( c == '\x1B' ){
-         /* vt codes */
-                       _c ++;
-                       u16 colour_id = 0;
-                       for( int i=0; i<3; i ++ ){
-                               if( _c[i] ){
-                                       if( _c[i] == 'm' ){
-                                               _c = _c + i + 1;
-                                               
-                                               switch( colour_id ){
-                                                       case '0':        current_colour = 0x00ffffff; break;
-                                                       case '3'|'1'<<8: current_colour = 0x00201fee; break;
-                                                       case '3'|'2'<<8: current_colour = 0x0037e420; break;
-                                                       case '3'|'3'<<8: current_colour = 0x000ed8e2; break;
-                                                       case '3'|'4'<<8: current_colour = 0x00f15010; break;
-                                                       case '3'|'5'<<8: current_colour = 0x00ee20ee; break;
-                                                       case '3'|'6'<<8: current_colour = 0x00eeee20; break;
-                                                       case '3'|'7'<<8: current_colour = 0x00ffffff; break;
-                                               }
-                                               
-                                               break;
-                                       }
-                                       
-                                       colour_id |= _c[i] << (i*8);
-                               } 
-                               else{
-                                       _c = _c +i;
-                                       break;
-                               }
-                       }
-
-         continue;
-               }
-      else if( c == '\t' ){
-         text_cursor[0] += UI_GLYPH_SPACING_X*scale*4;
-         continue;
+         *options;
+         u32 option_count;
+         ui_rect rect;
       }
+      _enum;
+   };
+
+       struct ui_modal{
+               const char *message;
+               u32 options;
                
-       text_cursor[0] += UI_GLYPH_SPACING_X*scale;
+               struct ui_modal_callbacks{
+                       void (*close)(u32);
+               }
+               callbacks;
        }
-}
-
-static void ui_image( ui_rect rect, GLuint image )
-{
-   ui_flush( k_ui_shader_colour );
+   modal;
+       
+       GLuint tex_glyphs, vao, vbo, ebo, tex_bg;
+   v2f bg_inverse_ratio;
 
-   glActiveTexture( GL_TEXTURE0 );
-   glBindTexture( GL_TEXTURE_2D, image );
-   ui_fill_rect( rect, 0xffffffff, (ui_px[4]){ 0,0, 255,255 } );
-   ui_flush( k_ui_shader_image );
-}
+       ui_px mouse[2], mouse_delta[2], mouse_click[2];
+   u32 mouse_state[2];
+   u32 ignore_input_frames;
+   bool mouse_pos_overriden;
+   int wants_mouse;
 
-static u32 v4f_u32_colour( v4f colour )
-{
-   u32 r = colour[0] * 255.0f,
-       g = colour[1] * 255.0f,
-       b = colour[2] * 255.0f,
-       a = colour[3] * 255.0f;
+   ui_rect click_fader, click_fader_end;
+   float click_fade_opacity;
+   f32 frosting;
 
-   return r | (g<<8) | (b<<16) | (a<<24);
-}
+   ui_scheme scheme;
+   const vg_font_face *font;
+   v2f inverse_font_sheet;
 
-static int ui_button( ui_rect rect, v4f colour )
-{
-   int clickup= ui_click_up(),
-       click  = ui_clicking() | clickup,
-       target = ui_inside_rect( rect, vg_uictx.mouse_click ) && click,
-       hover  = ui_inside_rect( rect, vg_uictx.mouse );
-
-   u32 basecolour = v4f_u32_colour( colour );
-
-   if( click ){
-      if( target ){
-         if( hover ){
-            if( clickup ){
-               ui_fill( rect, 0xffffffff );
-               vg_uictx.ignore_input_frames = 2;
-               rect_copy( rect, vg_uictx.click_fader );
-               vg_uictx.click_fade_opacity = 1.0f;
-               return 1;
-            }
-            else{
-               ui_fill( rect, 0xffcccccc );
-               return 0;
-            }
-         }
-         else{
-            ui_fill( rect, 0xff505050 );
-            ui_outline( rect, 1, 0xffffffff );
-            return 0;
-         }
-      }
-      else{
-         ui_fill( rect, 0xff505050 );
-         return 0;
-      }
-   }
-   else{
-      if( hover ){
-         ui_fill( rect, 0xffa0a0a0 );
-         return 0;
-      }
-      else{
-         ui_fill( rect, 0xff505050 );
-         return 0;
-      }
+   enum ui_cursor{
+      k_ui_cursor_default,
+      k_ui_cursor_ibeam,
+      k_ui_cursor_hand,
+      k_ui_cursor_max
    }
-}
-
-static int ui_button_text( ui_rect rect, const char *string, ui_px scale,
-                           v4f colour )
-{
-   int result = ui_button( rect, colour );
-   ui_rect t = { 0,0, ui_text_line_width( string )*scale, 14*scale };
-   ui_rect_center( rect, t );
-   ui_text( t, string, scale, k_ui_align_left );
-   return result;
-}
-
-static void ui_postrender(void)
-{
-   if( vg_uictx.click_fade_opacity > 0.0f ){
-
-      float scale = vg_uictx.click_fade_opacity;
-            scale = vg_maxf( 1.0f/255.0f, scale*scale );
+   cursor;
 
-      vg_uictx.click_fade_opacity -= vg.time_frame_delta * 1.8f;
-      u32 colour = v4f_u32_colour( (v4f){ 1.0f,1.0f,1.0f, scale } );
+   SDL_Cursor *cursor_map[ k_ui_cursor_max ];
+   v4f colour;
 
-      ui_rect rect;
-      rect[3] = (float)(vg_uictx.click_fader[3]) * scale;
-      rect[2] = vg_uictx.click_fader[2];
-      ui_rect_center( vg_uictx.click_fader, rect );
-
-      ui_fill( rect, colour );
-   }
-   ui_flush( k_ui_shader_colour );
-}
+   f32 hue; /* lol this sucks */
+} 
+extern vg_ui;
+
+enum ui_button_state {
+   k_ui_button_none            = 0x0,
+   k_ui_button_click           = 0x1,
+   k_ui_button_holding_inside  = 0x2,
+   k_ui_button_holding_outside = 0x4,
+   k_ui_button_hover           = 0x8
+};
 
-#endif /* VG_IMGUI_H */
+/* TODO: docu.. */
+
+void vg_ui_init(void);
+void rect_copy( ui_rect a, ui_rect b );
+void ui_flush( enum ui_shader shader, f32 w, f32 h );
+struct ui_vert *ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] );
+struct ui_vert *ui_fill( ui_rect rect, u32 colour );
+void ui_outline( ui_rect rect, ui_px thickness, u32 colour, u32 mask );
+void ui_split( ui_rect rect, enum ui_axis other, ui_px width, ui_px gap,
+               ui_rect l, ui_rect r );
+void ui_rect_center( ui_rect parent, ui_rect rect );
+void ui_fit_item( ui_rect rect, ui_px size[2], ui_rect d );
+void ui_split_ratio( ui_rect rect, enum ui_axis dir, float ratio, 
+                     ui_px gap, ui_rect l, ui_rect r );
+void ui_rect_pad( ui_rect rect, ui_px pad[2] );
+ui_px ui_text_line_width( const char *str );
+ui_px ui_text_string_height( const char *str );
+ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale,
+                         enum ui_align align );
+int ui_clip( ui_rect parent, ui_rect child, ui_rect clipped );
+int ui_inside_rect( ui_rect rect, ui_px co[2] );
+int ui_click_down( u32 mask );
+int ui_clicking( u32 mask );
+int ui_click_up( u32 mask );
+void ui_set_mouse_pos( ui_px x, ui_px y );
+void ui_prerender(void);
+u32 ui_colour( enum ui_scheme_colour id );
+
+/* get an appropriately contrasting colour given the base */
+u32 ui_colourcont( enum ui_scheme_colour id );
+
+void ui_hex_to_norm( u32 hex, v4f norm );
+u32 v4f_u32_colour( v4f colour );
+
+u32 ui_opacity( u32 colour, f32 opacity );
+void ui_font_face( vg_font_face *ff );
+u32 ui_ntext( ui_rect rect, const char *str, u32 len, ui_px scale, 
+              enum ui_align align, u32 colour );
+u32 ui_text( ui_rect rect, const char *str, ui_px scale, 
+             enum ui_align align, u32 colour );
+void ui_panel( ui_rect in_rect, ui_rect out_panel );
+void ui_label( ui_rect rect, const char *text, ui_px size,
+               ui_px gap, ui_rect r );
+void ui_standard_widget( ui_rect inout_panel, ui_rect out_rect, ui_px count );
+void ui_info( ui_rect inout_panel, const char *text );
+void ui_image( ui_rect rect, GLuint image );
+void ui_defocus_all(void);
+
+enum ui_button_state ui_button_base( ui_rect rect );
+enum ui_button_state ui_colourbutton( ui_rect rect, 
+                                      enum ui_scheme_colour colour,
+                                      enum ui_scheme_colour hover_colour,
+                                      enum ui_scheme_colour hi_colour );
+enum ui_button_state ui_colourbutton_text( 
+      ui_rect rect, const char *string, ui_px scale,
+      enum ui_scheme_colour colour );
+enum ui_button_state ui_button_text( ui_rect rect, 
+                                     const char *string, ui_px scale );
+enum ui_button_state ui_button( ui_rect inout_panel, const char *string );
+
+void ui_postrender(void);
+
+enum ui_button_state ui_checkbox_base( ui_rect box, i32 *data );
+int ui_checkbox( ui_rect inout_panel, const char *str_label, i32 *data );
+void ui_enum( ui_rect inout_panel, const char *str_label, 
+              struct ui_enum_opt *options, u32 len, i32 *value );
+enum ui_button_state ui_slider_base( ui_rect box, f32 min, f32 max, f32 *value, 
+                                     f32 *out_t );
+bool ui_slider( ui_rect inout_panel, const char *str_label, 
+                f32 min, f32 max, f32 *value );
+void ui_slider_text( ui_rect box, const char *format, f32 value );
+void ui_colourpicker( ui_rect inout_panel, const char *str_label, v4f value );
+int ui_textbox( ui_rect inout_panel, const char *label,
+                char *buf, u32 len, u32 lines, u32 flags,
+                struct ui_textbox_callbacks *callbacks );
+void ui_tabs( ui_rect inout_panel, ui_rect out_content_panel,
+              const char **titles, u32 count, i32 *page );
+void ui_start_modal( const char *message, u32 options );
+void ui_proc_key( SDL_Keysym ev );
+void ui_proc_utf8( const char *text );
+void ui_dev_colourview(void);
+
+void _ui_textbox_move_cursor( int *cursor0, int *cursor1, 
+                              int dir, int snap_together );
+int _ui_textbox_delete_char( int direction );
+void _ui_textbox_put_char( char c );
+void _ui_textbox_up(void);
+void _ui_textbox_left(void);
+void _ui_textbox_left_select(void);
+void _ui_textbox_down(void);
+void _ui_textbox_right_select(void);
+void _ui_textbox_right(void);
+void _ui_textbox_backspace(void);
+void _ui_textbox_delete(void);
+void _ui_textbox_home_select(void);
+void _ui_textbox_home(void);
+void _ui_textbox_end_select(void);
+void _ui_textbox_end(void);
+void _ui_textbox_select_all(void);
+void _ui_textbox_cut(void);
+void _ui_textbox_enter(void);