#include #include "base.h" #include "../game.h" // =========== [ impl ] =========== #include "../util/input.h" #include "../render/gl_2d.h" #include "../render/gl_2d_font.h" // ================================ __gui_internal _gui; void gui_load_font( GL_DATA* gl, __gui_internal::__font* fnt, const char* name ) { // todo: this probably crashes when fonts r re-created GL_FONT** fptr = gl->fonts.where( fn( GL_FONT** ptr ) { GL_FONT* f = *ptr; return strstr( f->name, name ) != 0; } ); if( !fptr ) { dlog( "gui_load_font() : error loading font %s\n", name ); return; } fnt->glfnt = *fptr; } void gui_init( GAME_DATA* game ) { GL_DATA* gl = game->gl; gui_load_font( gl, &_gui.fonts.jpn12, "jpn12" ); gui_load_font( gl, &_gui.fonts.jpn16, "jpn16" ); } void gui_end() { _gui.cur_window = 0; } void gui_draw( GAME_DATA* game ) { _gui.batch = game->render.batch_2d; gl_batch_empty( _gui.batch ); _gui.windows.each( fn( GUI_WINDOW** w ) { GUI_WINDOW* wnd = *w; if( !wnd->enabled ) return; wnd->draw_fn( wnd ); } ); gl_batch_draw( _gui.batch ); } void gui_input( GAME_DATA* game ) { for( I32 i = (I32)_gui.windows.size - 1; i >= 0; --i ) { GUI_WINDOW* wnd = _gui.windows[i]; if( !wnd->enabled ) continue; wnd->input_fn( wnd ); break; } } void gui_onframe( GAME_DATA* game ) { _profiled gui_input( game ); gui_draw( game ); gui_run_callbacks(); } void gui_free( GUI_BASE* node ) { node->children.each( fn( GUI_BASE** ptr ) { GUI_BASE* child = *ptr; gui_free( child ); } ); node->children.clear(); if( !node->parent ) { I32 idx = _gui.windows.idx_of( (GUI_WINDOW*)node ); if( idx != -1 ) _gui.windows.erase( idx ); if( _gui.cur_window == node ) { if( _gui.windows.size > 0 ) { idx = idx > _gui.windows.size - 1 ? _gui.windows.size - 1 : 0; _gui.cur_window = _gui.windows[idx]; } } } else if( _gui.cur_view == node ) { _gui.cur_view = 0; for( U32 i = 0; i < node->parent->children.size; ++i ) { GUI_BASE* n = node->parent->children.data[i]; if( n == node ) continue; if( !strcmp( n->name, "BASE_VIEW" ) ) { _gui.cur_view = (GUI_VIEW*)n; break; } n = gui_find_node( n, "BASE_VIEW" ); if( n != node ) { _gui.cur_view = (GUI_VIEW*)n; break; } } } delete node; } I32 gui_relx( GUI_BASE* node ) { I32 x = node->x; for( GUI_BASE* p = node->parent; !!p; p = p->parent ) { x += p->x; } return x; } I32 gui_rely( GUI_BASE* node ) { I32 y = node->y; for( GUI_BASE* p = node->parent; !!p; p = p->parent ) { y += p->y; } return y; } GUI_BASE* gui_find_node( GUI_BASE* root, const char* name ) { if( !root ) return 0; if( !strcmp( root->name, name ) ) return root; for( U32 i = 0; i < root->children.size; ++i ) { GUI_BASE* child = root->children.data[i]; GUI_BASE* node = gui_find_node( child, name ); if( node ) return node; } return 0; } GUI_WINDOW* gui_get_parent_wnd( GUI_BASE* node ) { if( !node->parent ) return (GUI_WINDOW*)node; for( GUI_BASE* b = node; !!b; b = b->parent ) { if( !b->parent ) return (GUI_WINDOW*)b; } return (GUI_WINDOW*)node; } void gui_empty_children( GUI_BASE* node ) { node->children.each( fn( GUI_BASE** ptr ) { gui_free( *ptr ); } ); node->children.clear(); } GUI_VIEW* gui_get_view() { return _gui.cur_view; } void gui_set_view( struct GUI_VIEW* view ) { _gui.cur_view = view; } GUI_WINDOW* gui_get_window() { return _gui.cur_window; } void gui_set_window( GUI_WINDOW* window ) { _gui.cur_window = window; } void gui_bring_to_top( GUI_WINDOW* window ) { for( U32 i = 0; i < _gui.windows.size; ++i ) { if( _gui.windows[i] == window ) { _gui.windows.erase( i ); break; } } _gui.windows.push( window ); } U8 gui_is_fg_window( GUI_BASE* node ) { GUI_WINDOW* top_wnd; for( I32 i = _gui.windows.size - 1; i >= 0; --i ) { GUI_WINDOW* wnd = _gui.windows[i]; if( !wnd->enabled ) continue; top_wnd = wnd; break; } if( !node->parent ) { return top_wnd == node; } return gui_get_parent_wnd( node ) == top_wnd; } U8 gui_check_target() { if( !_gui.cur_view ) { dlog( "gui_check_target() : no view" ); return 0; } return 1; } void gui_run_callbacks() { LIST<__gui_internal::callback_entry> cb_copy = _gui.callbacks; _gui.callbacks.clear(); for( I32 i = cb_copy.size - 1; i >= 0; --i ) { __gui_internal::callback_entry* e = &cb_copy[i]; if( e->callback ) e->callback( e->data ); } } void gui_push_callback( GUI_CALLBACK cb ) { __gui_internal::callback_entry e; e.data = 0; e.callback = cb; _gui.callbacks.push( e ); } void gui_push_callback( void* data, GUI_CALLBACK cb ) { __gui_internal::callback_entry e; e.data = data; e.callback = cb; _gui.callbacks.push( e ); } void gui_base_input_fn( void* ptr ) { GUI_BASE* e = (GUI_BASE*)ptr; if( !e ) return; e->children.each( fn( GUI_BASE** ptr ) { GUI_BASE* child = *ptr; if( child->enabled && child->input_fn ) child->input_fn( child ); } ); } void gui_draw_frect( I32 x, I32 y, I32 w, I32 h, CLR col ) { gl_2d_frect( _gui.batch, { (F32)x, (F32)y }, { (F32)w, (F32)h }, col ); } void gui_draw_rect( I32 x, I32 y, I32 w, I32 h, CLR col ) { gl_2d_rect( _gui.batch, { (F32)x, (F32)y }, { (F32)w, (F32)h }, col ); } void gui_draw_line( I32 x0, I32 y0, I32 x1, I32 y1, CLR col ) { gl_2d_line( _gui.batch, { (F32)x0, (F32)y0 }, { (F32)x1, (F32)y1 }, col ); } GL_FONT* gui_font_from_idx( U32 fnt ) { if( fnt > FNT_LAST ) { dlog( "gui_font_from_idx() : invalid font index %d", fnt ); return 0; } __gui_internal::__font* pfnt; switch( fnt ) { case FNT_JPN12: pfnt = &_gui.fonts.jpn12; break; case FNT_JPN16: pfnt = &_gui.fonts.jpn16; break; default: return 0; }; return pfnt->glfnt; } void gui_draw_str_internal( I32 x, I32 y, U8 align, U32 fnt, CLR col, const char* str ) { GL_FONT* pfnt = gui_font_from_idx( fnt ); if( !pfnt ) return; I32 w; gui_draw_get_str_bounds( &w, 0, fnt, str ); switch( align ) { case ALIGN_R: x -= w; break; case ALIGN_C: x -= (I32)( w * 0.5f ); break; } gl_font_draw( pfnt, _gui.batch, { (F32)x, (F32)y }, str, col ); } void gui_draw_str( I32 x, I32 y, U8 align, U32 fnt, CLR col, const char* fmt, ... ) { va_list args; va_start( args, fmt ); char buf[GUI_STR_BUF_MAX]; vsprintf( buf, fmt, args ); va_end( args ); gui_draw_str_internal( x, y, align, fnt, col, buf ); } void gui_draw_get_str_bounds_internal( I32* w, I32* h, U32 fnt, const char* buf ) { GL_FONT* pfnt = gui_font_from_idx( fnt ); if( !pfnt ) return; VEC2 dim = gl_font_dim( pfnt, buf ); if( w ) *w = dim.x; if( h ) *h = dim.y; } void gui_draw_get_str_bounds( I32* w, I32* h, U32 fnt, const char *fmt, ... ) { va_list args; va_start( args, fmt ); char buf[GUI_STR_BUF_MAX]; vsprintf( buf, fmt, args ); va_end( args ); gui_draw_get_str_bounds_internal( w, h, fnt, buf ); } void gui_draw_get_clip( I32 *x, I32 *y, I32 *w, I32 *h ) { VEC2 start, dim; gl_get_clip( _gui.batch->gl, &start, &dim ); if( x ) *x = (I32)start.x; if( y ) *y = (I32)start.y; if( w ) *w = (I32)dim.x; if( h ) *h = (I32)dim.y; } void gui_draw_set_clip( I32 x, I32 y, I32 w, I32 h ) { VEC2 start, dim; start.x = (F32)x; start.y = (F32)y; dim.x = (F32)w; dim.y = (F32)h; gl_set_clip( _gui.batch->gl, start, dim ); } void gui_draw_reset_clip() { gl_reset_clip( _gui.batch->gl ); } void gui_draw_push_clip( I32 x, I32 y, I32 w, I32 h ) { I32 cx,cy,cw,ch; gui_draw_get_clip( &cx, &cy, &cw, &ch ); __gui_internal::clip_rect rect{ cx, cy, cw, ch }; _gui.clip_rects.push( rect ); gui_draw_set_clip( x, y, w, h ); } void gui_draw_pop_clip() { if( !_gui.clip_rects.size ) { dlog( "gui_draw_pop_clip() : clip stack empty\n" ); return gui_draw_reset_clip(); } __gui_internal::clip_rect rect = _gui.clip_rects.pop(); gui_draw_set_clip( rect.x, rect.y, rect.w, rect.h ); } void gui_cursor_pos( I32 *x, I32 *y ) { if( x ) *x = input.mouse.pos.x; if( y ) *y = input.mouse.pos.y; } U8 gui_mbutton_down( U8 button ) { switch( button ) { case GUI_MBTNLEFT: return input.mouse.left; case GUI_MBTNRIGHT: return input.mouse.right; case GUI_MBTNMIDDLE: return input.mouse.middle; case GUI_MBTNSCROLL: return input.mouse.wheel; }; return 0; } void gui_capture_scroll() { input.mouse.wheel = 0; } U8 gui_key_down( U8 key ) { return input.keys[key]; } F32 gui_frametime() { return _gui.batch->gl->frametime; } F32 gui_time() { return u_time(); }