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