X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=vg_imgui.h;h=8ec033009859b9ac44fe180a945a44eb27fcaeeb;hb=HEAD;hp=2f84885e64b1a89f2ab7ee5103ad95c4cc78b202;hpb=2026f6fe648696888762c5b10210191748a6c9de;p=vg.git diff --git a/vg_imgui.h b/vg_imgui.h index 2f84885..8ec0330 100644 --- a/vg_imgui.h +++ b/vg_imgui.h @@ -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,18 +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. */ -#ifndef VG_IMGUI_H -#define VG_IMGUI_H +#pragma once -#define VG_GAME -#include "vg/vg.h" -#include "vg/vg_tex.h" -#include "vg/vg_shader.h" +#include "vg_engine.h" +#include "vg_tex.h" +#include "vg_shader.h" +#include "vg_font.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 ui_px ui_rect[4]; +typedef ui_px ui_point[2]; typedef struct ui_vert ui_vert; enum ui_axis { @@ -28,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, @@ -47,7 +55,7 @@ enum ui_align struct ui_vert { ui_px co[2]; - u8 uv[2]; + u16 uv[2]; u32 colour; }; #pragma pack(pop) @@ -67,6 +75,16 @@ enum ui_scheme_colour{ k_ui_brighter = 8 }; +enum ui_shader { + k_ui_shader_colour, + k_ui_shader_image, + k_ui_shader_hsv, +}; + +extern ui_px k_ui_widget_height, + k_ui_scale, + k_ui_padding; + typedef u32 ui_scheme[8*4]; #define UI_RGB( STDHEX ) 0xff000000 |\ @@ -76,8 +94,25 @@ typedef u32 ui_scheme[8*4]; #define UI_TEXTBOX_MULTILINE 0x1 #define UI_TEXTBOX_WRAP 0x2 +#define UI_TEXTBOX_AUTOFOCUS 0x4 + +#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 + +#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)) -struct{ +#define UI_TOP 0x1 +#define UI_LEFT 0x2 +#define UI_BOTTOM 0x4 +#define UI_RIGHT 0x8 + +struct vg_imgui +{ struct ui_vert *vertex_buffer; u16 *indice_buffer; u32 max_verts, max_indices, @@ -88,32 +123,74 @@ struct{ void *focused_control_id; /* uses the memory location of various locking controls as an id */ char *textbuf; + i32 *ptr_enum; }; 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; - struct ui_textbuf{ - int cursor_user, cursor_pos; - u32 len; - u32 flags; - } - textbox; + 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; + + struct ui_enum{ + struct ui_enum_opt{ + i32 value; + const char *alias; + } + *options; + u32 option_count; + ui_rect rect; + } + _enum; + }; + + struct ui_modal{ + const char *message; + u32 options; + + struct ui_modal_callbacks{ + void (*close)(u32); + } + callbacks; + } + modal; - GLuint tex_glyphs, vao, vbo, ebo; + GLuint tex_glyphs, vao, vbo, ebo, tex_bg; + v2f bg_inverse_ratio; - ui_px mouse[2], mouse_click[2]; + 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; ui_rect click_fader, click_fader_end; float click_fade_opacity; + f32 frosting; ui_scheme scheme; + const vg_font_face *font; + v2f inverse_font_sheet; enum ui_cursor{ k_ui_cursor_default, @@ -124,1593 +201,118 @@ struct{ cursor; SDL_Cursor *cursor_map[ k_ui_cursor_max ]; -} -static vg_ui = { - .scheme = { - [ k_ui_bg+0 ] = UI_RGB( 0x1d2021 ), - [ k_ui_bg+1 ] = UI_RGB( 0x282828 ), - [ k_ui_bg+2 ] = UI_RGB( 0x3c3836 ), - [ k_ui_bg+3 ] = UI_RGB( 0x504945 ), - [ k_ui_bg+4 ] = UI_RGB( 0x665c54 ), - [ k_ui_bg+5 ] = UI_RGB( 0x7c6f64 ), - [ k_ui_bg+6 ] = UI_RGB( 0x928374 ), - [ k_ui_bg+7 ] = UI_RGB( 0xa89984 ), - - [ k_ui_fg+0 ] = UI_RGB( 0xebdbb2 ), - [ k_ui_fg+1 ] = UI_RGB( 0xfbf1c7 ), - [ k_ui_fg+2 ] = UI_RGB( 0xd5c4a1 ), - [ k_ui_fg+3 ] = UI_RGB( 0xbdae93 ), - [ k_ui_fg+4 ] = UI_RGB( 0xa89984 ), - [ k_ui_fg+5 ] = UI_RGB( 0x000000 ), - [ k_ui_fg+6 ] = UI_RGB( 0x000000 ), - [ k_ui_fg+7 ] = UI_RGB( 0x000000 ), + v4f colour; - [ k_ui_red ] = UI_RGB( 0xcc241d ), - [ k_ui_orange ] = UI_RGB( 0xd65d0e ), - [ k_ui_yellow ] = UI_RGB( 0xd79921 ), - [ k_ui_green ] = UI_RGB( 0x98971a ), - [ k_ui_aqua ] = UI_RGB( 0x689d6a ), - [ k_ui_blue ] = UI_RGB( 0x458588 ), - [ k_ui_purple ] = UI_RGB( 0xb16286 ), - [ k_ui_gray ] = UI_RGB( 0x928374 ), - [ k_ui_red + k_ui_brighter ] = UI_RGB( 0xfb4934 ), - [ k_ui_orange + k_ui_brighter ] = UI_RGB( 0xfe8019 ), - [ k_ui_yellow + k_ui_brighter ] = UI_RGB( 0xfabd2f ), - [ k_ui_green + k_ui_brighter ] = UI_RGB( 0xb8bb26 ), - [ k_ui_aqua + k_ui_brighter ] = UI_RGB( 0x8ec07c ), - [ k_ui_blue + k_ui_brighter ] = UI_RGB( 0x83a598 ), - [ k_ui_purple + k_ui_brighter ] = UI_RGB( 0xd3869b ), - [ k_ui_gray + k_ui_brighter ] = UI_RGB( 0xa89984 ), - } -}; - -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;" - - "vec2 rand_hash22( vec2 p )" - "{" - "vec3 p3 = fract(vec3(p.xyx) * 213.8976123);" - "p3 += dot(p3, p3.yzx+19.19);" - "return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));" - "}" - "" - "void main()" - "{" - "vec4 diffuse = aColour;" - - "if( diffuse.a == 0.0 )" - "{" - "diffuse.a = texture( uTexGlyphs, aTexCoords ).r;" - "}" - - "FragColor = diffuse;" - "}" - } -}; - -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_ui.max_indices = 20000; - vg_ui.max_verts = 30000; - - /* Generate the buffer we are gonna be drawing to */ - glGenVertexArrays( 1, &vg_ui.vao ); - glGenBuffers( 1, &vg_ui.vbo ); - glGenBuffers( 1, &vg_ui.ebo ); - - glBindVertexArray( vg_ui.vao ); - glBindBuffer( GL_ARRAY_BUFFER, vg_ui.vbo ); - - glBufferData( GL_ARRAY_BUFFER, - vg_ui.max_verts * sizeof( struct ui_vert ), - NULL, GL_DYNAMIC_DRAW ); - glBindVertexArray( vg_ui.vao ); - - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_ui.ebo ); - glBufferData( GL_ELEMENT_ARRAY_BUFFER, - vg_ui.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_ui.max_verts*sizeof(struct ui_vert), - inds_size = vg_align8( vg_ui.max_indices*sizeof(u16) ); - - vg_ui.vertex_buffer = vg_linear_alloc( vg_mem.rtmemory, vert_size ); - vg_ui.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_ui.tex_glyphs ); - glBindTexture( GL_TEXTURE_2D, vg_ui.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 ); - - /* - * Cursors - * --------------------------------------------------------------- - */ - - vg_ui.cursor_map[ k_ui_cursor_default ] = - SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_ARROW ); - vg_ui.cursor_map[ k_ui_cursor_hand ] = - SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_HAND ); - vg_ui.cursor_map[ k_ui_cursor_ibeam ] = - SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_IBEAM ); -} - -enum ui_shader { - k_ui_shader_colour, - k_ui_shader_image + 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 }; -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_ui.vert_start*sizeof(ui_vert), - vertex_count = vg_ui.cur_vert-vg_ui.vert_start, - vertex_size = vertex_count*sizeof(ui_vert), - - indice_offset = vg_ui.indice_start*sizeof(u16), - indice_count = vg_ui.cur_indice-vg_ui.indice_start, - indice_size = indice_count * sizeof(u16); - - if( !vertex_size || !indice_size ) - return; - - glBindVertexArray( vg_ui.vao ); - glBindBuffer( GL_ARRAY_BUFFER, vg_ui.vbo ); - glBufferSubData( GL_ARRAY_BUFFER, vertex_offset, vertex_size, - vg_ui.vertex_buffer+vg_ui.vert_start ); - - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_ui.ebo ); - glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, indice_offset, indice_size, - vg_ui.indice_buffer+vg_ui.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_ui.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_ui.indice_start*sizeof(u16)) ); - - glDisable( GL_BLEND ); - - vg_ui.indice_start = vg_ui.cur_indice; - vg_ui.vert_start = vg_ui.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_ui.cur_vert + 4 > vg_ui.max_verts) || - (vg_ui.cur_indice + 6 > vg_ui.max_indices)) - return; - - struct ui_vert *vertices = &vg_ui.vertex_buffer[ vg_ui.cur_vert ]; - u16 *indices = &vg_ui.indice_buffer[ vg_ui.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_ui.cur_vert; - - u16 start = vg_ui.cur_vert; - u32 mesh[] = { 0,2,1, 0,3,2 }; - - for( u32 i=0; i vg_ui.max_verts) || - (vg_ui.cur_indice + 24 > vg_ui.max_indices)) - return; - - struct ui_vert *vertices = &vg_ui.vertex_buffer[ vg_ui.cur_vert ]; - u16 *indices = &vg_ui.indice_buffer[ vg_ui.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_ui.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 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; - - 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 ab?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; -} - -static int ui_click_down(void) -{ - if( vg_ui.ignore_input_frames ) return 0; - - if( (vg_ui.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT)) && - !(vg_ui.mouse_state[1] & SDL_BUTTON(SDL_BUTTON_LEFT)) ) - return 1; - else - return 0; -} - -static int ui_clicking(void) -{ - if( vg_ui.ignore_input_frames ) return 0; - return vg_ui.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT); -} - -static int ui_click_up(void) -{ - if( vg_ui.ignore_input_frames ) return 0; - if( (vg_ui.mouse_state[1] & SDL_BUTTON(SDL_BUTTON_LEFT)) && - !(vg_ui.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT)) ) - return 1; - else - return 0; -} - -static void ui_prerender(void) -{ - int x, y; - vg_ui.mouse_state[1] = vg_ui.mouse_state[0]; - vg_ui.mouse_state[0] = SDL_GetMouseState( &x, &y ); - vg_ui.mouse[0] = x; - vg_ui.mouse[1] = y; - - vg_ui.cur_vert = 0; - vg_ui.cur_indice = 0; - vg_ui.vert_start = 0; - vg_ui.indice_start = 0; - vg_ui.focused_control_hit = 0; - vg_ui.cursor = k_ui_cursor_default; - - if( vg_ui.ignore_input_frames ){ - vg_ui.ignore_input_frames --; - return; - } - - if( ui_click_down() ){ - vg_ui.mouse_click[0] = vg_ui.mouse[0]; - vg_ui.mouse_click[1] = vg_ui.mouse[1]; - } -} - -static u32 ui_colour( enum ui_scheme_colour id ) -{ - return vg_ui.scheme[ id ]; -} +/* 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 */ -static u32 ui_colourcont( enum ui_scheme_colour id ) -{ - if ( id < k_ui_bg+6 ) return ui_colour( k_ui_fg ); - else if( id < k_ui_fg ) return ui_colour( k_ui_bg+1 ); - else if( id < k_ui_hue ) return ui_colour( k_ui_bg+3 ); - else if( id < k_ui_red+k_ui_brighter ) return ui_colour( k_ui_fg ); - else return ui_colour( k_ui_fg+1 ); -} - -static u32 ui_ntext( ui_rect rect, const char *str, u32 len, ui_px scale, - enum ui_align align, u32 colour ) -{ - ui_rect text_cursor; - if( colour == 0 ) colour = ui_colour( k_ui_fg ); - - 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; - - u32 printed_chars = 0; - - if( align & (k_ui_align_middle|k_ui_align_bottom) ){ - ui_px height = ui_text_string_height( str ) * scale; - - if( align & k_ui_align_bottom ) - text_cursor[1] += rect[3]-height; - else - text_cursor[1] += (rect[3]-height)/2; - } - - while( (c = *(_c ++)) ){ - if( printed_chars >= len ){ - printed_chars = 0; - text_cursor[1] += 14*scale; - text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align ); - text_cursor[0] -= UI_GLYPH_SPACING_X*scale; - - u8 glyph_base[2]; - u8 glyph_index = '\x90'; - glyph_base[0] = glyph_index & 0xf; - glyph_base[1] = (glyph_index-glyph_base[0])>>4; - glyph_base[0] *= 8; - glyph_base[1] *= 8; - - ui_fill_rect( text_cursor, 0x00ffffff, (ui_px[4]) - { - glyph_base[0]+2, - glyph_base[1]+1, - glyph_base[0]+6, - glyph_base[1]+8 - }); - - text_cursor[0] += UI_GLYPH_SPACING_X*scale; - } - - if( c == '\n' ){ - text_cursor[1] += 14*scale; - text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align ); - printed_chars = 0; - 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, colour, (ui_px[4]) - { - glyph_base[0]+2, - glyph_base[1]+1, - glyph_base[0]+6, - glyph_base[1]+8 - }); - } - } - 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': colour = ui_colour( k_ui_fg ); break; - case '3'|'0'<<8: colour = ui_colour( k_ui_bg ); break; - case '3'|'1'<<8: colour = ui_colour( k_ui_red ); break; - case '3'|'2'<<8: colour = ui_colour( k_ui_green ); break; - case '3'|'3'<<8: colour = ui_colour( k_ui_yellow ); break; - case '3'|'4'<<8: colour = ui_colour( k_ui_blue ); break; - case '3'|'5'<<8: colour = ui_colour( k_ui_purple ); break; - case '3'|'6'<<8: colour = ui_colour( k_ui_aqua ); break; - case '3'|'7'<<8: colour = ui_colour( 0xffffffff ); break; - } - - colour &= 0x00ffffff; - 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; - printed_chars += 4; - continue; - } - - text_cursor[0] += UI_GLYPH_SPACING_X*scale; - printed_chars ++; - } - - return printed_chars; -} - -static void ui_text( ui_rect rect, const char *str, ui_px scale, - enum ui_align align, u32 colour ) -{ - ui_ntext( rect, str, 1024, scale, align, colour ); -} - -static void ui_image( ui_rect rect, GLuint image ) -{ - ui_flush( k_ui_shader_colour ); - - 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 ); -} - -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; - - return r | (g<<8) | (b<<16) | (a<<24); -} - -static void ui_defocus_all(void) -{ - if( vg_ui.focused_control_type == k_ui_control_textbox ){ - vg_info( "SDL_StopTextInput()\n" ); - SDL_StopTextInput(); - } - - vg_ui.focused_control_id = NULL; - vg_ui.focused_control_hit = 0; - vg_ui.focused_control_type = k_ui_control_none; -} - -static int ui_button( ui_rect rect, enum ui_scheme_colour colour ) -{ - int clickup= ui_click_up(), - click = ui_clicking() | clickup, - target = ui_inside_rect( rect, vg_ui.mouse_click ) && click, - hover = ui_inside_rect( rect, vg_ui.mouse ); - - u32 col_base = vg_ui.scheme[ colour ], - col_highlight = vg_ui.scheme[ k_ui_fg ], - col_hover = vg_ui.scheme[ colour + k_ui_brighter ]; - - if( hover ){ - vg_ui.cursor = k_ui_cursor_hand; - } - - if( click ){ - if( target ){ - if( hover ){ - if( clickup ){ - ui_fill( rect, col_highlight ); - vg_ui.ignore_input_frames = 2; - rect_copy( rect, vg_ui.click_fader ); - - rect_copy( rect, vg_ui.click_fader_end ); - vg_ui.click_fader_end[3] = 0; - ui_rect_center( rect, vg_ui.click_fader_end ); - - vg_ui.click_fade_opacity = 1.0f; - ui_defocus_all(); - return 1; - } - else{ - ui_fill( rect, col_highlight ); - return 0; - } - } - else{ - ui_fill( rect, col_base ); - ui_outline( rect, 1, col_highlight ); - return 0; - } - } - else{ - ui_fill( rect, col_base ); - return 0; - } - } - else{ - if( hover ){ - ui_fill( rect, col_hover ); - return 0; - } - else{ - ui_fill( rect, col_base ); - return 0; - } - } -} - -static int ui_button_text( ui_rect rect, const char *string, ui_px scale, - enum ui_scheme_colour 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, ui_colourcont(colour) ); - return result; -} - -static void ui_postrender(void) -{ - if( vg_ui.click_fade_opacity > 0.0f ){ - - float scale = vg_ui.click_fade_opacity; - scale = vg_maxf( 1.0f/255.0f, scale*scale ); - - vg_ui.click_fade_opacity -= vg.time_frame_delta * 3.8f; - u32 colour = (0x00ffffff & ui_colour(k_ui_fg))|0x7f000000; - - v4f begin, end, dest; - for( int i=0; i<4; i++ ){ - begin[i] = vg_ui.click_fader[i]; - end[i] = vg_ui.click_fader_end[i]+1; - } - - v4_lerp( end, begin, scale, dest ); - - ui_rect rect; - for( int i=0; i<4; i++ ){ - rect[i] = dest[i]; - } - - ui_fill( rect, colour ); - } - ui_flush( k_ui_shader_colour ); - - if( !vg_ui.focused_control_hit ){ - ui_defocus_all(); - } - - SDL_SetCursor( vg_ui.cursor_map[ vg_ui.cursor ] ); - SDL_ShowCursor(1); -} - - - -static void ui_dev_colourview(void) -{ - ui_rect window = {vg.window_x-256,0,256,vg.window_y}, swatch; - - const char *names[vg_list_size(vg_ui.scheme)] = { - [k_ui_bg] = "k_ui_bg", "k_ui_bg+1", "k_ui_bg+2", "k_ui_bg+3", - "k_ui_bg+4", "k_ui_bg+5", "k_ui_bg+6", "k_ui_bg+7", - - [k_ui_fg] = "k_ui_fg", "k_ui_fg+1", "k_ui_fg+2", "k_ui_fg+3", - "k_ui_fg+4", "k_ui_fg+5", "k_ui_fg+6", "k_ui_fg+7", - - [k_ui_red] = "k_ui_red", "k_ui_orange", "k_ui_yellow", "k_ui_green", - "k_ui_aqua", "k_ui_blue", "k_ui_purple", "k_ui_gray", - "k_ui_red+8","k_ui_orange+8","k_ui_yellow+8","k_ui_green+8", - "k_ui_aqua+8","k_ui_blue+8","k_ui_purple+8","k_ui_gray+8" }; - - ui_rect col[2]; - ui_split_ratio( window, k_ui_axis_v, 0.5f, 0.0f, col[0], col[1] ); - - for( int i=0; iterminator to start */ - int remaining_length = strlen( vg_ui.textbuf )+1-end; - memmove( &vg_ui.textbuf[ start ], - &vg_ui.textbuf[ end ], - remaining_length ); - return start; -} - -static void _ui_textbox_to_clipboard(void) -{ - int start, end; - _ui_textbox_make_selection( &start, &end ); - char buffer[512]; - - if( end-start ){ - memcpy( buffer, &vg_ui.textbuf[ start ], end-start ); - buffer[ end-start ] = 0x00; - SDL_SetClipboardText( buffer ); - } -} - -static void _ui_textbox_clipboard_paste(void) -{ - if( !SDL_HasClipboardText() ) - return; - - char *text = SDL_GetClipboardText(); - - if( !text ) - return; - - int datastart = _ui_textbox_delete_char( 0 ); - int length = strlen( text ); - int cpylength = _ui_textbox_makeroom( datastart, length ); - - memcpy( vg_ui.textbuf + datastart, text, cpylength); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, cpylength, 1 ); - SDL_free( text ); -} - -static void _ui_textbox_put_char( char c ) -{ - vg_ui.textbox.cursor_user = _ui_textbox_delete_char(0); - - if( _ui_textbox_makeroom( vg_ui.textbox.cursor_user, 1 ) ) - vg_ui.textbuf[ vg_ui.textbox.cursor_user ] = c; - - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, 1, 1 ); -} - -/* Receed secondary cursor */ -static void _ui_textbox_left_select(void) -{ - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, -1, 0 ); -} - -/* Match and receed both cursors */ -static void _ui_textbox_left(void) -{ - int cursor_diff = vg_ui.textbox.cursor_pos - vg_ui.textbox.cursor_user? 0: 1; - - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, -cursor_diff, 1 ); -} - -static void _ui_textbox_up(void) -{ - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ - int line_begin = vg_ui.textbox.cursor_user; - - while( line_begin ){ - if( vg_ui.textbuf[ line_begin-1 ] == '\n' ){ - break; - } - - line_begin --; - } - - if( line_begin ){ - int line_above_begin = line_begin-1; - - while( line_above_begin ){ - if( vg_ui.textbuf[ line_above_begin-1 ] == '\n' ){ - break; - } - - line_above_begin --; - } - - int offset = vg_ui.textbox.cursor_user - line_begin, - line_length_above = line_begin - line_above_begin -1; - - offset = VG_MIN( line_length_above, offset ); - - vg_ui.textbox.cursor_user = line_above_begin+offset; - vg_ui.textbox.cursor_pos = line_above_begin+offset; - } - else{ - vg_ui.textbox.cursor_user = line_begin; - vg_ui.textbox.cursor_pos = line_begin; - } - } - else{ - - } -} - -static void _ui_textbox_down(void) -{ - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ - int line_begin = vg_ui.textbox.cursor_user; - - while( line_begin ){ - if( vg_ui.textbuf[ line_begin-1 ] == '\n' ){ - break; - } - - line_begin --; - } - - int line_below_begin = vg_ui.textbox.cursor_user; - - while(1){ - if( vg_ui.textbuf[ line_below_begin ] == '\0' ){ - vg_ui.textbox.cursor_user = line_below_begin; - vg_ui.textbox.cursor_pos = line_below_begin; - return; - } - - if( vg_ui.textbuf[ line_below_begin ] == '\n' ){ - line_below_begin ++; - break; - } - - line_below_begin ++; - } - - int line_below_end = line_below_begin; - while(1){ - if( vg_ui.textbuf[ line_below_end ] == '\0' || - vg_ui.textbuf[ line_below_end ] == '\n' ){ - line_below_end ++; - break; - } - line_below_end ++; - } - - int offset = vg_ui.textbox.cursor_user - line_begin, - line_length_below = line_below_end - line_below_begin -1; - - offset = VG_MIN( line_length_below, offset ); - - vg_ui.textbox.cursor_user = line_below_begin+offset; - vg_ui.textbox.cursor_pos = line_below_begin+offset; - } - else{ - - } -} - -static void _ui_textbox_right_select(void) -{ - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, 1, 0 ); -} - -static void _ui_textbox_right(void) -{ - int cursor_diff = vg_ui.textbox.cursor_pos - vg_ui.textbox.cursor_user? 0: 1; - - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, +cursor_diff, 1 ); -} - -static void _ui_textbox_backspace(void) -{ - vg_ui.textbox.cursor_user = _ui_textbox_delete_char( -1 ); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; -} - -static void _ui_textbox_delete(void) -{ - vg_ui.textbox.cursor_user = _ui_textbox_delete_char( 1 ); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; -} - -static void _ui_textbox_home_select(void) -{ - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, -10000, 0 ); -} - -static void _ui_textbox_home(void) -{ - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, -10000, 1 ); -} - -static void _ui_textbox_end_select(void) -{ - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, 10000, 0 ); -} - -static void _ui_textbox_end(void) -{ - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, - vg_ui.textbox.len-1, 1 ); -} - -static void _ui_textbox_select_all(void) -{ - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, 10000, 0); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_pos, NULL, -10000, 0); -} - -static void _ui_textbox_cut(void) -{ - _ui_textbox_to_clipboard(); - vg_ui.textbox.cursor_user = _ui_textbox_delete_char(0); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; -} - -static void _ui_textbox_enter(void) -{ - if( vg_ui.focused_control_type == k_ui_control_textbox ){ - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ - _ui_textbox_put_char( '\n' ); - } - else{ - ui_defocus_all(); - } - } -} - -/* - * Handles binds - */ -static void _ui_proc_key( SDL_Keysym ev ) -{ - struct textbox_mapping - { - u16 mod; - SDL_Keycode key; - - void (*handler)(void); - } - mappings[] = - { - { 0, SDLK_LEFT, _ui_textbox_left }, - { KMOD_SHIFT, SDLK_LEFT, _ui_textbox_left_select }, - { 0, SDLK_RIGHT, _ui_textbox_right }, - { KMOD_SHIFT, SDLK_RIGHT, _ui_textbox_right_select }, - { 0, SDLK_DOWN, _ui_textbox_down }, - { 0, SDLK_UP, _ui_textbox_up }, - { 0, SDLK_BACKSPACE, _ui_textbox_backspace }, - { KMOD_SHIFT, SDLK_BACKSPACE, _ui_textbox_backspace }, - { KMOD_CTRL, SDLK_BACKSPACE, _ui_textbox_backspace }, - { 0, SDLK_DELETE, _ui_textbox_delete }, - { 0, SDLK_HOME, _ui_textbox_home }, - { KMOD_SHIFT, SDLK_HOME, _ui_textbox_home_select }, - { 0, SDLK_END, _ui_textbox_end }, - { KMOD_SHIFT, SDLK_END, _ui_textbox_end_select }, - { KMOD_CTRL, SDLK_a, _ui_textbox_select_all }, - { KMOD_CTRL, SDLK_c, _ui_textbox_to_clipboard }, - { KMOD_CTRL, SDLK_x, _ui_textbox_cut }, - { KMOD_CTRL, SDLK_v, _ui_textbox_clipboard_paste }, - { 0, SDLK_RETURN, _ui_textbox_enter }, -#if 0 - { KMOD_CTRL, SDLK_n, _ui_textbox_suggest_next }, - { KMOD_CTRL, SDLK_p, _ui_textbox_suggest_prev } -#endif - { 0, SDLK_ESCAPE, ui_defocus_all }, - }; - - if( vg_ui.focused_control_type == k_ui_control_none ){ - return; - } - - SDL_Keymod mod = 0; - - if( ev.mod & KMOD_SHIFT ) - mod |= KMOD_SHIFT; - - if( ev.mod & KMOD_CTRL ) - mod |= KMOD_CTRL; - - if( ev.mod & KMOD_ALT ) - mod |= KMOD_ALT; - - for( int i=0; ikey == ev.sym ){ - if( mapping->mod == 0 ){ - if( mod == 0 ){ - mapping->handler(); - return; - } - } - else if( (mod & mapping->mod) == mapping->mod ){ - mapping->handler(); - return; - } - } - } -} - -/* - * Callback for text entry mode - */ -VG_STATIC void ui_proc_utf8( const char *text ) -{ - const char *ptr = text; - - while( *ptr ){ - if( *ptr != '`' ) _ui_textbox_put_char( *ptr ); - ptr ++; - } -} - - - - - - - - - - - - - - - - - - - - - - -static void _ui_textbox_cursor_rect( ui_rect text_rect, ui_rect cursor ) -{ - int start = VG_MIN( vg_ui.textbox.cursor_pos, vg_ui.textbox.cursor_user ), - end = VG_MAX( vg_ui.textbox.cursor_pos, vg_ui.textbox.cursor_user ); - - cursor[0] = text_rect[0] + start*UI_GLYPH_SPACING_X-1; - cursor[1] = text_rect[1]; - cursor[2] = start == end? 2: (float)(end-start)*(float)UI_GLYPH_SPACING_X; - cursor[3] = 14; -} - -static void _ui_textbox_calc_index_from_grid( int co[3], int char_width ) -{ - int i[3] = {0,0,0}; - - char c; - while( (c = vg_ui.textbuf[i[2]]) ){ - if( i[1]==co[1] && i[0]>=co[0] ) break; - - if( i[0] >= char_width ){ - i[1] ++; - i[0] = 0; - } - - if( c >= 32 && c <= 126 ){ - i[0] ++; - i[2] ++; - } - else if( c == '\n' ){ - i[1] ++; - - if( i[1] > co[1] ) break; - - i[2] ++; - i[0] = 0; - } - else i[2] ++; - } - - co[0] = i[0]; - co[1] = i[1]; - co[2] = i[2]; -} - -static -void _ui_textbox_index_calc_coords( int co[3], int char_width ) -{ - co[0] = 0; - co[1] = 0; - - char c; - int i=0; - - while( (c = vg_ui.textbuf[i ++]) ){ - if( i > co[2] ) break; - if( co[0] >= char_width ){ - co[1] ++; - co[0] = 0; - } - if( c >= 32 && c <= 126 ) co[0] ++; - else if( c == '\n' ){ - co[1] ++; - co[0] = 0; - } - } -} - -static int _ui_textbox_run_remaining( int index[3], int char_width ) -{ - int i=0, printed_chars=0; - char c; - while( (c = vg_ui.textbuf[index[2] + (i ++)]) ){ - if( index[0]+i >= char_width ) break; - if( c >= 32 && c <= 126 ) printed_chars ++; - else if( c == '\n' ) break; - } - - return printed_chars+1; -} - -static int ui_textbox( ui_rect rect, char *buf, u32 len, u32 flags ) -{ - int clickup= ui_click_up(), - click = ui_clicking() | clickup, - target = ui_inside_rect( rect, vg_ui.mouse_click ) && click, - hover = ui_inside_rect( rect, vg_ui.mouse ); - - u32 col_base = ui_colour( k_ui_bg ), - col_highlight = ui_colour( k_ui_fg ), - col_cursor = (0x00ffffff & ui_colour(k_ui_fg))|0x7f000000; - - ui_px border = -1; - - ui_rect text_rect; - rect_copy( rect, text_rect ); - - if( flags & UI_TEXTBOX_MULTILINE ) text_rect[3] = rect[3]-16; - else text_rect[3] = 14; - - text_rect[2] -= 16; - ui_rect_center( rect, text_rect ); - - ui_px char_width = 1024; - - if( flags & UI_TEXTBOX_WRAP ) - char_width = text_rect[2] / UI_GLYPH_SPACING_X; - - if( hover ){ - vg_ui.cursor = k_ui_cursor_ibeam; - } - - if( vg_ui.focused_control_id == buf ){ - ui_fill( rect, col_base ); - ui_ntext( text_rect, buf, char_width, 1, k_ui_align_left, 0 ); - - if( (clickup||ui_click_down()) && !target ){ - ui_defocus_all(); - } - else{ - vg_ui.focused_control_hit = 1; - if( click && target ){ - int p0[3] ={ - (vg_ui.mouse_click[0] - text_rect[0]) / UI_GLYPH_SPACING_X, - (vg_ui.mouse_click[1] - text_rect[1]) / 14, - -1 - }, - p1[3] = { - (vg_ui.mouse[0] - text_rect[0]) / UI_GLYPH_SPACING_X, - (vg_ui.mouse[1] - text_rect[1]) / 14, - -1 - }; - - if( flags & UI_TEXTBOX_MULTILINE ){ - _ui_textbox_calc_index_from_grid( p0, char_width ); - _ui_textbox_calc_index_from_grid( p1, char_width ); - - vg_ui.textbox.cursor_pos = p0[2]; - vg_ui.textbox.cursor_user = p1[2]; - } - else{ - int max = strlen( buf ); - vg_ui.textbox.cursor_pos = VG_MAX( 0, VG_MIN( max, p0[0] )), - vg_ui.textbox.cursor_user = VG_MAX( 0, VG_MIN( max, p1[0] )); - } - } - - ui_outline( rect, -2, vg_ui.scheme[ k_ui_orange ] ); - - ui_rect cursor; - - int c0 = vg_ui.textbox.cursor_pos, - c1 = vg_ui.textbox.cursor_user, - start = VG_MIN( c0, c1 ), - end = VG_MAX( c0, c1 ), - chars = end-start; - - if( flags & (UI_TEXTBOX_MULTILINE|UI_TEXTBOX_WRAP) ){ - - int pos[3], remaining = chars; - - pos[2] = start; - _ui_textbox_index_calc_coords( pos, char_width ); - - if( start==end ){ - cursor[0] = text_rect[0] + pos[0]*UI_GLYPH_SPACING_X-1; - cursor[1] = text_rect[1] + pos[1]*14; - cursor[2] = 2; - cursor[3] = 13; - ui_fill( cursor, col_cursor ); - rect_copy( cursor, vg_ui.click_fader_end ); - } - else{ - while( remaining ){ - /* TODO: scan for newlines and stuff - * eol or char_width can have line breaks! */ - - int run = _ui_textbox_run_remaining( pos, char_width ); - run = VG_MIN( run, remaining ); - - cursor[0] = text_rect[0] + pos[0]*UI_GLYPH_SPACING_X-1; - cursor[1] = text_rect[1] + pos[1]*14; - cursor[2] = (float)(run)*(float)UI_GLYPH_SPACING_X; - cursor[3] = 13; - - ui_fill( cursor, col_cursor ); - - remaining -= run; - pos[0] = 0; - pos[1] ++; - pos[2] += run; - } - rect_copy( cursor, vg_ui.click_fader_end ); - } - } - else{ - cursor[0] = text_rect[0] + start*UI_GLYPH_SPACING_X-1; - cursor[1] = text_rect[1]; - cursor[3] = 13; - - if( start==end ){ - cursor[2] = 2; - } - else{ - cursor[2] = (float)(chars)*(float)UI_GLYPH_SPACING_X; - } - - if( (vg_ui.click_fade_opacity<=0.0f) && - ui_clip( rect, cursor, cursor ) ){ - ui_fill( cursor, col_cursor ); - } - - rect_copy( cursor, vg_ui.click_fader_end ); - } - } - - return 0; - } - - if( click ){ - if( target && hover ){ - ui_defocus_all(); - - ui_fill( rect, col_highlight ); - vg_ui.ignore_input_frames = 2; - rect_copy( rect, vg_ui.click_fader ); - rect_copy( rect, vg_ui.click_fader_end ); - - vg_ui.click_fade_opacity = 1.0f; - vg_ui.textbuf = buf; - vg_ui.focused_control_hit = 1; - vg_ui.focused_control_type = k_ui_control_textbox; - vg_ui.textbox.len = len; - vg_ui.textbox.flags = flags; - vg_ui.textbox.cursor_pos = 0; - vg_ui.textbox.cursor_user = 0; - - vg_info( "SDL_StartTextInput()\n" ); - SDL_StartTextInput(); - } - } - - ui_fill( rect, col_base ); - - if( hover ){ - ui_outline( rect, -1, col_highlight ); - } - - ui_ntext( text_rect, buf, char_width, 1, k_ui_align_left, 0 ); - return 0; -} - -#endif /* VG_IMGUI_H */ +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);