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