bad char
[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 static struct vg_shader _shader_ui =
7 {
8 .name = "[vg] ui",
9 .link = NULL,
10 .vs =
11 {
12 .orig_file = NULL,
13 .static_src =
14 "layout (location=0) in vec2 a_co;"
15 "layout (location=1) in vec2 a_uv;"
16 "layout (location=2) in vec4 a_colour;"
17 "layout (location=3) in vec4 a_clip;"
18 "uniform mat3 uPv;"
19 ""
20 "out vec2 aTexCoords;"
21 "out vec4 aColour;"
22 "out vec2 aWsp;"
23 "out vec4 aClip;"
24 ""
25 "void main()"
26 "{"
27 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
28 "aTexCoords = a_uv * 0.0078125;"
29 "aColour = a_colour;"
30
31 "aWsp = a_co;"
32 "aClip = a_clip;"
33 "}",
34 },
35 .fs =
36 {
37 .orig_file = NULL,
38 .static_src =
39 "uniform sampler2D uTexGlyphs;"
40 "out vec4 FragColor;"
41 ""
42 "in vec2 aTexCoords;"
43 "in vec4 aColour;"
44 ""
45 "in vec2 aWsp;"
46 "in vec4 aClip;"
47 ""
48 "void main()"
49 "{"
50 "float clip_blend = step( aWsp.x, aClip.z ) *"
51 "step( aWsp.y, aClip.w ) *"
52 "step( aClip.x, aWsp.x ) *"
53 "step( aClip.y, aWsp.y ); "
54
55 "vec4 glyph = vec4(1.0,1.0,1.0,1.0);"
56
57 "if( aColour.a == 0.0 )"
58 "{"
59 "glyph = texture( uTexGlyphs, aTexCoords );"
60 "glyph.a = smoothstep( 0.47, 0.53, glyph.r );"
61 "}"
62 "else"
63 "{"
64 "glyph.a = aColour.a;"
65 "}"
66
67 "FragColor = vec4( aColour.rgb, glyph.a*clip_blend );"
68 "}"
69 }
70 };
71
72 typedef i16 ui_px;
73 typedef u32 ui_colour;
74 typedef ui_px ui_rect[4];
75 typedef struct ui_ctx ui_ctx;
76 typedef struct ui_colourset ui_colourset;
77
78 /* Relative to cursor p0 */
79 enum ui_text_align
80 {
81 k_text_align_left = 0,
82 k_text_align_right = 1,
83 k_text_align_center = 2
84 };
85
86 struct ui_colourset
87 {
88 union
89 {
90 struct
91 {
92 ui_colour main;
93 ui_colour hover;
94 ui_colour active;
95 };
96 struct
97 {
98 ui_colour background;
99 ui_colour bar;
100 ui_colour bar_hover;
101 };
102 };
103 };
104
105 struct ui_ctx
106 {
107 struct ui_qnode
108 {
109 ui_rect rect;
110 ui_colour colour;
111 int mouse_over;
112 int capture_id;
113 }
114 stack[ 32 ];
115
116 #pragma pack(push,1)
117 struct ui_vert
118 {
119 ui_px co[2];
120 u8 uv[2];
121 u32 colour;
122 ui_rect clip;
123 }
124 *verts;
125 #pragma pack(pop)
126
127 u32 control_id;
128
129 u32 num_verts;
130 u16 *indices;
131 u32 num_indices;
132
133 ui_rect clipping;
134 ui_rect cursor;
135 u32 stack_count;
136 u32 capture_mouse_id;
137 int capture_lock;
138
139
140 /* User input */
141 ui_px mouse[2];
142 int click_state; /* 0: released, 1: on down, 2: pressed, 3: on release */
143
144 ui_colourset *colours;
145
146 GLuint vao;
147 GLuint vbo;
148 GLuint ebo;
149
150 struct ui_image
151 {
152 ui_rect rc;
153 GLuint image;
154 }
155 images[16];
156 int image_count;
157 };
158
159 #define UI_GLYPH_SPACING_X 9
160
161 static GLuint ui_glyph_texture = 0;
162 static ui_colourset ui_default_colours = {
163 .main = 0xff807373,
164 .hover = 0xff918484,
165 .active = 0xffad9f9e
166 };
167
168 static ui_ctx ui_global_ctx;
169
170 static void ui_init_context( ui_ctx *ctx, int index_buffer_size )
171 {
172 u32 vertex_buffer_size = (index_buffer_size+(index_buffer_size/2));
173
174 /* Generate the buffer we are gonna be drawing to */
175 {
176 glGenVertexArrays(1, &ctx->vao);
177 glGenBuffers( 1, &ctx->vbo );
178 glGenBuffers( 1, &ctx->ebo );
179 glBindVertexArray( ctx->vao );
180
181 glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo );
182
183 glBufferData( GL_ARRAY_BUFFER,
184 vertex_buffer_size * sizeof( struct ui_vert ),
185 NULL, GL_DYNAMIC_DRAW );
186 glBindVertexArray( ctx->vao );
187
188 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo );
189 glBufferData( GL_ELEMENT_ARRAY_BUFFER,
190 index_buffer_size * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
191
192 u32 const stride = sizeof( struct ui_vert );
193
194 /* XY */
195 glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE,
196 stride, (void *)offsetof( struct ui_vert, co ) );
197 glEnableVertexAttribArray( 0 );
198
199 /* UV */
200 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE,
201 stride, (void *)offsetof( struct ui_vert, uv ) );
202 glEnableVertexAttribArray( 1 );
203
204 /* COLOUR */
205 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride,
206 (void *)offsetof( struct ui_vert, colour ) );
207 glEnableVertexAttribArray( 2 );
208
209 /* CLIPPING */
210 glVertexAttribPointer( 3, 4, GL_SHORT, GL_FALSE, stride,
211 (void *)offsetof( struct ui_vert, clip ) );
212 glEnableVertexAttribArray( 3 );
213 }
214
215 /* Initialize default context */
216 {
217 ctx->verts = (struct ui_vert *)malloc(
218 vertex_buffer_size * sizeof(struct ui_vert) );
219 ctx->indices = (u16*)malloc( index_buffer_size * sizeof(u16) );
220
221 if( !ctx->colours )
222 ctx->colours = &ui_default_colours;
223 }
224 }
225
226 static void ui_context_free( ui_ctx *ctx )
227 {
228 glDeleteVertexArrays( 1, &ctx->vao );
229 glDeleteBuffers( 1, &ctx->vbo );
230 glDeleteBuffers( 1, &ctx->ebo );
231
232 free( ctx->verts );
233 free( ctx->indices );
234 }
235
236 static void ui_default_init(void)
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 = malloc( total );
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
264 glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 256, 256, 0,
265 GL_RED, GL_UNSIGNED_BYTE, image );
266
267 vg_tex2d_clamp();
268 vg_tex2d_nearest();
269
270 free( image );
271
272 vg_shader_register( &_shader_ui );
273 ui_init_context( &ui_global_ctx, 20000 );
274 }
275
276 static void ui_default_free(void)
277 {
278 glDeleteTextures( 1, &ui_glyph_texture );
279 ui_context_free( &ui_global_ctx );
280 }
281
282 static struct ui_vert *ui_fill_rect_uv( ui_ctx *ctx, ui_rect rect,
283 u32 colour, ui_px uv[4] );
284
285 static void ui_draw( ui_ctx *ctx, m3x3f view_override )
286 {
287 u32 num_indices_normal = ctx->num_indices;
288
289 /* Append images to back of buffer */
290 for( int i = 0; i < ctx->image_count; i ++ )
291 {
292 ui_fill_rect_uv( ctx, ctx->images[i].rc,
293 0xffffffff, (ui_px[4]){0,0,128,128} );
294 }
295
296 glBindVertexArray( ctx->vao );
297
298 glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo );
299 glBufferSubData( GL_ARRAY_BUFFER, 0,
300 ctx->num_verts * sizeof( struct ui_vert ), ctx->verts );
301
302 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo );
303 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0,
304 ctx->num_indices * sizeof( u16 ), ctx->indices );
305
306 glEnable(GL_BLEND);
307 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
308 glBlendEquation(GL_FUNC_ADD);
309
310 glUseProgram( _shader_ui.id );
311
312 m3x3f view = M3X3_IDENTITY;
313
314 if( !view_override )
315 {
316 view_override = view;
317
318 m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
319 m3x3_scale( view, (v3f){ 1.0f/((float)vg_window_x*0.5f),
320 -1.0f/((float)vg_window_y*0.5f), 1.0f } );
321 }
322
323 /* TODO? */
324 glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1,
325 GL_FALSE, (float *)view_override );
326
327 glActiveTexture( GL_TEXTURE0 );
328 glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
329 glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 );
330
331 glDrawElements( GL_TRIANGLES, num_indices_normal,
332 GL_UNSIGNED_SHORT, (void*)(0) );
333
334 /* Draw image elements */
335 for( int i = 0; i < ctx->image_count; i ++ )
336 {
337 struct ui_image *img = &ctx->images[i];
338
339 glBindTexture( GL_TEXTURE_2D, img->image );
340 glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
341 (void*)( (num_indices_normal + 6*i)*sizeof(u16) ) );
342 }
343
344 glDisable(GL_BLEND);
345 }
346
347 /*
348 * Rect controls
349 */
350 static void ui_rect_copy( ui_rect src, ui_rect dst )
351 {
352 dst[0] = src[0];
353 dst[1] = src[1];
354 dst[2] = src[2];
355 dst[3] = src[3];
356 }
357
358 static void ui_rect_pad( ui_rect rect, ui_px pad )
359 {
360 rect[0] += pad;
361 rect[1] += pad;
362 rect[2] -= pad*2;
363 rect[3] -= pad*2;
364 }
365
366 /*
367 * Stack control
368 */
369 static struct ui_qnode *ui_current( ui_ctx *ctx )
370 {
371 return &ctx->stack[ ctx->stack_count-1 ];
372 }
373
374 static void ui_new_node( ui_ctx *ctx )
375 {
376 if( ctx->stack_count == vg_list_size( ctx->stack ) )
377 vg_exiterr( "[UI] Stack overflow while creating box!" );
378
379 struct ui_qnode *parent = &ctx->stack[ ctx->stack_count-1 ];
380 struct ui_qnode *node = &ctx->stack[ ctx->stack_count++ ];
381 ui_rect_copy( ctx->cursor, node->rect );
382
383 if( parent->mouse_over )
384 {
385 if( ctx->mouse[0] >= node->rect[0] &&
386 ctx->mouse[0] < node->rect[0]+node->rect[2] &&
387 ctx->mouse[1] >= node->rect[1] &&
388 ctx->mouse[1] < node->rect[1]+node->rect[3] )
389 node->mouse_over = 1;
390 else
391 node->mouse_over = 0;
392 }
393 else
394 {
395 node->mouse_over = 0;
396 }
397 }
398
399 static int ui_hasmouse( ui_ctx *ctx )
400 {
401 struct ui_qnode *node = ui_current( ctx );
402 return (node->mouse_over && (node->capture_id == ctx->capture_mouse_id));
403 }
404
405 static void ui_end( ui_ctx *ctx )
406 {
407 struct ui_qnode *node = &ctx->stack[ --ctx->stack_count ];
408 ui_rect_copy( node->rect, ctx->cursor );
409 }
410
411 static void ui_end_down( ui_ctx *ctx )
412 {
413 ui_px height = ui_current( ctx )->rect[3];
414 ui_end( ctx );
415 ctx->cursor[1] += height;
416 }
417
418 static void ui_end_right( ui_ctx *ctx )
419 {
420 ui_px width = ui_current( ctx )->rect[2];
421 ui_end( ctx );
422 ctx->cursor[0] += width;
423 }
424
425 static void ui_fill_y( ui_ctx *ctx )
426 {
427 struct ui_qnode *node = ui_current( ctx );
428 ctx->cursor[3] = node->rect[3] - (ctx->cursor[1]-node->rect[1]);
429 }
430
431 static void ui_fill_x( ui_ctx *ctx )
432 {
433 struct ui_qnode *node = ui_current( ctx );
434 ctx->cursor[2] = node->rect[2] - (ctx->cursor[0]-node->rect[0]);
435 }
436
437 static void ui_align_bottom( ui_ctx *ctx )
438 {
439 struct ui_qnode *node = ui_current( ctx );
440 ctx->cursor[1] = node->rect[1] + node->rect[3] - ctx->cursor[3];
441 }
442
443 static void ui_align_right( ui_ctx *ctx )
444 {
445 struct ui_qnode *node = ui_current( ctx );
446 ctx->cursor[0] = node->rect[0] + node->rect[2] - ctx->cursor[2];
447 }
448
449 static void ui_align_top( ui_ctx *ctx )
450 {
451 ctx->cursor[1] = ui_current( ctx )->rect[1];
452 }
453
454 static void ui_align_left( ui_ctx *ctx )
455 {
456 ctx->cursor[0] = ui_current( ctx )->rect[0];
457 }
458
459 static void ui_clamp_rect( ui_rect parent, ui_rect dest )
460 {
461 dest[0] = vg_min( parent[0] + parent[2] - dest[2], dest[0] );
462 dest[1] = vg_min( parent[1] + parent[3] - dest[3], dest[1] );
463 dest[0] = vg_max( parent[0], dest[0] );
464 dest[1] = vg_max( parent[1], dest[1] );
465 }
466
467 static void ui_capture_mouse( ui_ctx *ctx, u32 id )
468 {
469 struct ui_qnode *node = &ctx->stack[ ctx->stack_count-1 ];
470 node->capture_id = id;
471
472 if( !ctx->capture_lock && node->mouse_over )
473 {
474 ctx->capture_mouse_id = id;
475 }
476 }
477
478 static int ui_want_mouse( ui_ctx *ctx )
479 {
480 return ctx->capture_mouse_id == 0? 0: 1;
481 }
482
483 static void ui_set_clip( ui_ctx *ctx, ui_rect clip )
484 {
485 ctx->clipping[0] = clip[0];
486 ctx->clipping[1] = clip[1];
487 ctx->clipping[2] = clip[0] + clip[2];
488 ctx->clipping[3] = clip[1] + clip[3];
489 }
490
491 static void ui_release_clip( ui_ctx *ctx )
492 {
493 ctx->clipping[0] = -32000;
494 ctx->clipping[1] = -32000;
495 ctx->clipping[2] = 32000;
496 ctx->clipping[3] = 32000;
497 }
498
499 static struct ui_vert *ui_fill_rect_uv( ui_ctx *ctx, ui_rect rect,
500 u32 colour, ui_px uv[4] )
501 {
502 struct ui_vert *vertices = &ctx->verts[ ctx->num_verts ];
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 = ctx->num_verts;
524 u16 *indices = &ctx->indices[ ctx->num_indices ];
525
526 ui_rect_copy( ctx->clipping, vertices[0].clip );
527 ui_rect_copy( ctx->clipping, vertices[1].clip );
528 ui_rect_copy( ctx->clipping, vertices[2].clip );
529 ui_rect_copy( ctx->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 ctx->num_indices += 6;
540 ctx->num_verts += 4;
541
542 return vertices;
543 }
544
545 static struct ui_vert *ui_fill_rect( ui_ctx *ctx, ui_rect rect, u32 colour )
546 {
547 return ui_fill_rect_uv( ctx, 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 static void ui_text( ui_ctx *ctx, 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( ctx, 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 static void ui_begin( ui_ctx *ctx, ui_px res_x, ui_px res_y )
664 {
665 ctx->cursor[0] = 0;
666 ctx->cursor[1] = 0;
667 ctx->cursor[2] = res_x;
668 ctx->cursor[3] = res_y;
669
670 ui_rect_copy( ctx->cursor, ctx->stack[0].rect );
671 ctx->stack[0].mouse_over = 1;
672
673 ctx->stack_count = 1;
674
675 ctx->num_verts = 0;
676 ctx->num_indices = 0;
677
678 ui_release_clip( ctx );
679
680 if( ctx->click_state == 0 )
681 ctx->capture_mouse_id = 0;
682
683 ctx->image_count = 0;
684 }
685
686 static void ui_resolve( ui_ctx *ctx )
687 {
688 if( ctx->stack_count-1 )
689 vg_exiterr( "[UI] Mismatched node create/drestroy!" );
690
691 if( ctx->click_state == 3 || ctx->click_state == 0 )
692 {
693 ctx->capture_lock = 0;
694 }
695 }
696
697 static void ui_set_mouse( ui_ctx *ctx, int x, int y, int click_state )
698 {
699 ctx->mouse[0] = x;
700 ctx->mouse[1] = y;
701
702 ctx->click_state = click_state;
703 }
704
705 /*
706 * High level controls
707 */
708 struct ui_window
709 {
710 const char *title;
711 ui_rect transform;
712
713 int drag;
714 ui_px drag_offset[2];
715 };
716
717 enum button_state
718 {
719 k_button_released = 0,
720 k_button_start_click,
721 k_button_click,
722 k_button_hold
723 };
724
725 static int ui_button( ui_ctx *ctx )
726 {
727 u32 uid = ctx->control_id ++;
728
729 ui_new_node( ctx );
730 {
731 ui_capture_mouse( ctx, uid );
732
733 if( ui_hasmouse(ctx) )
734 {
735 ui_fill_rect( ctx, ctx->cursor, ctx->colours->hover );
736
737 if( ctx->click_state == 1 )
738 {
739 ctx->capture_lock = 1;
740 return k_button_start_click;
741 }
742 else if( ctx->capture_lock && ctx->click_state == 3 )
743 return k_button_click;
744 else if( ctx->capture_lock && ctx->click_state == 2 )
745 return k_button_hold;
746 }
747 else
748 ui_fill_rect( ctx, ctx->cursor, ctx->colours->main );
749 }
750
751 return k_button_released;
752 }
753
754 static int ui_window( ui_ctx *ctx, struct ui_window *window, u32 control_group )
755 {
756 if( window->drag )
757 {
758 window->transform[0] = ctx->mouse[0]+window->drag_offset[0];
759 window->transform[1] = ctx->mouse[1]+window->drag_offset[1];
760
761 ui_clamp_rect( ctx->stack[0].rect, window->transform );
762
763 if( ctx->click_state == 0 || ctx->click_state == 3 )
764 {
765 window->drag = 0;
766 }
767 }
768
769 ui_rect_copy( window->transform, ctx->cursor );
770
771 ui_new_node( ctx );
772 {
773 ui_capture_mouse( ctx, ctx->control_id ++ );
774
775 /* Drag bar */
776 ctx->cursor[3] = 25;
777 ui_new_node( ctx );
778 {
779 ui_capture_mouse( ctx, ctx->control_id ++ );
780
781 struct ui_vert *drag_bar = ui_fill_rect( ctx, ctx->cursor, 0xff555555 );
782
783 /* title.. */
784 ctx->cursor[0] += 2;
785 ctx->cursor[1] += 2;
786 ui_text( ctx, ctx->cursor, window->title, 2, 0 );
787
788 /* Close button */
789 ctx->cursor[3] = 25;
790 ctx->cursor[2] = 25;
791 ui_align_right( ctx );
792 ui_align_top( ctx );
793 ui_rect_pad( ctx->cursor, 4 );
794
795 if( ui_button( ctx ) )
796 {
797 vg_info( "Click clacked\n" );
798 }
799 ctx->cursor[0] += 2;
800 ui_text( ctx, ctx->cursor, "x", 2, 0 );
801 ui_end( ctx );
802
803 if( ui_hasmouse( ctx ) )
804 {
805 drag_bar[0].colour = 0xff777777;
806 drag_bar[1].colour = 0xff777777;
807 drag_bar[2].colour = 0xff777777;
808 drag_bar[3].colour = 0xff777777;
809
810 /* start drag */
811 if( ctx->click_state == 1 )
812 {
813 window->drag = 1;
814 window->drag_offset[0] = window->transform[0]-ctx->mouse[0];
815 window->drag_offset[1] = window->transform[1]-ctx->mouse[1];
816 }
817 }
818 }
819 ui_end_down( ctx );
820 }
821
822 return 1;
823 }
824
825 static void ui_push_image( ui_ctx *ctx, ui_rect rc, GLuint image )
826 {
827 struct ui_image *img = &ctx->images[ ctx->image_count ++ ];
828 ui_rect_copy( rc, img->rc );
829 img->image = image;
830 }
831
832 struct ui_slider
833 {
834 float *data;
835 float min, max;
836 };
837
838 struct ui_slider_vector
839 {
840 float *data, min, max;
841 struct ui_slider sub[4];
842 u32 len;
843 };
844
845 struct ui_checkbox
846 {
847 int *data;
848 };
849
850 static void ui_slider( ui_ctx *ctx, struct ui_slider *slider )
851 {
852 ui_new_node( ctx );
853
854 ui_px slider_start = ctx->cursor[0];
855
856 float const ftotal = ctx->cursor[2],
857 fwidth = ftotal*0.25f,
858 fmove = ftotal - fwidth,
859 fstart = fwidth*0.5f,
860 frange = slider->max-slider->min,
861 fpos = (*slider->data - slider->min) / frange;
862
863 ui_fill_rect( ctx, ctx->cursor, 0xff111111 );
864 ctx->cursor[2] = fwidth;
865 ctx->cursor[0] = slider_start + fpos * fmove;
866
867 u32 uid = ctx->control_id ++;
868 int status = ui_button( ctx );
869 if( ctx->capture_lock && (ctx->capture_mouse_id == uid))
870 {
871 float ui_new = ctx->mouse[0],
872 local = ui_new - (slider_start + fstart),
873 zo = vg_clampf(local / fmove,0.0f,1.0f);
874
875 *slider->data = vg_lerpf( slider->min, slider->max, zo );
876 }
877
878 ctx->cursor[0] += 4;
879 ctx->cursor[1] += 4;
880
881 char buf[12];
882 snprintf( buf, 12, "%.2f", *slider->data );
883 ui_text( ctx, ctx->cursor, buf, 1, 0 );
884 ui_end_down( ctx );
885 ui_end_down( ctx );
886 }
887
888 static void ui_slider_vector( ui_ctx *ctx, struct ui_slider_vector *slider )
889 {
890 for( int i=0; i<slider->len; i++ )
891 {
892 slider->sub[i].data = &slider->data[i];
893 slider->sub[i].min = slider->min;
894 slider->sub[i].max = slider->max;
895 ui_slider( ctx, &slider->sub[i] );
896 }
897 }
898
899 static void ui_checkbox( ui_ctx *ctx, struct ui_checkbox *cb )
900 {
901 if( ui_button(ctx) == k_button_click )
902 *cb->data ^= 0x1;
903
904 ui_new_node(ctx);
905 ui_rect_pad( ctx->cursor, 4 );
906 if( *cb->data )
907 ui_fill_rect( ctx, ctx->cursor, 0xff00e052 );
908 else
909 ui_fill_rect( ctx, ctx->cursor, 0xff0052e0 );
910
911 ui_end(ctx);
912 ui_end_down(ctx);
913 }
914
915 /* Shortnames */
916 #define gui_draw(...) ui_draw( &ui_global_ctx, __VA_ARGS__)
917 #define gui_current(...) ui_current( &ui_global_ctx, __VA_ARGS__)
918 #define gui_new_node() ui_new_node( &ui_global_ctx )
919 #define gui_hasmouse(...) ui_hasmouse( &ui_global_ctx, __VA_ARGS__)
920 #define gui_end() ui_end( &ui_global_ctx )
921 #define gui_end_down() ui_end_down( &ui_global_ctx )
922 #define gui_end_right() ui_end_right( &ui_global_ctx )
923 #define gui_fill_y() ui_fill_y( &ui_global_ctx)
924 #define gui_fill_x() ui_fill_x( &ui_global_ctx)
925 #define gui_align_bottom() ui_align_bottom( &ui_global_ctx )
926 #define gui_align_right() ui_align_right( &ui_global_ctx )
927 #define gui_align_top() ui_align_top( &ui_global_ctx )
928 #define gui_align_left() ui_align_left( &ui_global_ctx )
929 #define gui_clamp_rect(...) ui_clamp_rect( &ui_global_ctx, __VA_ARGS__)
930 #define gui_capture_mouse(...) ui_capture_mouse( &ui_global_ctx, __VA_ARGS__)
931 #define gui_set_clip(...) ui_set_clip( &ui_global_ctx, __VA_ARGS__)
932 #define gui_release_clip() ui_release_clip( &ui_global_ctx )
933 #define gui_fill_rect_uv(...) ui_fill_rect_uv( &ui_global_ctx, __VA_ARGS__)
934 #define gui_fill_rect(...) ui_fill_rect( &ui_global_ctx, __VA_ARGS__)
935 #define gui_text(...) ui_text( &ui_global_ctx, __VA_ARGS__)
936 #define gui_begin(...) ui_begin( &ui_global_ctx, __VA_ARGS__)
937 #define gui_resolve(...) ui_resolve( &ui_global_ctx, __VA_ARGS__)
938 #define gui_set_mouse(...) ui_set_mouse( &ui_global_ctx, __VA_ARGS__)
939 #define gui_button(...) ui_button( &ui_global_ctx, __VA_ARGS__)
940 #define gui_window(...) ui_window( &ui_global_ctx, __VA_ARGS__)
941 #define gui_want_mouse() ui_want_mouse( &ui_global_ctx )
942 #define gui_push_image(...) ui_push_image( &ui_global_ctx, __VA_ARGS__ )
943 #define gui_reset_colours(...) ui_reset_colours( &ui_global_ctx )
944
945 #endif