-/* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */
+/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */
/*
* Principles:
* 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 {
/* 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,
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);