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