1 /* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */
6 * 1. layout is defined by subdividing
7 * 2. a parent node should never be resized by the content after creation
8 * 3. when the ui is in an interactive state, no controls should ever move
17 #include "vg/vg_tex.h"
18 #include "vg/vg_shader.h"
21 typedef ui_px ui_rect
[4];
22 typedef struct ui_vert ui_vert
;
29 /* Relative to cursor p0 */
32 k_ui_align_left
= 0x0000| 0x00,
33 k_ui_align_right
= 0x0000| 0x01,
34 k_ui_align_center
= 0x0000| 0x02,
36 k_ui_align_middle
= 0x0100,
37 k_ui_align_middle_left
= 0x0100| 0x00,
38 k_ui_align_middle_right
= 0x0100| 0x01,
39 k_ui_align_middle_center
= 0x0100| 0x02,
41 k_ui_align_bottom
= 0x0200,
42 k_ui_align_bottom_left
= 0x0200| 0x00,
43 k_ui_align_bottom_right
= 0x0200| 0x01,
44 k_ui_align_bottom_center
= 0x0200| 0x02,
56 enum ui_scheme_colour
{
71 typedef u32 ui_scheme
[8*4];
73 #define UI_RGB( STDHEX ) 0xff000000 |\
74 ((STDHEX&0x000000ff)<<16) |\
75 ((STDHEX&0x0000ff00) ) |\
76 ((STDHEX&0x00ff0000)>>16)
79 struct ui_vert
*vertex_buffer
;
81 u32 max_verts
, max_indices
,
83 vert_start
, indice_start
;
85 void *focused_control_id
; /* uses the memory location of various locking
87 u32 focused_control_hit
;
94 GLuint tex_glyphs
, vao
, vbo
, ebo
;
96 ui_px mouse
[2], mouse_click
[2];
98 u32 ignore_input_frames
;
101 float click_fade_opacity
;
107 [ k_ui_bg
+0 ] = UI_RGB( 0x1d2021 ),
108 [ k_ui_bg
+1 ] = UI_RGB( 0x282828 ),
109 [ k_ui_bg
+2 ] = UI_RGB( 0x3c3836 ),
110 [ k_ui_bg
+3 ] = UI_RGB( 0x504945 ),
111 [ k_ui_bg
+4 ] = UI_RGB( 0x665c54 ),
112 [ k_ui_bg
+5 ] = UI_RGB( 0x7c6f64 ),
113 [ k_ui_bg
+6 ] = UI_RGB( 0x928374 ),
114 [ k_ui_bg
+7 ] = UI_RGB( 0xa89984 ),
116 [ k_ui_fg
+0 ] = UI_RGB( 0xebdbb2 ),
117 [ k_ui_fg
+1 ] = UI_RGB( 0xfbf1c7 ),
118 [ k_ui_fg
+2 ] = UI_RGB( 0xd5c4a1 ),
119 [ k_ui_fg
+3 ] = UI_RGB( 0xbdae93 ),
120 [ k_ui_fg
+4 ] = UI_RGB( 0xa89984 ),
121 [ k_ui_fg
+5 ] = UI_RGB( 0x000000 ),
122 [ k_ui_fg
+6 ] = UI_RGB( 0x000000 ),
123 [ k_ui_fg
+7 ] = UI_RGB( 0x000000 ),
125 [ k_ui_red
] = UI_RGB( 0xcc241d ),
126 [ k_ui_orange
] = UI_RGB( 0xd65d0e ),
127 [ k_ui_yellow
] = UI_RGB( 0xd79921 ),
128 [ k_ui_green
] = UI_RGB( 0x98971a ),
129 [ k_ui_aqua
] = UI_RGB( 0x689d6a ),
130 [ k_ui_blue
] = UI_RGB( 0x458588 ),
131 [ k_ui_purple
] = UI_RGB( 0xb16286 ),
132 [ k_ui_gray
] = UI_RGB( 0x928374 ),
133 [ k_ui_red
+ k_ui_brighter
] = UI_RGB( 0xfb4934 ),
134 [ k_ui_orange
+ k_ui_brighter
] = UI_RGB( 0xfe8019 ),
135 [ k_ui_yellow
+ k_ui_brighter
] = UI_RGB( 0xfabd2f ),
136 [ k_ui_green
+ k_ui_brighter
] = UI_RGB( 0xb8bb26 ),
137 [ k_ui_aqua
+ k_ui_brighter
] = UI_RGB( 0x8ec07c ),
138 [ k_ui_blue
+ k_ui_brighter
] = UI_RGB( 0x83a598 ),
139 [ k_ui_purple
+ k_ui_brighter
] = UI_RGB( 0xd3869b ),
140 [ k_ui_gray
+ k_ui_brighter
] = UI_RGB( 0xa89984 ),
144 static struct vg_shader _shader_ui
=
152 "layout (location=0) in vec2 a_co;"
153 "layout (location=1) in vec2 a_uv;"
154 "layout (location=2) in vec4 a_colour;"
157 "out vec2 aTexCoords;"
163 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
164 "aTexCoords = a_uv * 0.0078125;"
165 "aColour = a_colour;"
174 "uniform sampler2D uTexGlyphs;"
175 "out vec4 FragColor;"
177 "in vec2 aTexCoords;"
182 "vec2 rand_hash22( vec2 p )"
184 "vec3 p3 = fract(vec3(p.xyx) * 213.8976123);"
185 "p3 += dot(p3, p3.yzx+19.19);"
186 "return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));"
191 "vec4 diffuse = aColour;"
193 "if( diffuse.a == 0.0 )"
195 "diffuse.a = texture( uTexGlyphs, aTexCoords ).r;"
198 "FragColor = diffuse;"
203 static struct vg_shader _shader_ui_image
=
205 .name
= "[vg] ui_image",
211 "layout (location=0) in vec2 a_co;"
212 "layout (location=1) in vec2 a_uv;"
213 "layout (location=2) in vec4 a_colour;"
216 "out vec2 aTexCoords;"
222 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
223 "aTexCoords = a_uv * 0.0078125;"
224 "aColour = a_colour;"
233 "uniform sampler2D uTexImage;"
234 "out vec4 FragColor;"
236 "in vec2 aTexCoords;"
242 "vec4 colour = texture( uTexImage, aTexCoords );"
246 "float value = dot(vec4(1.0),colour)*0.25;"
248 "vec3 col = vec3(pow(cos(value*3.14159265*2.0)*0.5+0.5,0.5))"
249 "* vec3(step(value,0.5),0.3,step(1.0-value,0.5));"
250 "FragColor = vec4( col*4.0, 1.0 );"
253 "FragColor = colour;"
257 #define UI_GLYPH_SPACING_X 8
259 VG_STATIC
void _vg_ui_init(void)
261 if( !vg_shader_compile( &_shader_ui
) ||
262 !vg_shader_compile( &_shader_ui_image
) )
263 vg_fatal_error( "Failed to compile ui shader" );
267 * ----------------------------------------
270 vg_ui
.max_indices
= 20000;
271 vg_ui
.max_verts
= 30000;
273 /* Generate the buffer we are gonna be drawing to */
274 glGenVertexArrays( 1, &vg_ui
.vao
);
275 glGenBuffers( 1, &vg_ui
.vbo
);
276 glGenBuffers( 1, &vg_ui
.ebo
);
278 glBindVertexArray( vg_ui
.vao
);
279 glBindBuffer( GL_ARRAY_BUFFER
, vg_ui
.vbo
);
281 glBufferData( GL_ARRAY_BUFFER
,
282 vg_ui
.max_verts
* sizeof( struct ui_vert
),
283 NULL
, GL_DYNAMIC_DRAW
);
284 glBindVertexArray( vg_ui
.vao
);
286 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, vg_ui
.ebo
);
287 glBufferData( GL_ELEMENT_ARRAY_BUFFER
,
288 vg_ui
.max_indices
* sizeof( u16
), NULL
, GL_DYNAMIC_DRAW
);
293 u32
const stride
= sizeof( struct ui_vert
);
296 glVertexAttribPointer( 0, 2, GL_SHORT
, GL_FALSE
, stride
,
297 (void *)offsetof( struct ui_vert
, co
) );
298 glEnableVertexAttribArray( 0 );
301 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE
, GL_FALSE
, stride
,
302 (void *)offsetof( struct ui_vert
, uv
) );
303 glEnableVertexAttribArray( 1 );
306 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, stride
,
307 (void *)offsetof( struct ui_vert
, colour
) );
308 glEnableVertexAttribArray( 2 );
312 /* Alloc RAM default context */
313 u32 vert_size
= vg_ui
.max_verts
*sizeof(struct ui_vert
),
314 inds_size
= vg_align8( vg_ui
.max_indices
*sizeof(u16
) );
316 vg_ui
.vertex_buffer
= vg_linear_alloc( vg_mem
.rtmemory
, vert_size
);
317 vg_ui
.indice_buffer
= vg_linear_alloc( vg_mem
.rtmemory
, inds_size
);
320 * -----------------------------------------------------
323 /* Load default font */
325 #include "vg/vg_pxfont_thin.h"
328 u32 pixels
= 0, total
= 256*256, data
= 0;
331 while( pixels
< total
){
332 for( int b
= 31; b
>= 0; b
-- ){
333 image
[ pixels
++ ] = (compressed
[data
] & (0x1u
<< b
))? 0xffu
: 0x00u
;
335 if( pixels
>= total
){
343 glGenTextures( 1, &vg_ui
.tex_glyphs
);
344 glBindTexture( GL_TEXTURE_2D
, vg_ui
.tex_glyphs
);
345 glTexImage2D( GL_TEXTURE_2D
, 0, GL_R8
, 256, 256, 0,
346 GL_RED
, GL_UNSIGNED_BYTE
, image
);
349 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
350 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
351 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
352 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
360 static void rect_copy( ui_rect a
, ui_rect b
)
362 for( int i
=0; i
<4; i
++ )
366 VG_STATIC
void ui_flush( enum ui_shader shader
)
368 u32 vertex_offset
= vg_ui
.vert_start
*sizeof(ui_vert
),
369 vertex_count
= vg_ui
.cur_vert
-vg_ui
.vert_start
,
370 vertex_size
= vertex_count
*sizeof(ui_vert
),
372 indice_offset
= vg_ui
.indice_start
*sizeof(u16
),
373 indice_count
= vg_ui
.cur_indice
-vg_ui
.indice_start
,
374 indice_size
= indice_count
* sizeof(u16
);
376 if( !vertex_size
|| !indice_size
)
379 glBindVertexArray( vg_ui
.vao
);
380 glBindBuffer( GL_ARRAY_BUFFER
, vg_ui
.vbo
);
381 glBufferSubData( GL_ARRAY_BUFFER
, vertex_offset
, vertex_size
,
382 vg_ui
.vertex_buffer
+vg_ui
.vert_start
);
384 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, vg_ui
.ebo
);
385 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER
, indice_offset
, indice_size
,
386 vg_ui
.indice_buffer
+vg_ui
.indice_start
);
388 glEnable( GL_BLEND
);
389 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
390 glBlendEquation( GL_FUNC_ADD
);
392 m3x3f view
= M3X3_IDENTITY
;
393 m3x3_translate( view
, (v3f
){ -1.0f
, 1.0f
, 0.0f
} );
394 m3x3_scale( view
, (v3f
){ 1.0f
/((float)vg
.window_x
*0.5f
),
395 -1.0f
/((float)vg
.window_y
*0.5f
), 1.0f
} );
397 if( shader
== k_ui_shader_colour
){
398 glUseProgram( _shader_ui
.id
);
400 glUniformMatrix3fv( glGetUniformLocation( _shader_ui
.id
, "uPv" ), 1,
401 GL_FALSE
, (float *)view
);
403 glActiveTexture( GL_TEXTURE0
);
404 glBindTexture( GL_TEXTURE_2D
, vg_ui
.tex_glyphs
);
405 glUniform1i( glGetUniformLocation( _shader_ui
.id
, "uTexGlyphs" ), 0 );
407 else if( shader
== k_ui_shader_image
){
408 glUseProgram( _shader_ui_image
.id
);
409 glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image
.id
, "uPv" ), 1,
410 GL_FALSE
, (float *)view
);
411 glUniform1i( glGetUniformLocation(_shader_ui_image
.id
,"uTexImage"), 0 );
414 vg_fatal_error( "Invalid UI shader (%d)\n", shader
);
416 glDrawElements( GL_TRIANGLES
, indice_count
, GL_UNSIGNED_SHORT
,
417 (void *)(vg_ui
.indice_start
*sizeof(u16
)) );
419 glDisable( GL_BLEND
);
421 vg_ui
.indice_start
= vg_ui
.cur_indice
;
422 vg_ui
.vert_start
= vg_ui
.cur_vert
;
425 static void ui_fill_rect( ui_rect rect
, u32 colour
, ui_px uv
[4] )
427 /* this if far from ideal but stops us from crashing */
428 if( (vg_ui
.cur_vert
+ 4 > vg_ui
.max_verts
) ||
429 (vg_ui
.cur_indice
+ 6 > vg_ui
.max_indices
))
432 struct ui_vert
*vertices
= &vg_ui
.vertex_buffer
[ vg_ui
.cur_vert
];
433 u16
*indices
= &vg_ui
.indice_buffer
[ vg_ui
.cur_indice
];
435 for( int i
=0; i
<4; i
++ ){
436 vertices
[i
].colour
= colour
;
439 vertices
[0].co
[0] = rect
[0];
440 vertices
[0].co
[1] = rect
[1];
441 vertices
[0].uv
[0] = uv
[0];
442 vertices
[0].uv
[1] = uv
[1];
443 vertices
[1].co
[0] = rect
[0]+rect
[2];
444 vertices
[1].co
[1] = rect
[1];
445 vertices
[1].uv
[0] = uv
[2];
446 vertices
[1].uv
[1] = uv
[1];
447 vertices
[2].co
[0] = rect
[0]+rect
[2];
448 vertices
[2].co
[1] = rect
[1]+rect
[3];
449 vertices
[2].uv
[0] = uv
[2];
450 vertices
[2].uv
[1] = uv
[3];
451 vertices
[3].co
[0] = rect
[0];
452 vertices
[3].co
[1] = rect
[1]+rect
[3];
453 vertices
[3].uv
[0] = uv
[0];
454 vertices
[3].uv
[1] = uv
[3];
455 u16 ind_start
= vg_ui
.cur_vert
;
457 u16 start
= vg_ui
.cur_vert
;
458 u32 mesh
[] = { 0,2,1, 0,3,2 };
460 for( u32 i
=0; i
<vg_list_size(mesh
); i
++ ){
461 indices
[i
] = start
+mesh
[i
];
464 vg_ui
.cur_indice
+= 6;
468 static void ui_fill( ui_rect rect
, u32 colour
)
470 ui_fill_rect( rect
, colour
, (ui_px
[4]){ 4,4,4,4 } );
473 static void ui_outline( ui_rect rect
, ui_px thickness
, u32 colour
)
475 /* this if far from ideal but stops us from crashing */
476 if( (vg_ui
.cur_vert
+ 8 > vg_ui
.max_verts
) ||
477 (vg_ui
.cur_indice
+ 24 > vg_ui
.max_indices
))
480 struct ui_vert
*vertices
= &vg_ui
.vertex_buffer
[ vg_ui
.cur_vert
];
481 u16
*indices
= &vg_ui
.indice_buffer
[ vg_ui
.cur_indice
];
483 for( int i
=0; i
<8; i
++ ){
484 vertices
[i
].uv
[0] = 4;
485 vertices
[i
].uv
[1] = 4;
486 vertices
[i
].colour
= colour
;
489 vertices
[0].co
[0] = rect
[0];
490 vertices
[0].co
[1] = rect
[1];
491 vertices
[1].co
[0] = rect
[0]+rect
[2];
492 vertices
[1].co
[1] = rect
[1];
493 vertices
[2].co
[0] = rect
[0]+rect
[2];
494 vertices
[2].co
[1] = rect
[1]+rect
[3];
495 vertices
[3].co
[0] = rect
[0];
496 vertices
[3].co
[1] = rect
[1]+rect
[3];
497 vertices
[4].co
[0] = vertices
[0].co
[0]-thickness
;
498 vertices
[4].co
[1] = vertices
[0].co
[1]-thickness
;
499 vertices
[5].co
[0] = vertices
[1].co
[0]+thickness
;
500 vertices
[5].co
[1] = vertices
[1].co
[1]-thickness
;
501 vertices
[6].co
[0] = vertices
[2].co
[0]+thickness
;
502 vertices
[6].co
[1] = vertices
[2].co
[1]+thickness
;
503 vertices
[7].co
[0] = vertices
[3].co
[0]-thickness
;
504 vertices
[7].co
[1] = vertices
[3].co
[1]+thickness
;
506 u16 start
= vg_ui
.cur_vert
;
507 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 };
509 for( u32 i
=0; i
<vg_list_size(mesh
); i
++ ){
510 indices
[i
] = start
+mesh
[i
];
513 vg_ui
.cur_indice
+= 24;
517 static void ui_split_px( ui_rect rect
,
518 enum ui_axis other
, ui_px width
, ui_px pad
,
519 ui_rect l
, ui_rect r
)
521 enum ui_axis dir
= other
^ 0x1;
524 rect_copy( rect
, temp
);
526 l
[ dir
] = temp
[ dir
] + pad
;
527 r
[ dir
] = temp
[ dir
] + width
+ (pad
/2);
528 l
[ other
] = temp
[ other
] + pad
;
529 r
[ other
] = temp
[ other
] + pad
;
530 l
[ 2+dir
] = width
- ((3*pad
)/2);
531 r
[ 2+dir
] = temp
[ 2+dir
] - width
- ((3*pad
)/2);
532 l
[ 2+other
] = temp
[ 2+other
] - pad
*2;
533 r
[ 2+other
] = temp
[ 2+other
] - pad
*2;
536 static void ui_rect_center( ui_rect parent
, ui_rect rect
)
538 rect
[0] = parent
[0] + (parent
[2]-rect
[2])/2;
539 rect
[1] = parent
[1] + (parent
[3]-rect
[3])/2;
542 static void ui_fit_item( ui_rect rect
, ui_px size
[2], ui_rect d
)
544 i32 rp
= (i32
)rect
[2] * (i32
)size
[1],
545 rc
= (i32
)size
[0] * (i32
)rect
[3];
547 enum ui_axis dir
, other
;
548 if( rc
> rp
) dir
= k_ui_axis_h
;
549 else dir
= k_ui_axis_v
;
552 d
[2+dir
] = rect
[2+dir
];
553 d
[2+other
] = (rect
[2+dir
] * size
[other
]) / size
[dir
];
555 ui_rect_center( rect
, d
);
558 static void ui_split_ratio( ui_rect rect
, enum ui_axis dir
, float ratio
,
559 ui_px pad
, ui_rect l
, ui_rect r
)
561 ui_px width
= (float)rect
[ 2+(dir
^0x1) ] * ratio
;
562 ui_split_px( rect
, dir
, width
, pad
, l
, r
);
565 static void ui_rect_pad( ui_rect rect
, ui_px pad
)
573 static ui_px
ui_text_line_width( const char *str
)
576 const char *_c
= str
;
579 while( (c
= *(_c
++)) ){
580 if( c
>= 32 && c
<= 126 )
589 static ui_px
ui_text_string_height( const char *str
)
592 const char *_c
= str
;
595 while( (c
= *(_c
++)) ){
596 if( c
== '\n' ) height
++;
602 static ui_px
ui_text_aligned_x( const char *str
, ui_rect rect
, ui_px scale
,
603 enum ui_align align
)
605 if( align
== k_ui_align_left
){
609 ui_px width
= ui_text_line_width( str
) * scale
;
611 if( align
== k_ui_align_right
)
612 return rect
[0] + rect
[2]-width
;
614 return rect
[0] + (rect
[2]-width
)/2;
618 static ui_px
ui_min( ui_px a
, ui_px b
){ return a
<b
?a
:b
; }
619 static ui_px
ui_max( ui_px a
, ui_px b
){ return a
>b
?a
:b
; }
620 static ui_px
ui_clamp( ui_px a
, ui_px min
, ui_px max
)
622 return ui_min( max
, ui_max( a
, min
) );
625 static int ui_clip( ui_rect parent
, ui_rect child
, ui_rect clipped
)
627 ui_px parent_max
[2], child_max
[2];
628 parent_max
[0] = parent
[0]+parent
[2];
629 parent_max
[1] = parent
[1]+parent
[3];
630 child_max
[0] = child
[0]+child
[2];
631 child_max
[1] = child
[1]+child
[3];
633 clipped
[0] = ui_clamp( child
[0], parent
[0], parent_max
[0] );
634 clipped
[1] = ui_clamp( child
[1], parent
[1], parent_max
[1] );
635 clipped
[2] = ui_clamp( child_max
[0], parent
[0], parent_max
[0] );
636 clipped
[3] = ui_clamp( child_max
[1], parent
[1], parent_max
[1] );
638 if( clipped
[0] == clipped
[2] ||
639 clipped
[1] == clipped
[3] )
642 clipped
[2] -= clipped
[0];
643 clipped
[3] -= clipped
[1];
648 static int ui_inside_rect( ui_rect rect
, ui_px co
[2] )
650 if( co
[0] >= rect
[0] &&
652 co
[0] <= rect
[0]+rect
[2] &&
653 co
[1] <= rect
[1]+rect
[3] )
661 static int ui_click_down(void)
663 if( vg_ui
.ignore_input_frames
) return 0;
665 if( (vg_ui
.mouse_state
[0] & SDL_BUTTON(SDL_BUTTON_LEFT
)) &&
666 !(vg_ui
.mouse_state
[1] & SDL_BUTTON(SDL_BUTTON_LEFT
)) )
672 static int ui_clicking(void)
674 if( vg_ui
.ignore_input_frames
) return 0;
675 return vg_ui
.mouse_state
[0] & SDL_BUTTON(SDL_BUTTON_LEFT
);
678 static int ui_click_up(void)
680 if( vg_ui
.ignore_input_frames
) return 0;
681 if( (vg_ui
.mouse_state
[1] & SDL_BUTTON(SDL_BUTTON_LEFT
)) &&
682 !(vg_ui
.mouse_state
[0] & SDL_BUTTON(SDL_BUTTON_LEFT
)) )
688 static void ui_prerender(void)
691 vg_ui
.mouse_state
[1] = vg_ui
.mouse_state
[0];
692 vg_ui
.mouse_state
[0] = SDL_GetMouseState( &x
, &y
);
697 vg_ui
.cur_indice
= 0;
698 vg_ui
.vert_start
= 0;
699 vg_ui
.indice_start
= 0;
700 vg_ui
.focused_control_hit
= 0;
702 if( vg_ui
.ignore_input_frames
){
703 vg_ui
.ignore_input_frames
--;
707 if( ui_click_down() ){
708 vg_ui
.mouse_click
[0] = vg_ui
.mouse
[0];
709 vg_ui
.mouse_click
[1] = vg_ui
.mouse
[1];
713 static u32
ui_colour( enum ui_scheme_colour id
)
715 return vg_ui
.scheme
[ id
];
718 static void ui_text( ui_rect rect
, const char *str
, ui_px scale
,
719 enum ui_align align
)
722 u32 colour
= ui_colour( k_ui_fg
) & 0x00ffffff;
724 const char *_c
= str
;
727 text_cursor
[0] = ui_text_aligned_x( str
, rect
, scale
, align
);
728 text_cursor
[1] = rect
[1];
729 text_cursor
[2] = 8*scale
;
730 text_cursor
[3] = 14*scale
;
732 if( align
& (k_ui_align_middle
|k_ui_align_bottom
) ){
733 ui_px height
= ui_text_string_height( str
) * scale
;
735 if( align
& k_ui_align_bottom
)
736 text_cursor
[1] += rect
[3]-height
;
738 text_cursor
[1] += (rect
[3]-height
)/2;
741 while( (c
= *(_c
++)) ){
743 text_cursor
[1] += 14*scale
;
744 text_cursor
[0] = ui_text_aligned_x( _c
, rect
, scale
, align
);
750 glyph_base
[0] = glyph_index
& 0xf;
751 glyph_base
[1] = (glyph_index
-glyph_base
[0])>>4;
758 if( ui_clip( rect
, text_cursor
, rect_char
) ){
759 ui_fill_rect( rect_char
, colour
, (ui_px
[4])
768 else if( c
== '\x1B' ){
772 for( int i
=0; i
<3; i
++ ){
778 case '0': colour
= ui_colour( k_ui_fg
); break;
779 case '3'|'0'<<8: colour
= ui_colour( k_ui_bg
); break;
780 case '3'|'1'<<8: colour
= ui_colour( k_ui_red
); break;
781 case '3'|'2'<<8: colour
= ui_colour( k_ui_green
); break;
782 case '3'|'3'<<8: colour
= ui_colour( k_ui_yellow
); break;
783 case '3'|'4'<<8: colour
= ui_colour( k_ui_blue
); break;
784 case '3'|'5'<<8: colour
= ui_colour( k_ui_purple
); break;
785 case '3'|'6'<<8: colour
= ui_colour( k_ui_aqua
); break;
786 case '3'|'7'<<8: colour
= ui_colour( 0xffffffff ); break;
789 colour
&= 0x00ffffff;
793 colour_id
|= _c
[i
] << (i
*8);
803 else if( c
== '\t' ){
804 text_cursor
[0] += UI_GLYPH_SPACING_X
*scale
*4;
808 text_cursor
[0] += UI_GLYPH_SPACING_X
*scale
;
812 static void ui_image( ui_rect rect
, GLuint image
)
814 ui_flush( k_ui_shader_colour
);
816 glActiveTexture( GL_TEXTURE0
);
817 glBindTexture( GL_TEXTURE_2D
, image
);
818 ui_fill_rect( rect
, 0xffffffff, (ui_px
[4]){ 0,0, 255,255 } );
819 ui_flush( k_ui_shader_image
);
822 static u32
v4f_u32_colour( v4f colour
)
824 u32 r
= colour
[0] * 255.0f
,
825 g
= colour
[1] * 255.0f
,
826 b
= colour
[2] * 255.0f
,
827 a
= colour
[3] * 255.0f
;
829 return r
| (g
<<8) | (b
<<16) | (a
<<24);
832 static void ui_defocus_all(void)
834 if( vg_ui
.focused_control_type
== k_ui_control_textbox
){
838 vg_ui
.focused_control_id
= NULL
;
839 vg_ui
.focused_control_hit
= 0;
840 vg_ui
.focused_control_type
= k_ui_control_none
;
843 static int ui_button( ui_rect rect
, enum ui_scheme_colour colour
)
845 int clickup
= ui_click_up(),
846 click
= ui_clicking() | clickup
,
847 target
= ui_inside_rect( rect
, vg_ui
.mouse_click
) && click
,
848 hover
= ui_inside_rect( rect
, vg_ui
.mouse
);
850 u32 col_base
= vg_ui
.scheme
[ colour
],
851 col_highlight
= vg_ui
.scheme
[ k_ui_fg
],
852 col_hover
= vg_ui
.scheme
[ colour
+ k_ui_brighter
];
858 ui_fill( rect
, col_highlight
);
859 vg_ui
.ignore_input_frames
= 2;
860 rect_copy( rect
, vg_ui
.click_fader
);
861 vg_ui
.click_fade_opacity
= 1.0f
;
866 ui_fill( rect
, col_highlight
);
871 ui_fill( rect
, col_base
);
872 ui_outline( rect
, 1, col_highlight
);
877 ui_fill( rect
, col_base
);
883 ui_fill( rect
, col_hover
);
887 ui_fill( rect
, col_base
);
893 static int ui_button_text( ui_rect rect
, const char *string
, ui_px scale
,
894 enum ui_scheme_colour colour
)
896 int result
= ui_button( rect
, colour
);
897 ui_rect t
= { 0,0, ui_text_line_width( string
)*scale
, 14*scale
};
898 ui_rect_center( rect
, t
);
899 ui_text( t
, string
, scale
, k_ui_align_left
);
903 static void ui_postrender(void)
905 if( vg_ui
.click_fade_opacity
> 0.0f
){
907 float scale
= vg_ui
.click_fade_opacity
;
908 scale
= vg_maxf( 1.0f
/255.0f
, scale
*scale
);
910 vg_ui
.click_fade_opacity
-= vg
.time_frame_delta
* 1.8f
;
911 u32 colour
= v4f_u32_colour( (v4f
){ 1.0f
,1.0f
,1.0f
, scale
} );
914 rect
[3] = (float)(vg_ui
.click_fader
[3]) * scale
;
915 rect
[2] = vg_ui
.click_fader
[2];
916 ui_rect_center( vg_ui
.click_fader
, rect
);
918 ui_fill( rect
, colour
);
920 ui_flush( k_ui_shader_colour
);
922 if( !vg_ui
.focused_control_hit
){
927 static int ui_textbox( ui_rect rect
, char *buf
, u32 len
)
929 int clickup
= ui_click_up(),
930 click
= ui_clicking() | clickup
,
931 target
= ui_inside_rect( rect
, vg_ui
.mouse_click
) && click
,
932 hover
= ui_inside_rect( rect
, vg_ui
.mouse
);
934 u32 col_base
= vg_ui
.scheme
[ k_ui_bg
],
935 col_highlight
= vg_ui
.scheme
[ k_ui_fg
];
939 if( vg_ui
.focused_control_id
== buf
){
940 ui_fill( rect
, col_base
);
941 ui_outline( rect
, -2, vg_ui
.scheme
[ k_ui_orange
] );
942 vg_ui
.focused_control_hit
= 1;
950 ui_fill( rect
, col_highlight
);
951 vg_ui
.ignore_input_frames
= 2;
952 rect_copy( rect
, vg_ui
.click_fader
);
953 vg_ui
.click_fade_opacity
= 1.0f
;
954 vg_ui
.focused_control_id
= buf
;
955 vg_ui
.focused_control_hit
= 1;
956 vg_ui
.focused_control_type
= k_ui_control_textbox
;
957 SDL_StartTextInput();
960 ui_fill( rect
, col_highlight
);
964 ui_fill( rect
, col_base
);
965 ui_outline( rect
, -1, col_highlight
);
969 ui_fill( rect
, col_base
);
973 ui_fill( rect
, col_base
);
976 ui_outline( rect
, -1, col_highlight
);
983 #endif /* VG_IMGUI_H */