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