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