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