#define UI_TEXTBOX_MULTILINE 0x1
#define UI_TEXTBOX_WRAP 0x2
+#define UI_TEXTBOX_AUTOFOCUS 0x4
+#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{
struct ui_vert *vertex_buffer;
u32 flags;
}
textbox;
+
+ void (*textbuf_on_enter) ( char *buf, u32 len );
+ void (*textbuf_on_up) ( char *buf, u32 len );
+ void (*textbuf_on_down) ( char *buf, u32 len );
+ void (*textbuf_on_change)( char *buf, u32 len );
GLuint tex_glyphs, vao, vbo, ebo;
ui_px mouse[2], mouse_click[2];
u32 mouse_state[2];
u32 ignore_input_frames;
+ int wants_mouse;
ui_rect click_fader, click_fader_end;
float click_fade_opacity;
glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, indice_offset, indice_size,
vg_ui.indice_buffer+vg_ui.indice_start );
+ glDisable( GL_DEPTH_TEST );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glBlendEquation( GL_FUNC_ADD );
{
int length = 0;
const char *_c = str;
- char c;
+ u8 c;
while( (c = *(_c ++)) ){
- if( c >= 32 && c <= 126 )
- length ++;
- else if( c == '\n' )
- break;
+ if( c >= 32 ) length ++;
+ else if( c == '\n' ) break;
}
return length * 8;
{
int height = 1;
const char *_c = str;
- char c;
+ u8 c;
while( (c = *(_c ++)) ){
if( c == '\n' ) height ++;
return 0;
}
-static int ui_click_down(void)
+static int ui_click_down( u32 mask )
{
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)) )
+ if( (vg_ui.mouse_state[0] & mask) &&
+ !(vg_ui.mouse_state[1] & mask) )
return 1;
else
return 0;
}
-static int ui_clicking(void)
+static int ui_clicking( u32 mask )
{
if( vg_ui.ignore_input_frames ) return 0;
- return vg_ui.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT);
+ return vg_ui.mouse_state[0] & mask;
}
-static int ui_click_up(void)
+static int ui_click_up( u32 mask )
{
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)) )
+ if( (vg_ui.mouse_state[1] & mask) &&
+ !(vg_ui.mouse_state[0] & mask) )
return 1;
else
return 0;
vg_ui.indice_start = 0;
vg_ui.focused_control_hit = 0;
vg_ui.cursor = k_ui_cursor_default;
+ vg_ui.wants_mouse = 0;
if( vg_ui.ignore_input_frames ){
vg_ui.ignore_input_frames --;
return;
}
- if( ui_click_down() ){
+ if( ui_click_down(UI_MOUSE_LEFT)||ui_click_down(UI_MOUSE_MIDDLE) ){
vg_ui.mouse_click[0] = vg_ui.mouse[0];
vg_ui.mouse_click[1] = vg_ui.mouse[1];
}
printed_chars = 0;
continue;
}
- else
- if( c >= 33 ){
+ else if( c >= 33 ){
u8 glyph_base[2];
u8 glyph_index = c;
glyph_base[0] = glyph_index & 0xf;
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;
+ case '3'|'7'<<8: colour = 0xffffffff; break;
}
colour &= 0x00ffffff;
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, image );
- ui_fill_rect( rect, 0xffffffff, (ui_px[4]){ 0,0, 255,255 } );
+ ui_fill_rect( rect, 0xffffffff, (ui_px[4]){ 0,128, 128, 0 } );
ui_flush( k_ui_shader_image );
}
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_type = k_ui_control_none;
}
-static int ui_button( ui_rect rect, enum ui_scheme_colour colour )
+static int ui_colourbutton( ui_rect rect, enum ui_scheme_colour colour )
{
- int clickup= ui_click_up(),
- click = ui_clicking() | clickup,
+ int clickup= ui_click_up(UI_MOUSE_LEFT),
+ click = ui_clicking(UI_MOUSE_LEFT) | clickup,
target = ui_inside_rect( rect, vg_ui.mouse_click ) && click,
hover = ui_inside_rect( rect, vg_ui.mouse );
}
}
-static int ui_button_text( ui_rect rect, const char *string, ui_px scale,
- enum ui_scheme_colour colour )
+static int ui_colourbutton_text( ui_rect rect, const char *string, ui_px scale,
+ enum ui_scheme_colour colour )
{
- int result = ui_button( rect, colour );
+ int result = ui_colourbutton( 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 int ui_button_text( ui_rect rect, const char *string, ui_px scale )
+{
+ return ui_colourbutton_text( rect, string, scale, k_ui_bg+4 );
+}
+
static void ui_postrender(void)
{
if( vg_ui.click_fade_opacity > 0.0f ){
ui_defocus_all();
}
+ if( vg_ui.wants_mouse ){
+ SDL_SetWindowGrab( vg.window, SDL_FALSE );
+ SDL_SetRelativeMouseMode( SDL_FALSE );
+ }
+ else{
+ SDL_SetWindowGrab( vg.window, SDL_TRUE );
+ SDL_SetRelativeMouseMode( SDL_TRUE );
+ }
+
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;
}
}
+/*
+ * checkbox
+ * -----------------------------------------------------------------------------
+ */
+static int ui_checkbox( ui_rect rect, const char *str_label, int *data )
+{
+ ui_rect label, box;
+ ui_split_px( rect, k_ui_axis_v, rect[2]-rect[3], 0, label, box );
+ ui_text( label, str_label, 1, k_ui_align_middle_left, 0 );
+ int changed = ui_colourbutton( box, k_ui_bg );
+ if( changed )
+ *data = (*data) ^ 0x1;
+ if( *data ){
+ ui_rect_pad( box, 4 );
+ ui_fill( box, ui_colour( k_ui_orange ) );
+ }
+
+ return changed;
+}
/*
- * text box interface
+ * Textbox chaos
+ * -----------------------------------------------------------------------------
*/
+
static void _ui_textbox_make_selection( int *start, int *end )
{
*start = VG_MIN( vg_ui.textbox.cursor_pos, vg_ui.textbox.cursor_user );
}
}
else{
-
+ if( vg_ui.textbuf_on_up ){
+ vg_ui.textbuf_on_up( vg_ui.textbuf, vg_ui.textbox.len );
+ }
}
}
vg_ui.textbox.cursor_pos = line_below_begin+offset;
}
else{
-
+ if( vg_ui.textbuf_on_down ){
+ vg_ui.textbuf_on_down( vg_ui.textbuf, vg_ui.textbox.len );
+ }
}
}
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;
+ if( vg_ui.focused_control_type == k_ui_control_textbox ){
+ vg_ui.textbox.cursor_user = _ui_textbox_delete_char( -1 );
+ vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user;
+
+ if( vg_ui.textbuf_on_change )
+ vg_ui.textbuf_on_change( vg_ui.textbuf, vg_ui.textbox.len );
+ }
}
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;
+ if( vg_ui.focused_control_type == k_ui_control_textbox ){
+ vg_ui.textbox.cursor_user = _ui_textbox_delete_char( 1 );
+ vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user;
+
+ if( vg_ui.textbuf_on_change )
+ vg_ui.textbuf_on_change( vg_ui.textbuf, vg_ui.textbox.len );
+ }
}
static void _ui_textbox_home_select(void)
_ui_textbox_put_char( '\n' );
}
else{
- ui_defocus_all();
+ if( !(vg_ui.textbox.flags & UI_TEXTBOX_AUTOFOCUS ) )
+ 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; i<vg_list_size( mappings ); i++ ){
- struct textbox_mapping *mapping = &mappings[i];
- if( mapping->key == ev.sym ){
- if( mapping->mod == 0 ){
- if( mod == 0 ){
- mapping->handler();
- return;
- }
- }
- else if( (mod & mapping->mod) == mapping->mod ){
- mapping->handler();
- return;
- }
+ if( vg_ui.textbuf_on_enter ){
+ vg_ui.textbuf_on_enter( vg_ui.textbuf, vg_ui.textbox.len );
}
}
}
-/*
- * Callback for text entry mode
+/*
+ * based on a visual character coordinate relative to the anchor of the textbox,
+ * this works out the linear place in the buffer that coordinate maps to
+ *
+ * input coordinates go in co[0], co[1], and the result index is in co[2]
*/
-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 )
+static void _ui_textbox_calc_index_from_grid( int co[3], int wrap_length )
{
int i[3] = {0,0,0};
while( (c = vg_ui.textbuf[i[2]]) ){
if( i[1]==co[1] && i[0]>=co[0] ) break;
- if( i[0] >= char_width ){
+ if( i[0] >= wrap_length ){
i[1] ++;
i[0] = 0;
}
co[2] = i[2];
}
-static
-void _ui_textbox_index_calc_coords( int co[3], int char_width )
+/*
+ * based on the index specied in co[2], work out the visual character
+ * coordinates and store them in co[0], co[1]
+ */
+static void _ui_textbox_index_calc_coords( int co[3], int wrap_length )
{
co[0] = 0;
co[1] = 0;
while( (c = vg_ui.textbuf[i ++]) ){
if( i > co[2] ) break;
- if( co[0] >= char_width ){
+ if( co[0] >= wrap_length ){
co[1] ++;
co[0] = 0;
}
}
}
-static int _ui_textbox_run_remaining( int index[3], int char_width )
+/*
+ * calculate the number of characters remaining until either:
+ * - the wrap_length limit is hit
+ * - end of the line/string
+ *
+ * index must be fully populated with visual X/Y, and linear index
+ */
+static int _ui_textbox_run_remaining( int index[3], int wrap_length )
{
int i=0, printed_chars=0;
char c;
while( (c = vg_ui.textbuf[index[2] + (i ++)]) ){
- if( index[0]+i >= char_width ) break;
+ if( index[0]+i >= wrap_length ) break;
if( c >= 32 && c <= 126 ) printed_chars ++;
else if( c == '\n' ) break;
}
static int ui_textbox( ui_rect rect, char *buf, u32 len, u32 flags )
{
- int clickup= ui_click_up(),
- click = ui_clicking() | clickup,
+ int clickup= ui_click_up(UI_MOUSE_LEFT),
+ click = ui_clicking(UI_MOUSE_LEFT) | clickup,
target = ui_inside_rect( rect, vg_ui.mouse_click ) && click,
hover = ui_inside_rect( rect, vg_ui.mouse );
text_rect[2] -= 16;
ui_rect_center( rect, text_rect );
- ui_px char_width = 1024;
+ ui_px wrap_length = 1024;
if( flags & UI_TEXTBOX_WRAP )
- char_width = text_rect[2] / UI_GLYPH_SPACING_X;
+ wrap_length = 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 );
+ ui_ntext( text_rect, buf, wrap_length, 1, k_ui_align_left, 0 );
- if( (clickup||ui_click_down()) && !target ){
+ if( !(flags & UI_TEXTBOX_AUTOFOCUS) &&
+ ((clickup||ui_click_down(UI_MOUSE_LEFT)) && !target) ){
ui_defocus_all();
}
else{
};
if( flags & UI_TEXTBOX_MULTILINE ){
- _ui_textbox_calc_index_from_grid( p0, char_width );
- _ui_textbox_calc_index_from_grid( p1, char_width );
+ _ui_textbox_calc_index_from_grid( p0, wrap_length );
+ _ui_textbox_calc_index_from_grid( p1, wrap_length );
vg_ui.textbox.cursor_pos = p0[2];
vg_ui.textbox.cursor_user = p1[2];
int pos[3], remaining = chars;
pos[2] = start;
- _ui_textbox_index_calc_coords( pos, char_width );
+ _ui_textbox_index_calc_coords( pos, wrap_length );
if( start==end ){
cursor[0] = text_rect[0] + pos[0]*UI_GLYPH_SPACING_X-1;
else{
while( remaining ){
/* TODO: scan for newlines and stuff
- * eol or char_width can have line breaks! */
+ * eol or wrap_length can have line breaks! */
- int run = _ui_textbox_run_remaining( pos, char_width );
+ int run = _ui_textbox_run_remaining( pos, wrap_length );
run = VG_MIN( run, remaining );
cursor[0] = text_rect[0] + pos[0]*UI_GLYPH_SPACING_X-1;
return 0;
}
- if( click ){
- if( target && hover ){
+ if( click || (flags & UI_TEXTBOX_AUTOFOCUS) ){
+ if( (target && hover) || (flags & UI_TEXTBOX_AUTOFOCUS) ){
ui_defocus_all();
ui_fill( rect, col_highlight );
vg_ui.textbox.cursor_pos = 0;
vg_ui.textbox.cursor_user = 0;
- vg_info( "SDL_StartTextInput()\n" );
SDL_StartTextInput();
}
}
ui_outline( rect, -1, col_highlight );
}
- ui_ntext( text_rect, buf, char_width, 1, k_ui_align_left, 0 );
+ ui_ntext( text_rect, buf, wrap_length, 1, k_ui_align_left, 0 );
return 0;
}
+/*
+ * Input handling
+ * -----------------------------------------------------------------------------
+ */
+
+/*
+ * Handles binds
+ */
+static void _ui_proc_key( SDL_Keysym ev )
+{
+ if( vg_ui.focused_control_type != k_ui_control_textbox ){
+ return;
+ }
+
+ 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 },
+ { 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; i<vg_list_size( mappings ); i++ ){
+ struct textbox_mapping *mapping = &mappings[i];
+
+ if( mapping->key == 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 )
+{
+ if( vg_ui.focused_control_type == k_ui_control_textbox ){
+ const char *ptr = text;
+
+ while( *ptr ){
+ if( *ptr != '`' ) _ui_textbox_put_char( *ptr );
+ ptr ++;
+ }
+
+ if( vg_ui.textbuf_on_change ){
+ vg_ui.textbuf_on_change( vg_ui.textbuf, vg_ui.textbox.len );
+ }
+ }
+}
+
#endif /* VG_IMGUI_H */