From f14b044ee3372d2e544825661328964d3a32bb74 Mon Sep 17 00:00:00 2001 From: hgn Date: Mon, 1 May 2023 19:43:12 +0100 Subject: [PATCH] textbox --- vg.h | 9 +- vg_console.h | 7 +- vg_imgui.h | 516 ++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 486 insertions(+), 46 deletions(-) diff --git a/vg.h b/vg.h index 0be8031..64f9187 100644 --- a/vg.h +++ b/vg.h @@ -332,7 +332,8 @@ VG_STATIC void _vg_process_events(void) SDL_Event event; while( SDL_PollEvent( &event ) ){ if( event.type == SDL_KEYDOWN ){ - console_proc_key( event.key.keysym ); + //console_proc_key( event.key.keysym ); + _ui_proc_key( event.key.keysym ); } else if( event.type == SDL_MOUSEWHEEL ){ vg.mouse_wheel[0] += event.wheel.preciseX; @@ -374,7 +375,8 @@ VG_STATIC void _vg_process_events(void) } } else if( event.type == SDL_TEXTINPUT ){ - console_proc_utf8( event.text.text ); + //console_proc_utf8( event.text.text ); + ui_proc_utf8( event.text.text ); } } @@ -757,6 +759,9 @@ VG_STATIC void _vg_init_window( const char *window_name ) vg_info( "CreateContext\n" ); + /* ????? */ + if( SDL_IsTextInputActive() ) SDL_StopTextInput(); + /* * OpenGL loading */ diff --git a/vg_console.h b/vg_console.h index 8d47210..dd9cc77 100644 --- a/vg_console.h +++ b/vg_console.h @@ -929,10 +929,13 @@ VG_STATIC void console_proc_key( SDL_Keysym ev ) if( ev.sym == SDLK_BACKQUOTE ){ vg_console.enabled = !vg_console.enabled; - if( vg_console.enabled ) + if( vg_console.enabled ){ + vg_info( "SDL_StartTextInput()\n" ); SDL_StartTextInput(); - else + } + else{ SDL_StopTextInput(); + } } if( !vg_console.enabled ) return; diff --git a/vg_imgui.h b/vg_imgui.h index deda2fc..16d2095 100644 --- a/vg_imgui.h +++ b/vg_imgui.h @@ -8,7 +8,6 @@ * 3. when the ui is in an interactive state, no controls should ever move */ - #ifndef VG_IMGUI_H #define VG_IMGUI_H @@ -82,14 +81,20 @@ struct{ cur_vert, cur_indice, vert_start, indice_start; + union { void *focused_control_id; /* uses the memory location of various locking controls as an id */ + char *textbuf; + }; + u32 focused_control_hit; enum ui_control_type{ k_ui_control_none, k_ui_control_textbox, } focused_control_type; + int cursor_user, cursor_pos, string_length; + u32 textbuf_len; GLuint tex_glyphs, vao, vbo, ebo; @@ -97,10 +102,20 @@ struct{ u32 mouse_state[2]; u32 ignore_input_frames; - ui_rect click_fader; + ui_rect click_fader, click_fader_end; float click_fade_opacity; ui_scheme scheme; + + enum ui_cursor{ + k_ui_cursor_default, + k_ui_cursor_ibeam, + k_ui_cursor_hand, + k_ui_cursor_max + } + cursor; + + SDL_Cursor *cursor_map[ k_ui_cursor_max ]; } static vg_ui = { .scheme = { @@ -350,6 +365,18 @@ VG_STATIC void _vg_ui_init(void) 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 { @@ -698,6 +725,7 @@ static void ui_prerender(void) 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 --; @@ -715,11 +743,23 @@ static u32 ui_colour( enum ui_scheme_colour id ) return vg_ui.scheme[ 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 void ui_text( ui_rect rect, const char *str, ui_px scale, - enum ui_align align ) + enum ui_align align, u32 colour ) { ui_rect text_cursor; - u32 colour = ui_colour( k_ui_fg ) & 0x00ffffff; + if( colour == 0 ) colour = ui_colour( k_ui_fg ); + + colour &= 0x00ffffff; const char *_c = str; u8 c; @@ -832,6 +872,7 @@ static u32 v4f_u32_colour( v4f colour ) static void ui_defocus_all(void) { if( vg_ui.focused_control_type == k_ui_control_textbox ){ + vg_info( "SDL_StopTextInput()\n" ); SDL_StopTextInput(); } @@ -851,6 +892,10 @@ static int ui_button( ui_rect rect, enum ui_scheme_colour 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 ){ @@ -858,6 +903,11 @@ static int ui_button( ui_rect rect, enum ui_scheme_colour colour ) 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; @@ -896,7 +946,7 @@ static int ui_button_text( ui_rect rect, const char *string, ui_px scale, 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_text( t, string, scale, k_ui_align_left, ui_colourcont(colour) ); return result; } @@ -907,13 +957,21 @@ static void ui_postrender(void) 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 * 1.8f; - u32 colour = v4f_u32_colour( (v4f){ 1.0f,1.0f,1.0f, 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; - rect[3] = (float)(vg_ui.click_fader[3]) * scale; - rect[2] = vg_ui.click_fader[2]; - ui_rect_center( vg_ui.click_fader, rect ); + for( int i=0; i<4; i++ ){ + rect[i] = dest[i]; + } ui_fill( rect, colour ); } @@ -922,8 +980,353 @@ static void ui_postrender(void) 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.cursor_user, + &vg_ui.cursor_pos, cpylength, 1 ); + SDL_free( text ); +} + +static void _ui_textbox_put_char( char c ) +{ + vg_ui.cursor_user = _ui_textbox_delete_char(0); + + if( _ui_textbox_makeroom( vg_ui.cursor_user, 1 ) ) + vg_ui.textbuf[ vg_ui.cursor_user ] = c; + + _ui_textbox_move_cursor( &vg_ui.cursor_user, &vg_ui.cursor_pos, 1, 1 ); +} + +/* Receed secondary cursor */ +static void _ui_textbox_left_select(void) +{ + _ui_textbox_move_cursor( &vg_ui.cursor_user, NULL, -1, 0 ); +} + +/* Match and receed both cursors */ +static void _ui_textbox_left(void) +{ + int cursor_diff = vg_ui.cursor_pos - vg_ui.cursor_user? 0: 1; + + _ui_textbox_move_cursor( &vg_ui.cursor_user, + &vg_ui.cursor_pos, -cursor_diff, 1 ); +} + +static void _ui_textbox_right_select(void) +{ + _ui_textbox_move_cursor( &vg_ui.cursor_user, NULL, 1, 0 ); +} + +static void _ui_textbox_right(void) +{ + int cursor_diff = vg_ui.cursor_pos - vg_ui.cursor_user? 0: 1; + + _ui_textbox_move_cursor( &vg_ui.cursor_user, + &vg_ui.cursor_pos, +cursor_diff, 1 ); +} + +static void _ui_textbox_backspace(void) +{ + vg_ui.cursor_user = _ui_textbox_delete_char( -1 ); + vg_ui.cursor_pos = vg_ui.cursor_user; +} + +static void _ui_textbox_delete(void) +{ + vg_ui.cursor_user = _ui_textbox_delete_char( 1 ); + vg_ui.cursor_pos = vg_ui.cursor_user; +} + +static void _ui_textbox_home_select(void) +{ + _ui_textbox_move_cursor( &vg_ui.cursor_user, NULL, -10000, 0 ); +} + +static void _ui_textbox_home(void) +{ + _ui_textbox_move_cursor( &vg_ui.cursor_user, + &vg_ui.cursor_pos, -10000, 1 ); +} + +static void _ui_textbox_end_select(void) +{ + _ui_textbox_move_cursor( &vg_ui.cursor_user, NULL, 10000, 0 ); +} + +static void _ui_textbox_end(void) +{ + _ui_textbox_move_cursor( &vg_ui.cursor_user, + &vg_ui.cursor_pos, + vg_ui.textbuf_len-1, 1 ); +} + +static void _ui_textbox_select_all(void) +{ + _ui_textbox_move_cursor( &vg_ui.cursor_user, NULL, 10000, 0); + _ui_textbox_move_cursor( &vg_ui.cursor_pos, NULL, -10000, 0); +} + +static void _ui_textbox_cut(void) +{ + _ui_textbox_to_clipboard(); + vg_ui.cursor_user = _ui_textbox_delete_char(0); + vg_ui.cursor_pos = vg_ui.cursor_user; +} + +static void _ui_textbox_enter(void) +{ + if( !strlen( vg_ui.textbuf ) ) + return; +} + +/* + * 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 }, +#if 0 + { 0, SDLK_DOWN, _ui_textbox_down }, + { 0, SDLK_UP, _ui_textbox_up }, +#endif + { 0, 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 } + }; + + 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.cursor_pos, vg_ui.cursor_user ), + end = VG_MAX( vg_ui.cursor_pos, vg_ui.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] = text_rect[3]; + +} + + + static int ui_textbox( ui_rect rect, char *buf, u32 len ) { int clickup= ui_click_up(), @@ -931,52 +1334,81 @@ static int ui_textbox( ui_rect rect, char *buf, u32 len ) target = ui_inside_rect( rect, vg_ui.mouse_click ) && click, hover = ui_inside_rect( rect, vg_ui.mouse ); - u32 col_base = vg_ui.scheme[ k_ui_bg ], - col_highlight = vg_ui.scheme[ k_ui_fg ]; + 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 ); + text_rect[3] = 14; + text_rect[2] -= 16; + ui_rect_center( rect, text_rect ); + + if( hover ){ + vg_ui.cursor = k_ui_cursor_ibeam; + } + if( vg_ui.focused_control_id == buf ){ ui_fill( rect, col_base ); - ui_outline( rect, -2, vg_ui.scheme[ k_ui_orange ] ); - vg_ui.focused_control_hit = 1; - return 0; - } + ui_text( text_rect, buf, 1, k_ui_align_left, 0 ); - 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 ); - vg_ui.click_fade_opacity = 1.0f; - vg_ui.focused_control_id = buf; - vg_ui.focused_control_hit = 1; - vg_ui.focused_control_type = k_ui_control_textbox; - SDL_StartTextInput(); - } - else{ - ui_fill( rect, col_highlight ); - } - } - else{ - ui_fill( rect, col_base ); - ui_outline( rect, -1, col_highlight ); - } + if( (clickup||ui_click_down()) && !target ){ + ui_defocus_all(); } else{ - ui_fill( rect, col_base ); + vg_ui.focused_control_hit = 1; + + if( click && target ){ + ui_px d0 = vg_ui.mouse_click[0] - text_rect[0], + d1 = vg_ui.mouse[0] - text_rect[0]; + + int max = strlen( vg_ui.textbuf ); + + vg_ui.cursor_pos = VG_MAX( 0, VG_MIN( max, d0/UI_GLYPH_SPACING_X )), + vg_ui.cursor_user= VG_MAX( 0, VG_MIN( max, d1/UI_GLYPH_SPACING_X )); + } + + ui_outline( rect, -2, vg_ui.scheme[ k_ui_orange ] ); + + ui_rect cursor; + _ui_textbox_cursor_rect( text_rect, cursor ); + rect_copy( cursor, vg_ui.click_fader_end ); + + if( (vg_ui.click_fade_opacity<=0.0f) && + ui_clip( rect, cursor, cursor ) ){ + ui_fill( cursor, col_cursor ); + } } + + return 0; } - else{ - ui_fill( rect, col_base ); - if( hover ){ - ui_outline( rect, -1, col_highlight ); + if( click ){ + if( target && hover ){ + ui_fill( rect, col_highlight ); + vg_ui.ignore_input_frames = 2; + rect_copy( rect, vg_ui.click_fader ); + _ui_textbox_cursor_rect( text_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.textbuf_len = len; + vg_info( "SDL_StartTextInput()\n" ); + SDL_StartTextInput(); } } + ui_fill( rect, col_base ); + + if( hover ){ + ui_outline( rect, -1, col_highlight ); + } + + ui_text( text_rect, buf, 1, k_ui_align_left, 0 ); return 0; } -- 2.25.1