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