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