3c16711619139ef0fb31a3886bf9f06829b16d3e
[vg.git] / vg_ui.h
1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
2
3 #ifndef VG_UI_H
4 #define VG_UI_H
5
6 #define VG_GAME
7 #include "vg/vg.h"
8 #include "vg/vg_tex.h"
9 #include "vg/vg_shader.h"
10
11 static struct vg_shader _shader_ui =
12 {
13 .name = "[vg] ui",
14 .link = NULL,
15 .vs =
16 {
17 .orig_file = NULL,
18 .static_src =
19 "layout (location=0) in vec2 a_co;"
20 "layout (location=1) in vec2 a_uv;"
21 "layout (location=2) in vec4 a_colour;"
22 "layout (location=3) in vec4 a_clip;"
23 "uniform mat3 uPv;"
24 ""
25 "out vec2 aTexCoords;"
26 "out vec4 aColour;"
27 "out vec2 aWsp;"
28 "out vec4 aClip;"
29 ""
30 "void main()"
31 "{"
32 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
33 "aTexCoords = a_uv * 0.0078125;"
34 "aColour = a_colour;"
35
36 "aWsp = a_co;"
37 "aClip = a_clip;"
38 "}",
39 },
40 .fs =
41 {
42 .orig_file = NULL,
43 .static_src =
44 "uniform sampler2D uTexGlyphs;"
45 "out vec4 FragColor;"
46 ""
47 "in vec2 aTexCoords;"
48 "in vec4 aColour;"
49 ""
50 "in vec2 aWsp;"
51 "in vec4 aClip;"
52 ""
53 "void main()"
54 "{"
55 "float clip_blend = step( aWsp.x, aClip.z ) *"
56 "step( aWsp.y, aClip.w ) *"
57 "step( aClip.x, aWsp.x ) *"
58 "step( aClip.y, aWsp.y ); "
59
60 "vec4 glyph = vec4(1.0,1.0,1.0,1.0);"
61
62 "if( aColour.a == 0.0 )"
63 "{"
64 "glyph = texture( uTexGlyphs, aTexCoords );"
65 "glyph.a = smoothstep( 0.47, 0.53, glyph.r );"
66 "}"
67 "else"
68 "{"
69 "glyph.a = aColour.a;"
70 "}"
71
72 "FragColor = vec4( aColour.rgb, glyph.a*clip_blend );"
73 "}"
74 }
75 };
76
77 static struct vg_shader _shader_ui_image =
78 {
79 .name = "[vg] ui_image",
80 .link = NULL,
81 .vs =
82 {
83 .orig_file = NULL,
84 .static_src =
85 "layout (location=0) in vec2 a_co;"
86 "layout (location=1) in vec2 a_uv;"
87 "layout (location=2) in vec4 a_colour;"
88 "layout (location=3) in vec4 a_clip;"
89 "uniform mat3 uPv;"
90 ""
91 "out vec2 aTexCoords;"
92 "out vec4 aColour;"
93 "out vec2 aWsp;"
94 "out vec4 aClip;"
95 ""
96 "void main()"
97 "{"
98 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
99 "aTexCoords = a_uv * 0.0078125;"
100 "aColour = a_colour;"
101
102 "aWsp = a_co;"
103 "aClip = a_clip;"
104 "}",
105 },
106 .fs =
107 {
108 .orig_file = NULL,
109 .static_src =
110 "uniform sampler2D uTexImage;"
111 "out vec4 FragColor;"
112 ""
113 "in vec2 aTexCoords;"
114 "in vec4 aColour;"
115 ""
116 "in vec2 aWsp;"
117 "in vec4 aClip;"
118
119 "void main()"
120 "{"
121 "float clip_blend = step( aWsp.x, aClip.z ) *"
122 "step( aWsp.y, aClip.w ) *"
123 "step( aClip.x, aWsp.x ) *"
124 "step( aClip.y, aWsp.y ); "
125
126 "vec4 colour = texture( uTexImage, aTexCoords );"
127 "float value = dot(vec4(1.0),colour)*0.25;"
128
129 "vec3 col = vec3(pow(cos(value*3.14159265*2.0)*0.5+0.5,0.5))"
130 "* vec3(step(value,0.5),0.3,step(1.0-value,0.5));"
131
132 "FragColor = vec4( col*4.0, clip_blend );"
133 "}"
134 }
135 };
136
137 typedef i16 ui_px;
138 typedef u32 ui_colour;
139 typedef ui_px ui_rect[4];
140 typedef struct ui_colourset ui_colourset;
141
142 /* Relative to cursor p0 */
143 enum ui_text_align
144 {
145 k_text_align_left = 0,
146 k_text_align_right = 1,
147 k_text_align_center = 2
148 };
149
150 struct ui_colourset
151 {
152 union
153 {
154 struct
155 {
156 ui_colour main;
157 ui_colour hover;
158 ui_colour active;
159 };
160 struct
161 {
162 ui_colour background;
163 ui_colour bar;
164 ui_colour bar_hover;
165 };
166 };
167 };
168
169 #pragma pack(push,1)
170 struct ui_vert
171 {
172 ui_px co[2];
173 u8 uv[2];
174 u32 colour;
175 ui_rect clip;
176 };
177 #pragma pack(pop)
178
179 struct
180 {
181 struct ui_qnode
182 {
183 ui_rect rect;
184 ui_colour colour;
185 int mouse_over;
186 int capture_id;
187 }
188 stack[ 32 ];
189
190 u32 control_id;
191
192 struct ui_vert *vertex_buffer;
193 u16 *indice_buffer;
194 u32 max_verts, max_indices, cur_vert, cur_indice;
195
196 ui_rect clipping;
197 ui_rect cursor;
198 u32 stack_count;
199 u32 capture_mouse_id;
200 int capture_lock;
201
202 /* User input */
203 ui_px mouse[2];
204 int click_state; /* 0: released, 1: on down, 2: pressed, 3: on release */
205
206 ui_colourset *colours;
207
208 GLuint vao;
209 GLuint vbo;
210 GLuint ebo;
211
212 struct ui_image
213 {
214 ui_rect rc;
215 GLuint image;
216 }
217 images[16];
218 int image_count;
219 }
220 static vg_uictx;
221
222 #define UI_GLYPH_SPACING_X 8
223
224 static GLuint ui_glyph_texture = 0;
225 static ui_colourset ui_default_colours = {
226 .main = 0xff807373,
227 .hover = 0xff918484,
228 .active = 0xffad9f9e
229 };
230
231 VG_STATIC void _vg_ui_init(void)
232 {
233 if( !vg_shader_compile( &_shader_ui ) ||
234 !vg_shader_compile( &_shader_ui_image ) )
235 vg_fatal_error( "Failed to compile ui shader" );
236
237 /*
238 * Vertex buffer
239 * ----------------------------------------
240 */
241
242 vg_uictx.max_indices = 20000;
243 vg_uictx.max_verts = 30000;
244 vg_uictx.colours = &ui_default_colours;
245
246 /* Generate the buffer we are gonna be drawing to */
247 glGenVertexArrays( 1, &vg_uictx.vao );
248 glGenBuffers( 1, &vg_uictx.vbo );
249 glGenBuffers( 1, &vg_uictx.ebo );
250
251 glBindVertexArray( vg_uictx.vao );
252 glBindBuffer( GL_ARRAY_BUFFER, vg_uictx.vbo );
253
254 glBufferData( GL_ARRAY_BUFFER,
255 vg_uictx.max_verts * sizeof( struct ui_vert ),
256 NULL, GL_DYNAMIC_DRAW );
257 glBindVertexArray( vg_uictx.vao );
258
259 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_uictx.ebo );
260 glBufferData( GL_ELEMENT_ARRAY_BUFFER,
261 vg_uictx.max_indices * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
262
263 VG_CHECK_GL_ERR();
264
265 /* Set pointers */
266 u32 const stride = sizeof( struct ui_vert );
267
268 /* XY */
269 glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE,
270 stride, (void *)offsetof( struct ui_vert, co ) );
271 glEnableVertexAttribArray( 0 );
272
273 /* UV */
274 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE,
275 stride, (void *)offsetof( struct ui_vert, uv ) );
276 glEnableVertexAttribArray( 1 );
277
278 /* COLOUR */
279 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride,
280 (void *)offsetof( struct ui_vert, colour ) );
281 glEnableVertexAttribArray( 2 );
282
283 /* CLIPPING */
284 glVertexAttribPointer( 3, 4, GL_SHORT, GL_FALSE, stride,
285 (void *)offsetof( struct ui_vert, clip ) );
286 glEnableVertexAttribArray( 3 );
287
288 VG_CHECK_GL_ERR();
289
290 /* Alloc RAM default context */
291 u32 vert_size = vg_uictx.max_verts*sizeof(struct ui_vert),
292 inds_size = vg_align8( vg_uictx.max_indices*sizeof(u16) );
293
294 vg_uictx.vertex_buffer = vg_linear_alloc( vg_mem.rtmemory, vert_size );
295 vg_uictx.indice_buffer = vg_linear_alloc( vg_mem.rtmemory, inds_size );
296
297 /* font
298 * -----------------------------------------------------
299 */
300
301 /* Load default font */
302 u32 compressed[] = {
303 #include "vg/vg_pxfont_thin.h"
304 };
305
306 u32 pixels = 0, total = 256*256, data = 0;
307 u8 image[256*256];
308
309 while( pixels < total ){
310 for( int b = 31; b >= 0; b-- ){
311 image[ pixels ++ ] = (compressed[data] & (0x1u << b))? 0xffu: 0x00u;
312
313 if( pixels >= total ){
314 total = 0;
315 break;
316 }
317 }
318 data++;
319 }
320
321 glGenTextures( 1, &ui_glyph_texture );
322 glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
323 glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 256, 256, 0,
324 GL_RED, GL_UNSIGNED_BYTE, image );
325
326 VG_CHECK_GL_ERR();
327 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
328 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
329 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
330 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
331 }
332
333 static struct ui_vert *ui_fill_rect_uv( ui_rect rect, u32 colour, ui_px uv[4] );
334
335 VG_STATIC void ui_draw( m3x3f view_override )
336 {
337 u32 num_indices_normal = vg_uictx.cur_indice;
338
339 /* Append images to back of buffer */
340 for( int i = 0; i < vg_uictx.image_count; i ++ )
341 {
342 ui_fill_rect_uv( vg_uictx.images[i].rc, 0xffffffff,
343 (ui_px[4]){0,0,128,128} );
344 }
345
346 glBindVertexArray( vg_uictx.vao );
347
348 glBindBuffer( GL_ARRAY_BUFFER, vg_uictx.vbo );
349 glBufferSubData( GL_ARRAY_BUFFER, 0,
350 vg_uictx.cur_vert * sizeof( struct ui_vert ),
351 vg_uictx.vertex_buffer );
352
353 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_uictx.ebo );
354 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0,
355 vg_uictx.cur_indice * sizeof( u16 ),
356 vg_uictx.indice_buffer );
357
358 glEnable(GL_BLEND);
359 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
360 glBlendEquation(GL_FUNC_ADD);
361
362 glUseProgram( _shader_ui.id );
363
364 m3x3f view = M3X3_IDENTITY;
365
366 if( !view_override )
367 {
368 view_override = view;
369
370 m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
371 m3x3_scale( view, (v3f){ 1.0f/((float)vg.window_x*0.5f),
372 -1.0f/((float)vg.window_y*0.5f), 1.0f } );
373 }
374
375 /* TODO? */
376 glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1,
377 GL_FALSE, (float *)view_override );
378
379 glActiveTexture( GL_TEXTURE0 );
380 glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
381 glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 );
382
383 glDrawElements( GL_TRIANGLES, num_indices_normal,
384 GL_UNSIGNED_SHORT, (void*)(0) );
385
386
387 /* images */
388 glUseProgram( _shader_ui_image.id );
389 glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1,
390 GL_FALSE, (float *)view_override );
391
392 glActiveTexture( GL_TEXTURE1 );
393 glUniform1i( glGetUniformLocation( _shader_ui_image.id, "uTexImage" ), 1 );
394
395 /* Draw image elements */
396 for( int i = 0; i < vg_uictx.image_count; i ++ )
397 {
398 struct ui_image *img = &vg_uictx.images[i];
399
400 glBindTexture( GL_TEXTURE_2D, img->image );
401 glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
402 (void*)( (num_indices_normal + 6*i)*sizeof(u16) ) );
403 }
404
405 glDisable(GL_BLEND);
406 }
407
408 /*
409 * Rect controls
410 */
411 VG_STATIC void ui_rect_copy( ui_rect src, ui_rect dst )
412 {
413 dst[0] = src[0];
414 dst[1] = src[1];
415 dst[2] = src[2];
416 dst[3] = src[3];
417 }
418
419 VG_STATIC void ui_rect_pad( ui_rect rect, ui_px pad )
420 {
421 rect[0] += pad;
422 rect[1] += pad;
423 rect[2] -= pad*2;
424 rect[3] -= pad*2;
425 }
426
427 /*
428 * Stack control
429 */
430 static struct ui_qnode *ui_current(void)
431 {
432 return &vg_uictx.stack[ vg_uictx.stack_count-1 ];
433 }
434
435 VG_STATIC void ui_new_node(void)
436 {
437 if( vg_uictx.stack_count == vg_list_size( vg_uictx.stack ) )
438 {
439 vg_error( "[UI] Stack overflow while creating box!" );
440 return;
441 }
442
443 struct ui_qnode *parent = &vg_uictx.stack[ vg_uictx.stack_count-1 ];
444 struct ui_qnode *node = &vg_uictx.stack[ vg_uictx.stack_count++ ];
445 ui_rect_copy( vg_uictx.cursor, node->rect );
446
447 if( parent->mouse_over )
448 {
449 if( vg_uictx.mouse[0] >= node->rect[0] &&
450 vg_uictx.mouse[0] < node->rect[0]+node->rect[2] &&
451 vg_uictx.mouse[1] >= node->rect[1] &&
452 vg_uictx.mouse[1] < node->rect[1]+node->rect[3] )
453 node->mouse_over = 1;
454 else
455 node->mouse_over = 0;
456 }
457 else
458 {
459 node->mouse_over = 0;
460 }
461 }
462
463 static int ui_hasmouse(void)
464 {
465 struct ui_qnode *node = ui_current();
466 return (node->mouse_over && (node->capture_id == vg_uictx.capture_mouse_id));
467 }
468
469 VG_STATIC void ui_end(void)
470 {
471 struct ui_qnode *node = &vg_uictx.stack[ --vg_uictx.stack_count ];
472 ui_rect_copy( node->rect, vg_uictx.cursor );
473 }
474
475 VG_STATIC void ui_end_down(void)
476 {
477 ui_px height = ui_current()->rect[3];
478 ui_end();
479 vg_uictx.cursor[1] += height;
480 }
481
482 VG_STATIC void ui_end_right(void)
483 {
484 ui_px width = ui_current()->rect[2];
485 ui_end();
486 vg_uictx.cursor[0] += width;
487 }
488
489 VG_STATIC void ui_fill_y(void)
490 {
491 struct ui_qnode *node = ui_current();
492 vg_uictx.cursor[3] = node->rect[3] - (vg_uictx.cursor[1]-node->rect[1]);
493 }
494
495 VG_STATIC void ui_fill_x(void)
496 {
497 struct ui_qnode *node = ui_current();
498 vg_uictx.cursor[2] = node->rect[2] - (vg_uictx.cursor[0]-node->rect[0]);
499 }
500
501 VG_STATIC void ui_align_bottom(void)
502 {
503 struct ui_qnode *node = ui_current();
504 vg_uictx.cursor[1] = node->rect[1] + node->rect[3] - vg_uictx.cursor[3];
505 }
506
507 VG_STATIC void ui_align_right(void)
508 {
509 struct ui_qnode *node = ui_current();
510 vg_uictx.cursor[0] = node->rect[0] + node->rect[2] - vg_uictx.cursor[2];
511 }
512
513 VG_STATIC void ui_align_top(void)
514 {
515 vg_uictx.cursor[1] = ui_current()->rect[1];
516 }
517
518 VG_STATIC void ui_align_left(void)
519 {
520 vg_uictx.cursor[0] = ui_current()->rect[0];
521 }
522
523 VG_STATIC void ui_clamp_rect( ui_rect parent, ui_rect dest )
524 {
525 dest[0] = vg_min( parent[0] + parent[2] - dest[2], dest[0] );
526 dest[1] = vg_min( parent[1] + parent[3] - dest[3], dest[1] );
527 dest[0] = vg_max( parent[0], dest[0] );
528 dest[1] = vg_max( parent[1], dest[1] );
529 }
530
531 VG_STATIC void ui_capture_mouse( u32 id )
532 {
533 struct ui_qnode *node = &vg_uictx.stack[ vg_uictx.stack_count-1 ];
534 node->capture_id = id;
535
536 if( !vg_uictx.capture_lock && node->mouse_over )
537 {
538 vg_uictx.capture_mouse_id = id;
539 }
540 }
541
542 static int ui_want_mouse(void)
543 {
544 return vg_uictx.capture_mouse_id == 0? 0: 1;
545 }
546
547 VG_STATIC void ui_set_clip( ui_rect clip )
548 {
549 vg_uictx.clipping[0] = clip[0];
550 vg_uictx.clipping[1] = clip[1];
551 vg_uictx.clipping[2] = clip[0] + clip[2];
552 vg_uictx.clipping[3] = clip[1] + clip[3];
553 }
554
555 VG_STATIC void ui_release_clip(void)
556 {
557 vg_uictx.clipping[0] = -32000;
558 vg_uictx.clipping[1] = -32000;
559 vg_uictx.clipping[2] = 32000;
560 vg_uictx.clipping[3] = 32000;
561 }
562
563 static struct ui_vert *ui_fill_rect_uv( ui_rect rect, u32 colour, ui_px uv[4] )
564 {
565 /* this if far from ideal but stops us from crashing */
566 if( (vg_uictx.cur_vert + 6 > vg_uictx.max_verts) ||
567 (vg_uictx.cur_indice + 4 > vg_uictx.max_indices))
568 return vg_uictx.vertex_buffer;
569
570 struct ui_vert *vertices = &vg_uictx.vertex_buffer[ vg_uictx.cur_vert ];
571 u16 *indices = &vg_uictx.indice_buffer[ vg_uictx.cur_indice ];
572
573 vertices[0].co[0] = rect[0];
574 vertices[0].co[1] = rect[1];
575 vertices[0].uv[0] = uv[0];
576 vertices[0].uv[1] = uv[1];
577 vertices[0].colour = colour;
578 vertices[1].co[0] = rect[0]+rect[2];
579 vertices[1].co[1] = rect[1];
580 vertices[1].uv[0] = uv[2];
581 vertices[1].uv[1] = uv[1];
582 vertices[1].colour = colour;
583 vertices[2].co[0] = rect[0]+rect[2];
584 vertices[2].co[1] = rect[1]+rect[3];
585 vertices[2].uv[0] = uv[2];
586 vertices[2].uv[1] = uv[3];
587 vertices[2].colour = colour;
588 vertices[3].co[0] = rect[0];
589 vertices[3].co[1] = rect[1]+rect[3];
590 vertices[3].uv[0] = uv[0];
591 vertices[3].uv[1] = uv[3];
592 vertices[3].colour = colour;
593 u16 ind_start = vg_uictx.cur_vert;
594
595 ui_rect_copy( vg_uictx.clipping, vertices[0].clip );
596 ui_rect_copy( vg_uictx.clipping, vertices[1].clip );
597 ui_rect_copy( vg_uictx.clipping, vertices[2].clip );
598 ui_rect_copy( vg_uictx.clipping, vertices[3].clip );
599
600 indices[0] = ind_start+0;
601 indices[1] = ind_start+2;
602 indices[2] = ind_start+1;
603
604 indices[3] = ind_start+0;
605 indices[4] = ind_start+3;
606 indices[5] = ind_start+2;
607
608 vg_uictx.cur_indice += 6;
609 vg_uictx.cur_vert += 4;
610
611 return vertices;
612 }
613
614 static struct ui_vert *ui_fill_rect( ui_rect rect, u32 colour )
615 {
616 return ui_fill_rect_uv( rect, colour, (ui_px[4]){ 4,4, 4,4 } );
617 }
618
619 static ui_px ui_text_line_offset( const char *str, ui_px scale,
620 enum ui_text_align align )
621 {
622 if( align == k_text_align_left )
623 return 0;
624
625 int length = 0;
626 const char *_c = str;
627 char c;
628
629 while( (c = *(_c ++)) )
630 if( c >= 32 && c <= 126 )
631 length ++;
632 else if( c == '\n' )
633 break;
634
635 if( align == k_text_align_right )
636 return -length * scale*8;
637 else
638 return (-length * scale*8) / 2;
639 }
640
641 VG_STATIC void ui_text( ui_px pos[2],
642 const char *str, ui_px scale, enum ui_text_align align )
643 {
644 ui_rect text_cursor;
645 u32 current_colour = 0x00ffffff;
646
647 const char *_c = str;
648 u8 c;
649
650 text_cursor[0] = pos[0] + ui_text_line_offset( str, scale, align );
651 text_cursor[1] = pos[1];
652 text_cursor[2] = 8*scale;
653 text_cursor[3] = 14*scale;
654
655 while( (c = *(_c ++)) ){
656 if( c == '\n' ){
657 text_cursor[1] += 14*scale;
658 text_cursor[0] = pos[0] + ui_text_line_offset( _c, scale, align );
659 continue;
660 }
661 else if( c >= 33 ){
662 u8 glyph_base[2];
663 u8 glyph_index = c;
664 glyph_base[0] = glyph_index & 0xf;
665 glyph_base[1] = (glyph_index-glyph_base[0])>>4;
666
667 glyph_base[0] *= 8;
668 glyph_base[1] *= 8;
669
670 ui_fill_rect_uv( text_cursor, current_colour, (ui_px[4])
671 {
672 glyph_base[0]+2,
673 glyph_base[1]+1,
674 glyph_base[0]+6,
675 glyph_base[1]+8
676 });
677 }
678 else if( c == '\x1B' ){
679 _c ++;
680 u16 colour_id = 0;
681 for( int i = 0; i < 3; i ++ ){
682 if( _c[i] ){
683 if( _c[i] == 'm' ){
684 _c = _c + i + 1;
685
686 switch( colour_id ){
687 case '0': current_colour = 0x00ffffff; break;
688 case '3'|'1'<<8: current_colour = 0x00201fee; break;
689 case '3'|'2'<<8: current_colour = 0x0037e420; break;
690 case '3'|'3'<<8: current_colour = 0x000ed8e2; break;
691 case '3'|'4'<<8: current_colour = 0x00f15010; break;
692 case '3'|'5'<<8: current_colour = 0x00ee20ee; break;
693 case '3'|'6'<<8: current_colour = 0x00eeee20; break;
694 case '3'|'7'<<8: current_colour = 0x00ffffff; break;
695 }
696
697 break;
698 }
699
700 colour_id |= _c[i] << (i*8);
701 }
702 else{
703 _c = _c +i;
704 break;
705 }
706 }
707
708 continue;
709 }
710 else if( c == '\t' ){
711 text_cursor[0] += UI_GLYPH_SPACING_X*scale*4;
712 continue;
713 }
714
715 text_cursor[0] += UI_GLYPH_SPACING_X*scale;
716 }
717 }
718
719 /*
720 * API Control
721 */
722 VG_STATIC void ui_begin( ui_px res_x, ui_px res_y )
723 {
724 vg_uictx.cursor[0] = 0;
725 vg_uictx.cursor[1] = 0;
726 vg_uictx.cursor[2] = res_x;
727 vg_uictx.cursor[3] = res_y;
728
729 ui_rect_copy( vg_uictx.cursor, vg_uictx.stack[0].rect );
730 vg_uictx.stack[0].mouse_over = 1;
731
732 vg_uictx.stack_count = 1;
733
734 vg_uictx.cur_vert = 0;
735 vg_uictx.cur_indice = 0;
736
737 ui_release_clip();
738
739 if( vg_uictx.click_state == 0 )
740 vg_uictx.capture_mouse_id = 0;
741
742 vg_uictx.image_count = 0;
743 }
744
745 VG_STATIC void ui_resolve(void)
746 {
747 if( vg_uictx.stack_count-1 )
748 {
749 vg_error( "[UI] Mismatched node create/drestroy!" );
750 return;
751 }
752
753 if( vg_uictx.click_state == 3 || vg_uictx.click_state == 0 )
754 {
755 vg_uictx.capture_lock = 0;
756 }
757 }
758
759 VG_STATIC void ui_set_mouse( int x, int y, int click_state )
760 {
761 vg_uictx.mouse[0] = x;
762 vg_uictx.mouse[1] = y;
763
764 vg_uictx.click_state = click_state;
765 }
766
767 /*
768 * High level controls
769 */
770 struct ui_window
771 {
772 const char *title;
773 ui_rect transform;
774
775 int drag;
776 ui_px drag_offset[2];
777 };
778
779 enum button_state
780 {
781 k_button_released = 0,
782 k_button_start_click,
783 k_button_click,
784 k_button_hold
785 };
786
787 static int ui_button(void)
788 {
789 u32 uid = vg_uictx.control_id ++;
790
791 ui_new_node();
792 {
793 ui_capture_mouse( uid );
794
795 if( ui_hasmouse() )
796 {
797 ui_fill_rect( vg_uictx.cursor, vg_uictx.colours->hover );
798
799 if( vg_uictx.click_state == 1 )
800 {
801 vg_uictx.capture_lock = 1;
802 return k_button_start_click;
803 }
804 else if( vg_uictx.capture_lock && vg_uictx.click_state == 3 )
805 return k_button_click;
806 else if( vg_uictx.capture_lock && vg_uictx.click_state == 2 )
807 return k_button_hold;
808
809 return k_button_click;
810 }
811 else
812 ui_fill_rect( vg_uictx.cursor, vg_uictx.colours->main );
813 }
814
815 return k_button_released;
816 }
817
818 static int ui_window( struct ui_window *window, u32 control_group )
819 {
820 if( window->drag )
821 {
822 window->transform[0] = vg_uictx.mouse[0]+window->drag_offset[0];
823 window->transform[1] = vg_uictx.mouse[1]+window->drag_offset[1];
824
825 ui_clamp_rect( vg_uictx.stack[0].rect, window->transform );
826
827 if( vg_uictx.click_state == 0 || vg_uictx.click_state == 3 )
828 {
829 window->drag = 0;
830 }
831 }
832
833 ui_rect_copy( window->transform, vg_uictx.cursor );
834
835 ui_new_node();
836 {
837 ui_capture_mouse( vg_uictx.control_id ++ );
838
839 /* Drag bar */
840 vg_uictx.cursor[3] = 25;
841 ui_new_node();
842 {
843 ui_capture_mouse( vg_uictx.control_id ++ );
844
845 struct ui_vert *drag_bar = ui_fill_rect( vg_uictx.cursor, 0xff555555 );
846
847 /* title.. */
848 vg_uictx.cursor[0] += 2;
849 vg_uictx.cursor[1] += 2;
850 ui_text( vg_uictx.cursor, window->title, 2, 0 );
851
852 /* Close button */
853 vg_uictx.cursor[3] = 25;
854 vg_uictx.cursor[2] = 25;
855 ui_align_right();
856 ui_align_top();
857 ui_rect_pad( vg_uictx.cursor, 4 );
858
859 if( ui_button() )
860 {
861 vg_info( "Click clacked\n" );
862 }
863 vg_uictx.cursor[0] += 2;
864 ui_text( vg_uictx.cursor, "x", 2, 0 );
865 ui_end();
866
867 if( ui_hasmouse() )
868 {
869 drag_bar[0].colour = 0xff777777;
870 drag_bar[1].colour = 0xff777777;
871 drag_bar[2].colour = 0xff777777;
872 drag_bar[3].colour = 0xff777777;
873
874 /* start drag */
875 if( vg_uictx.click_state == 1 )
876 {
877 window->drag = 1;
878 window->drag_offset[0] = window->transform[0]-vg_uictx.mouse[0];
879 window->drag_offset[1] = window->transform[1]-vg_uictx.mouse[1];
880 }
881 }
882 }
883 ui_end_down();
884 }
885
886 return 1;
887 }
888
889 VG_STATIC void ui_push_image( ui_rect rc, GLuint image )
890 {
891 struct ui_image *img = &vg_uictx.images[ vg_uictx.image_count ++ ];
892 ui_rect_copy( rc, img->rc );
893 img->image = image;
894 }
895
896 struct ui_slider
897 {
898 float *data;
899 float min, max;
900 };
901
902 struct ui_slider_vector
903 {
904 float *data, min, max;
905 struct ui_slider sub[4];
906 u32 len;
907 };
908
909 struct ui_checkbox
910 {
911 int *data;
912 };
913
914 VG_STATIC void ui_slider( struct ui_slider *slider )
915 {
916 ui_new_node();
917
918 ui_px slider_start = vg_uictx.cursor[0];
919
920 float const ftotal = vg_uictx.cursor[2],
921 fwidth = ftotal*0.25f,
922 fmove = ftotal - fwidth,
923 fstart = fwidth*0.5f,
924 frange = slider->max-slider->min,
925 fpos = (*slider->data - slider->min) / frange;
926
927 ui_fill_rect( vg_uictx.cursor, 0xff111111 );
928 vg_uictx.cursor[2] = fwidth;
929 vg_uictx.cursor[0] = slider_start + fpos * fmove;
930
931 u32 uid = vg_uictx.control_id ++;
932 int status = ui_button();
933 if( vg_uictx.capture_lock && (vg_uictx.capture_mouse_id == uid))
934 {
935 float ui_new = vg_uictx.mouse[0],
936 local = ui_new - (slider_start + fstart),
937 zo = vg_clampf(local / fmove,0.0f,1.0f);
938
939 *slider->data = vg_lerpf( slider->min, slider->max, zo );
940 }
941
942 vg_uictx.cursor[0] += 4;
943 vg_uictx.cursor[1] += 4;
944
945 char buf[12];
946 snprintf( buf, 12, "%.2f", *slider->data );
947 ui_text( vg_uictx.cursor, buf, 1, 0 );
948 ui_end_down();
949 ui_end_down();
950 }
951
952 VG_STATIC void ui_slider_vector( struct ui_slider_vector *slider )
953 {
954 for( int i=0; i<slider->len; i++ )
955 {
956 slider->sub[i].data = &slider->data[i];
957 slider->sub[i].min = slider->min;
958 slider->sub[i].max = slider->max;
959 ui_slider( &slider->sub[i] );
960 }
961 }
962
963 VG_STATIC void ui_checkbox( struct ui_checkbox *cb )
964 {
965 if( ui_button() == k_button_click )
966 *cb->data ^= 0x1;
967
968 ui_new_node();
969 ui_rect_pad( vg_uictx.cursor, 4 );
970 if( *cb->data )
971 ui_fill_rect( vg_uictx.cursor, 0xff00e052 );
972 else
973 ui_fill_rect( vg_uictx.cursor, 0xff0052e0 );
974
975 ui_end();
976 ui_end_down();
977 }
978
979 #endif /* VG_UI_H */