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 u32 ui_colour
;
22 typedef ui_px ui_rect
[4];
23 typedef struct ui_vert ui_vert
;
30 /* Relative to cursor p0 */
33 k_ui_align_left
= 0x0000| 0x00,
34 k_ui_align_right
= 0x0000| 0x01,
35 k_ui_align_center
= 0x0000| 0x02,
37 k_ui_align_middle
= 0x0100,
38 k_ui_align_middle_left
= 0x0100| 0x00,
39 k_ui_align_middle_right
= 0x0100| 0x01,
40 k_ui_align_middle_center
= 0x0100| 0x02,
42 k_ui_align_bottom
= 0x0200,
43 k_ui_align_bottom_left
= 0x0200| 0x00,
44 k_ui_align_bottom_right
= 0x0200| 0x01,
45 k_ui_align_bottom_center
= 0x0200| 0x02,
59 struct ui_vert
*vertex_buffer
;
61 u32 max_verts
, max_indices
,
63 vert_start
, indice_start
;
65 GLuint tex_glyphs
, vao
, vbo
, ebo
;
67 ui_px mouse
[2], mouse_click
[2];
69 u32 ignore_input_frames
;
72 float click_fade_opacity
;
76 static struct vg_shader _shader_ui
=
84 "layout (location=0) in vec2 a_co;"
85 "layout (location=1) in vec2 a_uv;"
86 "layout (location=2) in vec4 a_colour;"
89 "out vec2 aTexCoords;"
95 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
96 "aTexCoords = a_uv * 0.0078125;"
106 "uniform sampler2D uTexGlyphs;"
107 "out vec4 FragColor;"
109 "in vec2 aTexCoords;"
117 "vec4 glyph = vec4(1.0,1.0,1.0,1.0);"
119 "if( aColour.a == 0.0 )"
121 "glyph = texture( uTexGlyphs, aTexCoords );"
122 "glyph.a = smoothstep( 0.47, 0.53, glyph.r );"
126 "glyph.a = aColour.a;"
129 "FragColor = vec4( aColour.rgb, glyph.a );"
134 static struct vg_shader _shader_ui_image
=
136 .name
= "[vg] ui_image",
142 "layout (location=0) in vec2 a_co;"
143 "layout (location=1) in vec2 a_uv;"
144 "layout (location=2) in vec4 a_colour;"
147 "out vec2 aTexCoords;"
153 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
154 "aTexCoords = a_uv * 0.0078125;"
155 "aColour = a_colour;"
164 "uniform sampler2D uTexImage;"
165 "out vec4 FragColor;"
167 "in vec2 aTexCoords;"
173 "vec4 colour = texture( uTexImage, aTexCoords );"
177 "float value = dot(vec4(1.0),colour)*0.25;"
179 "vec3 col = vec3(pow(cos(value*3.14159265*2.0)*0.5+0.5,0.5))"
180 "* vec3(step(value,0.5),0.3,step(1.0-value,0.5));"
181 "FragColor = vec4( col*4.0, 1.0 );"
184 "FragColor = colour;"
188 #define UI_GLYPH_SPACING_X 8
190 VG_STATIC
void _vg_ui_init(void)
192 if( !vg_shader_compile( &_shader_ui
) ||
193 !vg_shader_compile( &_shader_ui_image
) )
194 vg_fatal_error( "Failed to compile ui shader" );
198 * ----------------------------------------
201 vg_uictx
.max_indices
= 20000;
202 vg_uictx
.max_verts
= 30000;
204 /* Generate the buffer we are gonna be drawing to */
205 glGenVertexArrays( 1, &vg_uictx
.vao
);
206 glGenBuffers( 1, &vg_uictx
.vbo
);
207 glGenBuffers( 1, &vg_uictx
.ebo
);
209 glBindVertexArray( vg_uictx
.vao
);
210 glBindBuffer( GL_ARRAY_BUFFER
, vg_uictx
.vbo
);
212 glBufferData( GL_ARRAY_BUFFER
,
213 vg_uictx
.max_verts
* sizeof( struct ui_vert
),
214 NULL
, GL_DYNAMIC_DRAW
);
215 glBindVertexArray( vg_uictx
.vao
);
217 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, vg_uictx
.ebo
);
218 glBufferData( GL_ELEMENT_ARRAY_BUFFER
,
219 vg_uictx
.max_indices
* sizeof( u16
), NULL
, GL_DYNAMIC_DRAW
);
224 u32
const stride
= sizeof( struct ui_vert
);
227 glVertexAttribPointer( 0, 2, GL_SHORT
, GL_FALSE
, stride
,
228 (void *)offsetof( struct ui_vert
, co
) );
229 glEnableVertexAttribArray( 0 );
232 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE
, GL_FALSE
, stride
,
233 (void *)offsetof( struct ui_vert
, uv
) );
234 glEnableVertexAttribArray( 1 );
237 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, stride
,
238 (void *)offsetof( struct ui_vert
, colour
) );
239 glEnableVertexAttribArray( 2 );
243 /* Alloc RAM default context */
244 u32 vert_size
= vg_uictx
.max_verts
*sizeof(struct ui_vert
),
245 inds_size
= vg_align8( vg_uictx
.max_indices
*sizeof(u16
) );
247 vg_uictx
.vertex_buffer
= vg_linear_alloc( vg_mem
.rtmemory
, vert_size
);
248 vg_uictx
.indice_buffer
= vg_linear_alloc( vg_mem
.rtmemory
, inds_size
);
251 * -----------------------------------------------------
254 /* Load default font */
256 #include "vg/vg_pxfont_thin.h"
259 u32 pixels
= 0, total
= 256*256, data
= 0;
262 while( pixels
< total
){
263 for( int b
= 31; b
>= 0; b
-- ){
264 image
[ pixels
++ ] = (compressed
[data
] & (0x1u
<< b
))? 0xffu
: 0x00u
;
266 if( pixels
>= total
){
274 glGenTextures( 1, &vg_uictx
.tex_glyphs
);
275 glBindTexture( GL_TEXTURE_2D
, vg_uictx
.tex_glyphs
);
276 glTexImage2D( GL_TEXTURE_2D
, 0, GL_R8
, 256, 256, 0,
277 GL_RED
, GL_UNSIGNED_BYTE
, image
);
280 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
281 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
282 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
283 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
291 static void rect_copy( ui_rect a
, ui_rect b
)
293 for( int i
=0; i
<4; i
++ )
297 VG_STATIC
void ui_flush( enum ui_shader shader
)
299 u32 vertex_offset
= vg_uictx
.vert_start
*sizeof(ui_vert
),
300 vertex_count
= vg_uictx
.cur_vert
-vg_uictx
.vert_start
,
301 vertex_size
= vertex_count
*sizeof(ui_vert
),
303 indice_offset
= vg_uictx
.indice_start
*sizeof(u16
),
304 indice_count
= vg_uictx
.cur_indice
-vg_uictx
.indice_start
,
305 indice_size
= indice_count
* sizeof(u16
);
307 if( !vertex_size
|| !indice_size
)
310 glBindVertexArray( vg_uictx
.vao
);
311 glBindBuffer( GL_ARRAY_BUFFER
, vg_uictx
.vbo
);
312 glBufferSubData( GL_ARRAY_BUFFER
, vertex_offset
, vertex_size
,
313 vg_uictx
.vertex_buffer
+vg_uictx
.vert_start
);
315 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, vg_uictx
.ebo
);
316 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER
, indice_offset
, indice_size
,
317 vg_uictx
.indice_buffer
+vg_uictx
.indice_start
);
319 glEnable( GL_BLEND
);
320 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
321 glBlendEquation( GL_FUNC_ADD
);
323 m3x3f view
= M3X3_IDENTITY
;
324 m3x3_translate( view
, (v3f
){ -1.0f
, 1.0f
, 0.0f
} );
325 m3x3_scale( view
, (v3f
){ 1.0f
/((float)vg
.window_x
*0.5f
),
326 -1.0f
/((float)vg
.window_y
*0.5f
), 1.0f
} );
328 if( shader
== k_ui_shader_colour
){
329 glUseProgram( _shader_ui
.id
);
331 glUniformMatrix3fv( glGetUniformLocation( _shader_ui
.id
, "uPv" ), 1,
332 GL_FALSE
, (float *)view
);
334 glActiveTexture( GL_TEXTURE0
);
335 glBindTexture( GL_TEXTURE_2D
, vg_uictx
.tex_glyphs
);
336 glUniform1i( glGetUniformLocation( _shader_ui
.id
, "uTexGlyphs" ), 0 );
338 else if( shader
== k_ui_shader_image
){
339 glUseProgram( _shader_ui_image
.id
);
340 glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image
.id
, "uPv" ), 1,
341 GL_FALSE
, (float *)view
);
342 glUniform1i( glGetUniformLocation(_shader_ui_image
.id
,"uTexImage"), 0 );
345 vg_fatal_error( "Invalid UI shader (%d)\n", shader
);
347 glDrawElements( GL_TRIANGLES
, indice_count
, GL_UNSIGNED_SHORT
,
348 (void *)(vg_uictx
.indice_start
*sizeof(u16
)) );
350 glDisable( GL_BLEND
);
352 vg_uictx
.indice_start
= vg_uictx
.cur_indice
;
353 vg_uictx
.vert_start
= vg_uictx
.cur_vert
;
356 static void ui_fill_rect( ui_rect rect
, u32 colour
, ui_px uv
[4] )
358 /* this if far from ideal but stops us from crashing */
359 if( (vg_uictx
.cur_vert
+ 4 > vg_uictx
.max_verts
) ||
360 (vg_uictx
.cur_indice
+ 6 > vg_uictx
.max_indices
))
363 struct ui_vert
*vertices
= &vg_uictx
.vertex_buffer
[ vg_uictx
.cur_vert
];
364 u16
*indices
= &vg_uictx
.indice_buffer
[ vg_uictx
.cur_indice
];
366 for( int i
=0; i
<4; i
++ ){
367 vertices
[i
].colour
= colour
;
370 vertices
[0].co
[0] = rect
[0];
371 vertices
[0].co
[1] = rect
[1];
372 vertices
[0].uv
[0] = uv
[0];
373 vertices
[0].uv
[1] = uv
[1];
374 vertices
[1].co
[0] = rect
[0]+rect
[2];
375 vertices
[1].co
[1] = rect
[1];
376 vertices
[1].uv
[0] = uv
[2];
377 vertices
[1].uv
[1] = uv
[1];
378 vertices
[2].co
[0] = rect
[0]+rect
[2];
379 vertices
[2].co
[1] = rect
[1]+rect
[3];
380 vertices
[2].uv
[0] = uv
[2];
381 vertices
[2].uv
[1] = uv
[3];
382 vertices
[3].co
[0] = rect
[0];
383 vertices
[3].co
[1] = rect
[1]+rect
[3];
384 vertices
[3].uv
[0] = uv
[0];
385 vertices
[3].uv
[1] = uv
[3];
386 u16 ind_start
= vg_uictx
.cur_vert
;
388 u16 start
= vg_uictx
.cur_vert
;
389 u32 mesh
[] = { 0,2,1, 0,3,2 };
391 for( u32 i
=0; i
<vg_list_size(mesh
); i
++ ){
392 indices
[i
] = start
+mesh
[i
];
395 vg_uictx
.cur_indice
+= 6;
396 vg_uictx
.cur_vert
+= 4;
399 static void ui_fill( ui_rect rect
, u32 colour
)
401 ui_fill_rect( rect
, colour
, (ui_px
[4]){ 4,4,4,4 } );
404 static void ui_outline( ui_rect rect
, ui_px thickness
, u32 colour
)
406 /* this if far from ideal but stops us from crashing */
407 if( (vg_uictx
.cur_vert
+ 8 > vg_uictx
.max_verts
) ||
408 (vg_uictx
.cur_indice
+ 24 > vg_uictx
.max_indices
))
411 struct ui_vert
*vertices
= &vg_uictx
.vertex_buffer
[ vg_uictx
.cur_vert
];
412 u16
*indices
= &vg_uictx
.indice_buffer
[ vg_uictx
.cur_indice
];
414 for( int i
=0; i
<8; i
++ ){
415 vertices
[i
].uv
[0] = 4;
416 vertices
[i
].uv
[1] = 4;
417 vertices
[i
].colour
= colour
;
420 vertices
[0].co
[0] = rect
[0];
421 vertices
[0].co
[1] = rect
[1];
422 vertices
[1].co
[0] = rect
[0]+rect
[2];
423 vertices
[1].co
[1] = rect
[1];
424 vertices
[2].co
[0] = rect
[0]+rect
[2];
425 vertices
[2].co
[1] = rect
[1]+rect
[3];
426 vertices
[3].co
[0] = rect
[0];
427 vertices
[3].co
[1] = rect
[1]+rect
[3];
428 vertices
[4].co
[0] = vertices
[0].co
[0]-thickness
;
429 vertices
[4].co
[1] = vertices
[0].co
[1]-thickness
;
430 vertices
[5].co
[0] = vertices
[1].co
[0]+thickness
;
431 vertices
[5].co
[1] = vertices
[1].co
[1]-thickness
;
432 vertices
[6].co
[0] = vertices
[2].co
[0]+thickness
;
433 vertices
[6].co
[1] = vertices
[2].co
[1]+thickness
;
434 vertices
[7].co
[0] = vertices
[3].co
[0]-thickness
;
435 vertices
[7].co
[1] = vertices
[3].co
[1]+thickness
;
437 u16 start
= vg_uictx
.cur_vert
;
438 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 };
440 for( u32 i
=0; i
<vg_list_size(mesh
); i
++ ){
441 indices
[i
] = start
+mesh
[i
];
444 vg_uictx
.cur_indice
+= 24;
445 vg_uictx
.cur_vert
+= 8;
448 static void ui_split_px( ui_rect rect
,
449 enum ui_axis other
, ui_px width
, ui_px pad
,
450 ui_rect l
, ui_rect r
)
452 enum ui_axis dir
= other
^ 0x1;
455 rect_copy( rect
, temp
);
457 l
[ dir
] = temp
[ dir
] + pad
;
458 r
[ dir
] = temp
[ dir
] + width
+ (pad
/2);
459 l
[ other
] = temp
[ other
] + pad
;
460 r
[ other
] = temp
[ other
] + pad
;
461 l
[ 2+dir
] = width
- ((3*pad
)/2);
462 r
[ 2+dir
] = temp
[ 2+dir
] - width
- ((3*pad
)/2);
463 l
[ 2+other
] = temp
[ 2+other
] - pad
*2;
464 r
[ 2+other
] = temp
[ 2+other
] - pad
*2;
467 static void ui_rect_center( ui_rect parent
, ui_rect rect
)
469 rect
[0] = parent
[0] + (parent
[2]-rect
[2])/2;
470 rect
[1] = parent
[1] + (parent
[3]-rect
[3])/2;
473 static void ui_fit_item( ui_rect rect
, ui_px size
[2], ui_rect d
)
475 i32 rp
= (i32
)rect
[2] * (i32
)size
[1],
476 rc
= (i32
)size
[0] * (i32
)rect
[3];
478 enum ui_axis dir
, other
;
479 if( rc
> rp
) dir
= k_ui_axis_h
;
480 else dir
= k_ui_axis_v
;
483 d
[2+dir
] = rect
[2+dir
];
484 d
[2+other
] = (rect
[2+dir
] * size
[other
]) / size
[dir
];
486 ui_rect_center( rect
, d
);
489 static void ui_split_ratio( ui_rect rect
, enum ui_axis dir
, float ratio
,
490 ui_px pad
, ui_rect l
, ui_rect r
)
492 ui_px width
= (float)rect
[ 2+(dir
^0x1) ] * ratio
;
493 ui_split_px( rect
, dir
, width
, pad
, l
, r
);
496 static void ui_rect_pad( ui_rect rect
, ui_px pad
)
504 static ui_px
ui_text_line_width( const char *str
)
507 const char *_c
= str
;
510 while( (c
= *(_c
++)) ){
511 if( c
>= 32 && c
<= 126 )
520 static ui_px
ui_text_string_height( const char *str
)
523 const char *_c
= str
;
526 while( (c
= *(_c
++)) ){
527 if( c
== '\n' ) height
++;
533 static ui_px
ui_text_aligned_x( const char *str
, ui_rect rect
, ui_px scale
,
534 enum ui_align align
)
536 if( align
== k_ui_align_left
){
540 ui_px width
= ui_text_line_width( str
) * scale
;
542 if( align
== k_ui_align_right
)
543 return rect
[0] + rect
[2]-width
;
545 return rect
[0] + (rect
[2]-width
)/2;
549 static ui_px
ui_min( ui_px a
, ui_px b
){ return a
<b
?a
:b
; }
550 static ui_px
ui_max( ui_px a
, ui_px b
){ return a
>b
?a
:b
; }
551 static ui_px
ui_clamp( ui_px a
, ui_px min
, ui_px max
)
553 return ui_min( max
, ui_max( a
, min
) );
556 static int ui_clip( ui_rect parent
, ui_rect child
, ui_rect clipped
)
558 ui_px parent_max
[2], child_max
[2];
559 parent_max
[0] = parent
[0]+parent
[2];
560 parent_max
[1] = parent
[1]+parent
[3];
561 child_max
[0] = child
[0]+child
[2];
562 child_max
[1] = child
[1]+child
[3];
564 clipped
[0] = ui_clamp( child
[0], parent
[0], parent_max
[0] );
565 clipped
[1] = ui_clamp( child
[1], parent
[1], parent_max
[1] );
566 clipped
[2] = ui_clamp( child_max
[0], parent
[0], parent_max
[0] );
567 clipped
[3] = ui_clamp( child_max
[1], parent
[1], parent_max
[1] );
569 if( clipped
[0] == clipped
[2] ||
570 clipped
[1] == clipped
[3] )
573 clipped
[2] -= clipped
[0];
574 clipped
[3] -= clipped
[1];
579 static int ui_inside_rect( ui_rect rect
, ui_px co
[2] )
581 if( co
[0] >= rect
[0] &&
583 co
[0] <= rect
[0]+rect
[2] &&
584 co
[1] <= rect
[1]+rect
[3] )
592 static int ui_click_down(void)
594 if( vg_uictx
.ignore_input_frames
) return 0;
596 if( (vg_uictx
.mouse_state
[0] & SDL_BUTTON(SDL_BUTTON_LEFT
)) &&
597 !(vg_uictx
.mouse_state
[1] & SDL_BUTTON(SDL_BUTTON_LEFT
)) )
603 static int ui_clicking(void)
605 if( vg_uictx
.ignore_input_frames
) return 0;
606 return vg_uictx
.mouse_state
[0] & SDL_BUTTON(SDL_BUTTON_LEFT
);
609 static int ui_click_up(void)
611 if( vg_uictx
.ignore_input_frames
) return 0;
612 if( (vg_uictx
.mouse_state
[1] & SDL_BUTTON(SDL_BUTTON_LEFT
)) &&
613 !(vg_uictx
.mouse_state
[0] & SDL_BUTTON(SDL_BUTTON_LEFT
)) )
619 static void ui_prerender(void)
622 vg_uictx
.mouse_state
[1] = vg_uictx
.mouse_state
[0];
623 vg_uictx
.mouse_state
[0] = SDL_GetMouseState( &x
, &y
);
624 vg_uictx
.mouse
[0] = x
;
625 vg_uictx
.mouse
[1] = y
;
627 vg_uictx
.cur_vert
= 0;
628 vg_uictx
.cur_indice
= 0;
629 vg_uictx
.vert_start
= 0;
630 vg_uictx
.indice_start
= 0;
632 if( vg_uictx
.ignore_input_frames
){
633 vg_uictx
.ignore_input_frames
--;
637 if( ui_click_down() ){
638 vg_uictx
.mouse_click
[0] = vg_uictx
.mouse
[0];
639 vg_uictx
.mouse_click
[1] = vg_uictx
.mouse
[1];
643 static void ui_text( ui_rect rect
, const char *str
, ui_px scale
,
644 enum ui_align align
)
647 u32 current_colour
= 0x00ffffff;
649 const char *_c
= str
;
652 text_cursor
[0] = ui_text_aligned_x( str
, rect
, scale
, align
);
653 text_cursor
[1] = rect
[1];
654 text_cursor
[2] = 8*scale
;
655 text_cursor
[3] = 14*scale
;
657 if( align
& (k_ui_align_middle
|k_ui_align_bottom
) ){
658 ui_px height
= ui_text_string_height( str
) * scale
;
660 if( align
& k_ui_align_bottom
)
661 text_cursor
[1] += rect
[3]-height
;
663 text_cursor
[1] += (rect
[3]-height
)/2;
666 while( (c
= *(_c
++)) ){
668 text_cursor
[1] += 14*scale
;
669 text_cursor
[0] = ui_text_aligned_x( _c
, rect
, scale
, align
);
675 glyph_base
[0] = glyph_index
& 0xf;
676 glyph_base
[1] = (glyph_index
-glyph_base
[0])>>4;
683 if( ui_clip( rect
, text_cursor
, rect_char
) ){
684 ui_fill_rect( rect_char
, current_colour
, (ui_px
[4])
693 else if( c
== '\x1B' ){
697 for( int i
=0; i
<3; i
++ ){
703 case '0': current_colour
= 0x00ffffff; break;
704 case '3'|'1'<<8: current_colour
= 0x00201fee; break;
705 case '3'|'2'<<8: current_colour
= 0x0037e420; break;
706 case '3'|'3'<<8: current_colour
= 0x000ed8e2; break;
707 case '3'|'4'<<8: current_colour
= 0x00f15010; break;
708 case '3'|'5'<<8: current_colour
= 0x00ee20ee; break;
709 case '3'|'6'<<8: current_colour
= 0x00eeee20; break;
710 case '3'|'7'<<8: current_colour
= 0x00ffffff; break;
716 colour_id
|= _c
[i
] << (i
*8);
726 else if( c
== '\t' ){
727 text_cursor
[0] += UI_GLYPH_SPACING_X
*scale
*4;
731 text_cursor
[0] += UI_GLYPH_SPACING_X
*scale
;
735 static void ui_image( ui_rect rect
, GLuint image
)
737 ui_flush( k_ui_shader_colour
);
739 glActiveTexture( GL_TEXTURE0
);
740 glBindTexture( GL_TEXTURE_2D
, image
);
741 ui_fill_rect( rect
, 0xffffffff, (ui_px
[4]){ 0,0, 255,255 } );
742 ui_flush( k_ui_shader_image
);
745 static u32
v4f_u32_colour( v4f colour
)
747 u32 r
= colour
[0] * 255.0f
,
748 g
= colour
[1] * 255.0f
,
749 b
= colour
[2] * 255.0f
,
750 a
= colour
[3] * 255.0f
;
752 return r
| (g
<<8) | (b
<<16) | (a
<<24);
755 static int ui_button( ui_rect rect
, v4f colour
)
757 int clickup
= ui_click_up(),
758 click
= ui_clicking() | clickup
,
759 target
= ui_inside_rect( rect
, vg_uictx
.mouse_click
) && click
,
760 hover
= ui_inside_rect( rect
, vg_uictx
.mouse
);
762 u32 basecolour
= v4f_u32_colour( colour
);
768 ui_fill( rect
, 0xffffffff );
769 vg_uictx
.ignore_input_frames
= 2;
770 rect_copy( rect
, vg_uictx
.click_fader
);
771 vg_uictx
.click_fade_opacity
= 1.0f
;
775 ui_fill( rect
, 0xffcccccc );
780 ui_fill( rect
, 0xff505050 );
781 ui_outline( rect
, 1, 0xffffffff );
786 ui_fill( rect
, 0xff505050 );
792 ui_fill( rect
, 0xffa0a0a0 );
796 ui_fill( rect
, 0xff505050 );
802 static int ui_button_text( ui_rect rect
, const char *string
, ui_px scale
,
805 int result
= ui_button( rect
, colour
);
806 ui_rect t
= { 0,0, ui_text_line_width( string
)*scale
, 14*scale
};
807 ui_rect_center( rect
, t
);
808 ui_text( t
, string
, scale
, k_ui_align_left
);
812 static void ui_postrender(void)
814 if( vg_uictx
.click_fade_opacity
> 0.0f
){
816 float scale
= vg_uictx
.click_fade_opacity
;
817 scale
= vg_maxf( 1.0f
/255.0f
, scale
*scale
);
819 vg_uictx
.click_fade_opacity
-= vg
.time_frame_delta
* 1.8f
;
820 u32 colour
= v4f_u32_colour( (v4f
){ 1.0f
,1.0f
,1.0f
, scale
} );
823 rect
[3] = (float)(vg_uictx
.click_fader
[3]) * scale
;
824 rect
[2] = vg_uictx
.click_fader
[2];
825 ui_rect_center( vg_uictx
.click_fader
, rect
);
827 ui_fill( rect
, colour
);
829 ui_flush( k_ui_shader_colour
);
832 #endif /* VG_IMGUI_H */