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