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