diff options
Diffstat (limited to 'src/gui')
| -rw-r--r-- | src/gui/base.cpp | 405 | ||||
| -rw-r--r-- | src/gui/base.h | 314 | ||||
| -rw-r--r-- | src/gui/button.cpp | 66 | ||||
| -rw-r--r-- | src/gui/checkbox.cpp | 78 | ||||
| -rw-r--r-- | src/gui/colorinput.cpp | 59 | ||||
| -rw-r--r-- | src/gui/console.h | 0 | ||||
| -rw-r--r-- | src/gui/floatinput.cpp | 257 | ||||
| -rw-r--r-- | src/gui/label.cpp | 40 | ||||
| -rw-r--r-- | src/gui/list.cpp | 98 | ||||
| -rw-r--r-- | src/gui/textbox.cpp | 246 | ||||
| -rw-r--r-- | src/gui/vectorinput.cpp | 120 | ||||
| -rw-r--r-- | src/gui/view.cpp | 59 | ||||
| -rw-r--r-- | src/gui/window.cpp | 113 |
13 files changed, 1855 insertions, 0 deletions
diff --git a/src/gui/base.cpp b/src/gui/base.cpp new file mode 100644 index 0000000..5d345d8 --- /dev/null +++ b/src/gui/base.cpp @@ -0,0 +1,405 @@ +#include <string.h> + +#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.gl2d = game->shaders.gl2d; + _gui.gl2d_font = game->shaders.gl2d_texcoord; + + _gui.windows.each( fn( GUI_WINDOW** w ) { + GUI_WINDOW* wnd = *w; + if( !wnd->enabled ) return; + + wnd->draw_fn( wnd ); + } ); +} + +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 ) { + gui_run_callbacks(); + + gui_input( game ); + gui_draw( game ); +} + +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.gl2d, { (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.gl2d, { (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.gl2d, { (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.gl2d_font, { (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.gl2d->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.gl2d->gl, start, dim ); +} + +void gui_draw_reset_clip() { + gl_reset_clip( _gui.gl2d->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.gl2d->gl->frametime; +} + +F32 gui_time() { + return u_time(); +} diff --git a/src/gui/base.h b/src/gui/base.h new file mode 100644 index 0000000..96e0fa5 --- /dev/null +++ b/src/gui/base.h @@ -0,0 +1,314 @@ +#pragma once +#include "../util/color.h" +#include "../util/allocator.h" + +// ======================================= [ colorscheme ] ======================================== + +static const struct { + CLR txt = CLR::WHITE(); + CLR txt_inactive = CLR::blend( CLR::WHITE(), CLR::BLACK(), 0.3f ); + CLR border = CLR::WHITE(); + CLR border_inactive = CLR::blend( CLR::WHITE(), CLR::BLACK(), 0.55f ); + CLR bg = CLR::blend( CLR::WHITE(), CLR::BLACK(), 0.92f ); + CLR bg_sec = CLR::blend( CLR::WHITE(), CLR::BLACK(), 0.97f ); + CLR bg_alt = CLR::blend( CLR::WHITE(), CLR::BLACK(), 0.985f ); +} ui_clr; + +// ========================================== [ base ] ============================================ + +const U32 GUI_STR_BUF_MAX = 16384; +const U32 GUI_TEXTBOX_MAX = 1024; +const U32 GUI_NAME_LEN = 512; + +/* impl */ +extern void gui_init( struct GAME_DATA* game ); +extern void gui_draw( struct GAME_DATA* game ); +extern void gui_input( struct GAME_DATA* game ); +extern void gui_onframe( struct GAME_DATA* game ); +extern void gui_end(); +/* */ + +extern I32 gui_relx( struct GUI_BASE* node ); //relative x pos for node +extern I32 gui_rely( struct GUI_BASE* node ); //relative y pos for node + +extern struct GUI_BASE* gui_find_node( struct GUI_BASE* root, const char* name ); +extern struct GUI_WINDOW* gui_get_parent_wnd( struct GUI_BASE* node ); +extern void gui_empty_children( struct GUI_BASE* node ); + +extern void gui_free( struct GUI_BASE* node ); + +extern struct GUI_VIEW* gui_get_view(); // gets currently edited view +extern void gui_set_view( struct GUI_VIEW* view ); // sets currently edited view +extern struct GUI_WINDOW* gui_get_window(); // gets currently edited window +extern void gui_set_window( struct GUI_WINDOW* wnd ); // sets currently edited window +extern void gui_bring_to_top( struct GUI_WINDOW* wnd ); // pushes window to be rendered last +extern U8 gui_is_fg_window( struct GUI_BASE* node ); // 1 if parent window (or window itself) is rendered last + +// ======================================= [ components ] ========================================= + +struct GUI_LIST_ENTRY { + I32 val; + char title[256]; +}; + +typedef void( *GUI_CALLBACK )( void* ptr ); + +extern struct GUI_VIEW* gui_view( I32 x, I32 y, I32 w, I32 h ); // sets current view +extern struct GUI_WINDOW* gui_window( I32 w, I32 h ); // sets current view and window +extern struct GUI_WINDOW* gui_window( I32 x, I32 y, I32 w, I32 h ); // sets current view and window +extern struct GUI_TITLE* gui_title( const char* title ); + +extern struct GUI_LABEL* gui_label( I32 x, I32 y, const char* title, ... ); +extern struct GUI_BUTTON* gui_button( I32 x, I32 y, I32 w, I32 h, const char* title, GUI_CALLBACK cb ); +extern struct GUI_TEXTBOX* gui_textbox( I32 x, I32 y, I32 w, I32 h, const char* title, U32 maxlen, U8 allow_newl = 0 ); +extern struct GUI_CHECKBOX* gui_checkbox( I32 x, I32 y, const char* title, U8* pval ); + +extern struct GUI_LIST* gui_list( I32 x, I32 y, I32 w, I32 h, const char* title, LIST<GUI_LIST_ENTRY>* list, I32* pval ); +extern struct GUI_LIST_ENTRY* gui_list_get_selected( struct GUI_LIST* list ); + +extern struct GUI_FLOATINPUT* gui_floatinput( + I32 x, I32 y, I32 w, + const char* title, + F32* pval, + F32 min, + F32 max, + F32 step = 1.f, + const char* printfmt = "%.2f" +); + +extern struct GUI_VECTORINPUT* gui_vectorinput( + I32 x, I32 y, I32 w, + const char* title, + F32* pval, + U32 valc, + F32 min, + F32 max, + F32 step = 1.f, + const char* letters = "xyzw", + const char* printfmt = "%.2f" +); + +extern struct GUI_COLORINPUT* gui_colorinput( + I32 x, + I32 y, + I32 w, + const char* title, + CLR* pval, + U8 showalpha = 1 +); + +// ======================================= [ definitions ] ======================================== + +enum GuiTxtAlign_t { + ALIGN_L, + ALIGN_R, + ALIGN_C +}; + +enum GuiFont_t { + FNT_JPN12, + FNT_JPN16, + FNT_LAST +}; + +typedef void( *GUI_DRAW_FN )( void* el ); +typedef void( *GUI_INPUT_FN )( void* el ); + +extern void gui_base_input_fn( void* ); + +struct GUI_BASE { + I32 x{}, y{}; + I32 w{}, h{}; + U8 enabled{1}; + U8 canvas{}; + + char name[GUI_NAME_LEN]; + + I32 xbound{}, ybound{}; + I32 xoff{}, yoff{}; + + GUI_BASE* parent{}; + LIST<GUI_BASE*> children{}; + + GUI_DRAW_FN draw_fn{}; + GUI_INPUT_FN input_fn = gui_base_input_fn; +}; + +struct GUI_VIEW : GUI_BASE { + U8 initheld; +}; +struct GUI_WINDOW : GUI_VIEW { + U8 locked{}; + U8 ontop{}; +}; + +struct GUI_LABEL : GUI_BASE {}; + +struct GUI_TITLE : GUI_BASE { + I32 xoff, yoff; + U8 held{}; +}; + +struct GUI_LIST : GUI_BASE { + LIST<GUI_LIST_ENTRY>* plist; + I32* pval; + U8 held; + + GUI_CALLBACK cb; +}; + +struct GUI_BUTTON : GUI_BASE { + GUI_CALLBACK cb; + U8 held; + void* extra; +}; + +struct GUI_CHECKBOX : GUI_BASE { + U8 held; + + U8* pval; + GUI_CALLBACK cb; +}; + +struct GUI_TEXTBOX : GUI_BASE { + char value[GUI_TEXTBOX_MAX]; + + U32 len; + U32 maxlen; + + U8 keys[0xff]; + U8 prevkeys[0xff]; + + U8 heldkey; + F32 heldtime; + F32 repeattime; + + F32 blinktime; + U8 blink; + + U8 allow_newl; + U8 active; + + U8 m1held; + + GUI_CALLBACK cb; +}; + +struct GUI_FLOATINPUT : GUI_BASE { + const char* valfmt; + F32* pval; + + F32 min; + F32 max; + F32 step; + + I32 lastmx; + U8 heldoutbounds; + U8 held; + + // for color sliders + CLR bgcol; + U8 customclr; + + U8 wraparound; + GUI_CALLBACK cb; +}; + +struct GUI_VECTORINPUT : GUI_VIEW { + const char* valfmt; + F32* pval; + U32 valc; + + const char* letters; + F32 min; + F32 max; + F32 step; + + GUI_VIEW* inputview; + LIST<GUI_FLOATINPUT*> inputs; + + GUI_CALLBACK cb; +}; + +struct GUI_COLORINPUT : GUI_VECTORINPUT {}; + +// ======================================== [ internal ] ========================================== + +extern void gui_push_callback( GUI_CALLBACK cb ); +extern void gui_push_callback( void* data, GUI_CALLBACK cb ); +extern void gui_run_callbacks(); + +extern U8 gui_check_target(); + +extern void gui_draw_line( I32 x0, I32 y0, I32 x1, I32 y1, CLR col ); +extern void gui_draw_rect( I32 x, I32 y, I32 w, I32 h, CLR col ); +extern void gui_draw_frect( I32 x, I32 y, I32 w, I32 h, CLR col ); +extern void gui_draw_circle( I32 x, I32 y, I32 r, CLR col ); + +extern void gui_draw_str( I32 x, I32 y, U8 align, U32 fnt, CLR col, const char* fmt, ... ); +extern void gui_draw_get_str_bounds( I32* w, I32* h, U32 fnt, const char* fmt, ... ); + +extern void gui_draw_get_clip( I32* x, I32* y, I32* w, I32* h ); +extern void gui_draw_set_clip( I32 x, I32 y, I32 w, I32 h ); +extern void gui_draw_reset_clip(); +extern void gui_draw_push_clip( I32 x, I32 y, I32 w, I32 h ); +extern void gui_draw_pop_clip(); + +extern F32 gui_frametime(); +extern F32 gui_time(); +extern void gui_cursor_pos( I32* x, I32* y ); +extern U8 gui_key_down( U8 key ); +extern U8 gui_mbutton_down( U8 button ); +extern void gui_capture_scroll(); + +enum __gui_internal_mouse_button { + GUI_MBTNLEFT, + GUI_MBTNRIGHT, + GUI_MBTNMIDDLE, + GUI_MBTNSCROLL +}; + +struct __gui_internal { + GUI_VIEW* cur_view; + GUI_WINDOW* cur_window; + LIST<GUI_WINDOW*> windows; + struct clip_rect { + I32 x,y,w,h; + }; + + LIST<clip_rect> clip_rects; + struct callback_entry { + GUI_CALLBACK callback; + void* data; + }; + + LIST<callback_entry> callbacks; + + struct __font { + struct GL_FONT* glfnt; + }; + + struct { + __font jpn12; + __font jpn16; + } fonts; + + // draw stuff below + struct GL_SHADER_PROGRAM* gl2d; + struct GL_SHADER_PROGRAM* gl2d_font; +}; + +void __gui_internal_vectorinput_init( + GUI_VECTORINPUT* input, + I32 x, I32 y, I32 w, + const char* title, + F32* pval, + U32 valc, + F32 min, + F32 max, + F32 step, + const char* letters, + const char* printfmt +); + +extern __gui_internal _gui; diff --git a/src/gui/button.cpp b/src/gui/button.cpp new file mode 100644 index 0000000..2b57772 --- /dev/null +++ b/src/gui/button.cpp @@ -0,0 +1,66 @@ +#include "base.h" + +void gui_button_draw_fn( void* ptr ) { + GUI_BUTTON* btn = (GUI_BUTTON*)ptr; + + I32 x = gui_relx( btn ); + I32 y = gui_rely( btn ); + + CLR col = gui_is_fg_window( btn )? ui_clr.border : ui_clr.border_inactive; + gui_draw_frect( x, y, btn->w, btn->h, col ); + gui_draw_frect( x+1, y+1, btn->w-2, btn->h-2, ui_clr.bg_sec ); + + I32 middle = x + btn->w/2; + I32 middle_y = y + btn->h/2 - 7; + + gui_draw_str( middle, middle_y, ALIGN_C, FNT_JPN12, ui_clr.txt, btn->name ); +} + +void gui_button_input_fn( void* ptr ) { + GUI_BUTTON* btn = (GUI_BUTTON*)ptr; + + I32 x = gui_relx( btn ); + I32 y = gui_rely( btn ); + + U8 m1 = gui_mbutton_down( 0 ); + I32 mx, my; + gui_cursor_pos( &mx, &my ); + + U8 inbounds = mx >= x && mx <= x + btn->w && my >= y && my <= y + btn->h; + + if( !m1 ) { + // button could be destroyed by callback + U8 was_held = btn->held; + btn->held = 0; + if( inbounds && was_held ) + btn->cb( btn ); + + return; + } + + if( inbounds ) { + btn->held = 1; + } +} + +GUI_BUTTON* gui_button( I32 x, I32 y, I32 w, I32 h, const char* title, GUI_CALLBACK cb ) { + if( !gui_check_target() ) return 0; + + GUI_BUTTON* btn = new GUI_BUTTON; + btn->x = x; + btn->y = y; + btn->xbound = btn->w = w; + btn->ybound = btn->h = h; + btn->cb = cb; + btn->draw_fn = gui_button_draw_fn; + btn->input_fn = gui_button_input_fn; + + btn->parent = _gui.cur_view; + strcpy( btn->name, title ); + + btn->extra = 0; + btn->held = 0; + + gui_get_view()->children.push( btn ); + return btn; +} diff --git a/src/gui/checkbox.cpp b/src/gui/checkbox.cpp new file mode 100644 index 0000000..02cadc6 --- /dev/null +++ b/src/gui/checkbox.cpp @@ -0,0 +1,78 @@ +#include "base.h" + +const I32 CHECKBOX_SIZE = 14; + +void gui_checkbox_draw_fn( void* ptr ) { + GUI_CHECKBOX* check = (GUI_CHECKBOX*)ptr; + + I32 x = gui_relx( check ); + I32 y = gui_rely( check ); + + I32 half = CHECKBOX_SIZE / 2; + + CLR col = gui_is_fg_window( check )? ui_clr.border : ui_clr.border_inactive; + gui_draw_frect( x, y+2, CHECKBOX_SIZE, CHECKBOX_SIZE, col ); + gui_draw_frect( x+1, y+3, CHECKBOX_SIZE-2, CHECKBOX_SIZE-2, ui_clr.bg_sec ); + + if( *check->pval ) + gui_draw_str( x + half, y, ALIGN_C, FNT_JPN12, ui_clr.txt, "x" ); + + gui_draw_str( x + CHECKBOX_SIZE + 2, y, ALIGN_L, FNT_JPN12, ui_clr.txt, check->name ); +} + +void gui_checkbox_input_fn( void* ptr ) { + GUI_CHECKBOX* check = (GUI_CHECKBOX*)ptr; + + I32 x = gui_relx( check ); + I32 y = gui_rely( check ); + + U8 m1 = gui_mbutton_down( 0 ); + I32 mx, my; + gui_cursor_pos( &mx, &my ); + + gui_draw_get_str_bounds( &check->w, 0, FNT_JPN12, check->name ); + check->w += CHECKBOX_SIZE + 2; + + U8 inbounds = mx >= x && mx <= x + check->w && my >= y && my <= y + check->h; + + if( !m1 ) { + // checkbox could be destroyed by callback + U8 was_held = check->held; + check->held = 0; + if( inbounds && was_held ) { + *check->pval = !*check->pval; + if( check->cb ) + check->cb( check ); + } + + return; + } + + if( inbounds ) + check->held = 1; +} + +GUI_CHECKBOX* gui_checkbox( I32 x, I32 y, const char* title, U8* pval ) { + if( !gui_check_target() ) return 0; + + GUI_CHECKBOX* check = new GUI_CHECKBOX; + check->x = x; + check->y = y; + check->cb = 0; + check->ybound = check->h = 16; + check->draw_fn = gui_checkbox_draw_fn; + check->input_fn = gui_checkbox_input_fn; + + check->pval = pval; + + check->parent = _gui.cur_view; + strcpy( check->name, title ); + + gui_draw_get_str_bounds( &check->w, 0, FNT_JPN12, check->name ); + check->w += CHECKBOX_SIZE + 2; + check->xbound = check->w; + check->ybound = check->h; + + gui_get_view()->children.push( check ); + return check; +} diff --git a/src/gui/colorinput.cpp b/src/gui/colorinput.cpp new file mode 100644 index 0000000..1d649fb --- /dev/null +++ b/src/gui/colorinput.cpp @@ -0,0 +1,59 @@ +#include "base.h" +#include <stdio.h> + +void gui_colorinput_draw_fn( void* ptr ) { + GUI_COLORINPUT* input = (GUI_COLORINPUT*)ptr; + + I32 x = gui_relx( input ); + I32 y = gui_rely( input ); + + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, input->name ); + + CLR val = *(CLR*)input->pval; + char hex[16]; + sprintf( hex, "#%02x%02x%02x", + (I32)(val.r * 255.f), + (I32)(val.g * 255.f), + (I32)(val.b * 255.f) + ); + + gui_draw_str( x + input->w, y, ALIGN_R, FNT_JPN12, ui_clr.txt, hex ); + + CLR border = { + 1.f - val.r, + 1.f - val.g, + 1.f - val.b, + 1.f + }; + + gui_draw_frect( x + input->w - 21, y + 14, 22, 22, border ); + gui_draw_frect( x + input->w - 19, y + 16, 18, 18, CLR::BLACK() ); + gui_draw_frect( x + input->w - 19, y + 16, 18, 18, *(CLR*)(input->pval) ); + + GUI_VIEW* inputview = input->inputview; + inputview->draw_fn( inputview ); +} + +GUI_COLORINPUT* gui_colorinput( I32 x, I32 y, I32 w, const char* title, CLR* pval, U8 showalpha ) { + if( !gui_check_target() ) return 0; + + GUI_COLORINPUT* input = new GUI_COLORINPUT; + + __gui_internal_vectorinput_init( + input, + x, y, + w - 26, + title, + (F32*)pval, + showalpha? 4 : 3, + 0.f, + 1.f, + 1.f / 255.f, + "rgba", + "%.02f" + ); + + input->xbound = input->w = w; + input->draw_fn = gui_colorinput_draw_fn; + return input; +} diff --git a/src/gui/console.h b/src/gui/console.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/gui/console.h diff --git a/src/gui/floatinput.cpp b/src/gui/floatinput.cpp new file mode 100644 index 0000000..9f7fdfe --- /dev/null +++ b/src/gui/floatinput.cpp @@ -0,0 +1,257 @@ +#include "base.h" +#include <math.h> + +U8 gui_floatinput_is_bound_val( GUI_FLOATINPUT* input ) { + if( input->wraparound ) + return 0; + if( !isfinite( input->min ) || isnan( input->min ) ) + return 0; + if( !isfinite( input->max ) || isnan( input->max ) ) + return 0; + if( input->min > input->max ) + return 0; + return 1; +} + +CLR gui_floatinput_get_progress_clr( GUI_FLOATINPUT* input ) { + if( input->customclr ) { + if( gui_is_fg_window( input ) ) return input->bgcol; + else return CLR::blend( input->bgcol, CLR::BLACK(), 0.333f ); + } + + CLR clr_border = gui_is_fg_window( input )? ui_clr.border : ui_clr.border_inactive; + return CLR::blend( clr_border, CLR::BLACK(), 0.5f ); +} + +void gui_floatinput_draw_bound( GUI_FLOATINPUT* input ) { + I32 x = gui_relx( input ); + I32 y = gui_rely( input ); + I32 w = input->w; + I32 h = input->h; + + F32 min = input->min; + F32 max = input->max; + + F32 val = *input->pval; + F32 percent = (val - min) / (max - min); + if( percent > 1.f ) percent = 1.f; + if( percent < 0.f ) percent = 0.f; + + CLR clr_progress = gui_floatinput_get_progress_clr( input ); + gui_draw_frect( x+1, y+1, (I32)( (w-2) * percent ), h-2, clr_progress ); + + gui_draw_push_clip( (x+1) + (I32)( (w-2) * percent ), (y+1), (I32)( (w-2) * (1.f - percent) ), h-2 ); + gui_draw_str( x + 2, y + 2, ALIGN_L, FNT_JPN12, ui_clr.txt, input->name ); + gui_draw_str( x + w - 2, y + 2, ALIGN_R, FNT_JPN12, ui_clr.txt, input->valfmt, val ); + gui_draw_set_clip( (x+1), (y+1), (I32)( (w-2) * percent ), h-2 ); + gui_draw_str( x + 2, y + 2, ALIGN_L, FNT_JPN12, ui_clr.bg_alt, input->name ); + gui_draw_str( x + w - 2, y + 2, ALIGN_R, FNT_JPN12, ui_clr.bg_alt, input->valfmt, val ); + gui_draw_pop_clip(); +} + +void gui_floatinput_draw_unbound( GUI_FLOATINPUT* input ) { + I32 x = gui_relx( input ); + I32 y = gui_rely( input ); + I32 w = input->w; + + F32 val = *input->pval; + + gui_draw_str( x + 2, y + 2, ALIGN_L, FNT_JPN12, ui_clr.txt, input->name ); + gui_draw_str( x + w - 2, y + 2, ALIGN_R, FNT_JPN12, ui_clr.txt, input->valfmt, val ); + + I32 t1w, t2w, t3w; + gui_draw_get_str_bounds( &t1w, 0, FNT_JPN12, input->name ); + gui_draw_get_str_bounds( &t2w, 0, FNT_JPN12, input->valfmt, val ); + gui_draw_get_str_bounds( &t3w, 0, FNT_JPN12, "<->" ); + + I32 stw = t2w + t3w - 2; + I32 pos = w/2; + if( stw > pos ) + pos = stw; + + CLR handleclr = CLR::blend( ui_clr.txt, CLR::BLACK(), .7f ); + + if( t1w < w / 2 && t1w + t2w + t3w + 8 < w ) { + gui_draw_str( + x + w - pos, + y + 2, + ALIGN_C, + FNT_JPN12, + handleclr, + "<->" + ); + } + else { + gui_draw_str( x + w - pos + 8, y, ALIGN_C, FNT_JPN12, handleclr, "-" ); + gui_draw_str( x + w - pos + 8, y + 5, ALIGN_C, FNT_JPN12, handleclr, "+" ); + } +} + +void gui_floatinput_draw_fn( void* ptr ) { + GUI_FLOATINPUT* input = (GUI_FLOATINPUT*)ptr; + + I32 x = gui_relx( input ); + I32 y = gui_rely( input ); + I32 w = input->w; + I32 h = input->h; + + CLR clr_border = gui_is_fg_window( input )? ui_clr.border : ui_clr.border_inactive; + + gui_draw_frect( x, y, w, h, clr_border ); + gui_draw_frect( x+1, y+1, w-2, h-2, ui_clr.bg_sec ); + + if( !gui_floatinput_is_bound_val( input ) ) + gui_floatinput_draw_unbound( input ); + else + gui_floatinput_draw_bound( input ); +} + +void gui_floatinput_input_bound( GUI_FLOATINPUT* input ) { + if( !gui_mbutton_down( GUI_MBTNLEFT ) ) + return; + + I32 x = gui_relx( input ); + I32 w = input->w; + + F32 min = input->min; + F32 max = input->max; + + I32 mx, my; + gui_cursor_pos( &mx, &my ); + + F32 progress = (F32)(mx - x) / w; + if( progress < 0.f ) progress = 0.f; + if( progress > 1.f ) progress = 1.f; + + F32 nval = min + (max - min) * progress; + F32 rmn = remainderf( nval, input->step ); + *input->pval = nval - rmn; +} + +void gui_floatinput_input_unbound( GUI_FLOATINPUT* input ) { + if( !gui_mbutton_down( GUI_MBTNLEFT ) ) + return; + + I32 mx, my; + gui_cursor_pos( &mx, &my ); + + I32 dx = mx - input->lastmx; + if( dx ) + *input->pval += dx * input->step; + + F32 min = input->min; + F32 max = input->max; + + if( isfinite( min ) && *input->pval < min ) + *input->pval = input->wraparound? max : min; + if( isfinite( max ) && *input->pval > max ) + *input->pval = input->wraparound? min : max; + + F32 rmn = remainderf( *input->pval, input->step ); + *input->pval -= rmn; +} + +void gui_floatinput_input_scroll( GUI_FLOATINPUT* input ) { + U8 scroll = gui_mbutton_down( GUI_MBTNSCROLL ); + gui_capture_scroll(); + F32 oldval = *input->pval; + F32 nval = oldval; + + if( !scroll ) + return; + + if( scroll == 1 ) + nval += input->step; + else if( scroll == (U8)-1 ) + nval -= input->step; + + if( isfinite( input->min ) && nval < input->min ) + nval = input->wraparound? input->max : input->min; + if( isfinite( input->max ) && nval > input->max ) + nval = input->wraparound? input->min : input->max; + + F32 rmn = remainderf( nval, input->step ); + *input->pval = nval - rmn; + + if( input->cb ) + input->cb( input ); +} + +void gui_floatinput_input_fn( void* ptr ) { + GUI_FLOATINPUT* input = (GUI_FLOATINPUT*)ptr; + + I32 m1 = gui_mbutton_down( 0 ); + + I32 x = gui_relx( input ); + I32 y = gui_rely( input ); + I32 w = input->w; + I32 h = input->h; + + I32 mx, my; + gui_cursor_pos( &mx, &my ); + U8 inbounds = mx >= x && mx <= x + w && my >= y && my <= y + h; + + if( inbounds ) + gui_floatinput_input_scroll( input ); + + if( !input->held && m1 && !inbounds ) + input->heldoutbounds = 1; + if( !input->heldoutbounds && m1 && inbounds ) { + if( !input->held ) { + input->lastmx = mx; + input->held = 1; + return; + } + } + + if( !m1 ) { + input->heldoutbounds = 0; + input->held = 0; + return; + } + + if( input->heldoutbounds ) + return; + + F32 oldv = *input->pval; + if( !gui_floatinput_is_bound_val( input ) ) + gui_floatinput_input_unbound( input ); + else + gui_floatinput_input_bound( input ); + + if( input->cb && oldv != *input->pval ) { + input->cb( input ); + } + + input->lastmx = mx; +} + +struct GUI_FLOATINPUT* gui_floatinput( I32 x, I32 y, I32 w, const char* title, F32* pval, F32 min, F32 max, F32 step, const char* valfmt ) { + if( !gui_check_target() ) return 0; + + GUI_FLOATINPUT* input = new GUI_FLOATINPUT; + input->x = x; + input->y = y; + input->xbound = input->w = w; + input->ybound = input->h = 20; + strcpy( input->name, title ); + input->input_fn = gui_floatinput_input_fn; + input->draw_fn = gui_floatinput_draw_fn; + + input->cb = 0; + input->pval = pval; + input->min = min; + input->max = max; + input->step = step; + input->valfmt = valfmt; + + input->wraparound = 0; + input->customclr = 0; + input->held = 0; + + GUI_VIEW* parent = gui_get_view(); + parent->children.push( input ); + input->parent = parent; + + return input; +} diff --git a/src/gui/label.cpp b/src/gui/label.cpp new file mode 100644 index 0000000..b5d6290 --- /dev/null +++ b/src/gui/label.cpp @@ -0,0 +1,40 @@ +#include "base.h" +#include <stdarg.h> +#include <stdio.h> + +void gui_label_draw_fn( void* ptr ) { + GUI_LABEL* label = (GUI_LABEL*)ptr; + + I32 x = gui_relx( label ); + I32 y = gui_rely( label ); + + gui_draw_get_str_bounds( &label->w, &label->h, FNT_JPN12, label->name ); + label->xbound = label->w; + label->ybound = label->h; + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, label->name ); +} + +GUI_LABEL* gui_label( I32 x, I32 y, const char* title, ... ) { + if( !gui_check_target() ) return 0; + char buf[GUI_NAME_LEN]; + + va_list args; + va_start( args, title ); + vsprintf( buf, title, args ); + va_end( args ); + + GUI_LABEL* label = new GUI_LABEL; + label->x = x; + label->y = y; + label->draw_fn = gui_label_draw_fn; + strcpy( label->name, buf ); + + gui_draw_get_str_bounds( &label->w, &label->h, FNT_JPN12, label->name ); + label->xbound = label->w; + label->ybound = label->h; + + label->parent = gui_get_view(); + label->parent->children.push( label ); + + return label; +} diff --git a/src/gui/list.cpp b/src/gui/list.cpp new file mode 100644 index 0000000..90f2f49 --- /dev/null +++ b/src/gui/list.cpp @@ -0,0 +1,98 @@ +#include "base.h" + +const I32 LIST_TITLE_OFFSET = 15; +const I32 LIST_ITEM_HEIGHT = 18; + +void gui_list_draw_fn( void* ptr ) { + GUI_LIST* list = (GUI_LIST*)ptr; + + I32 x = gui_relx( list ); + I32 y = gui_rely( list ); + + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, list->name ); + y += LIST_TITLE_OFFSET; + + CLR col = gui_is_fg_window( list )? ui_clr.border : ui_clr.border_inactive; + gui_draw_frect( x, y, list->w, list->h, col ); + gui_draw_frect( x+1, y+1, list->w-2, list->h-2, ui_clr.bg_sec ); + + I32 yoff = 0; + list->plist->each( fn( GUI_LIST_ENTRY* e ) { + U8 selected = e->val == *list->pval; + CLR col = selected? ui_clr.txt : ui_clr.txt_inactive; + + gui_draw_str( x + 4, y + yoff + 6, ALIGN_L, FNT_JPN12, col, e->title ); + yoff += LIST_ITEM_HEIGHT; + } ); +} + +void gui_list_input_fn( void* ptr ) { + GUI_LIST* list = (GUI_LIST*)ptr; + + I32 x = gui_relx( list ); + I32 y = gui_rely( list ); + I32 w = list->w; + I32 h = list->h; + + U8 m1 = gui_mbutton_down( 0 ); + I32 mx, my; + gui_cursor_pos( &mx, &my ); + + U8 inbounds = mx >= x && mx <= x + w && my >= y && my <= y + h; + if( !inbounds ) + return; + + I32 diff = my - y; + I32 idx = diff / LIST_ITEM_HEIGHT - 1; + if( idx >= list->plist->size ) idx = list->plist->size - 1; + if( idx < 0 ) idx = 0; + + if( m1 ) { + if( !list->held ) { + GUI_LIST_ENTRY* e = &list->plist->data[idx]; + I32 prevval = *list->pval; + *list->pval = e->val; + + if( list->cb && prevval != *list->pval ) + list->cb( list ); + } + list->held = 1; + } else { + list->held = 0; + } +} + +GUI_LIST* gui_list( I32 x, I32 y, I32 w, I32 h, const char* title, LIST<GUI_LIST_ENTRY>* plist, I32* pval ) { + if( !gui_check_target() ) return 0; + + GUI_LIST* list = new GUI_LIST; + list->x = x; + list->y = y; + list->w = w; + list->h = h; + + list->xbound = w; + list->ybound = h + LIST_TITLE_OFFSET; + + list->cb = 0; + list->pval = pval; + list->plist = plist; + list->draw_fn = gui_list_draw_fn; + list->input_fn = gui_list_input_fn; + strcpy( list->name, title ); + + list->parent = gui_get_view(); + + gui_get_view()->children.push( list ); + return list; +} + +GUI_LIST_ENTRY* gui_list_get_selected( GUI_LIST* list ) { + for( U32 i = 0; i < list->plist->size; ++i ) { + GUI_LIST_ENTRY* e = &list->plist->data[i]; + if( e->val == *list->pval ) + return e; + } + + return 0; +} diff --git a/src/gui/textbox.cpp b/src/gui/textbox.cpp new file mode 100644 index 0000000..688450b --- /dev/null +++ b/src/gui/textbox.cpp @@ -0,0 +1,246 @@ +#include "base.h" +#include "../util/input.h" + +#include <SDL_keycode.h> + +const U32 TEXTBOX_TITLE_OFFSET = 15; + +U8 is_printable_char( U8 key ) { + return key >= 32 && key < 127; +} + +void gui_textbox_draw_newline_str( GUI_TEXTBOX* tb, I32 x, I32 y, CLR clr, I32* endposx, I32* endposy ) { + char linebuf[GUI_TEXTBOX_MAX]; + char* start = tb->value; + I32 tw, th; + + for( char* c = start; !!*c; ++c ) { + if( *c == '\n' ) { + U32 len = (U32)( c - start ); + memcpy( linebuf, start, len ); + linebuf[len] = 0; + + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, clr, linebuf ); + gui_draw_get_str_bounds( &tw, &th, FNT_JPN12, linebuf ); + y += th + 1; + + *endposx = x + tw; + *endposy = y + th; + + if( *(c+1) == 0 ) + break; + + start = ++c; + } + else if( (*c+1) == 0 ) { + U32 len = (U32)( c - start ); + memcpy( linebuf, start, len ); + linebuf[len] = 0; + + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, clr, linebuf ); + gui_draw_get_str_bounds( &tw, &th, FNT_JPN12, linebuf ); + y += th + 1; + *endposx = x + tw; + *endposy = y + th; + + break; + } + } +} + +void gui_textbox_draw_blinker( GUI_TEXTBOX* tb, I32 x, I32 y, CLR clr ) { + if( !tb->active ) + return; + if( gui_time() - tb->blinktime > 1.0f ) { + tb->blink = !tb->blink; + tb->blinktime = gui_time(); + } + + if( tb->blink ) + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, clr, "|" ); +} + +void gui_textbox_draw_fn( void* ptr ) { + GUI_TEXTBOX* tb = (GUI_TEXTBOX*)ptr; + + I32 x = gui_relx( tb ); + I32 y = gui_rely( tb ); + + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, tb->name ); + y += TEXTBOX_TITLE_OFFSET; + + CLR col = gui_is_fg_window( tb )? ui_clr.border : ui_clr.border_inactive; + + gui_draw_frect( x, y, tb->w, tb->h, col ); + gui_draw_frect( x+1, y+1, tb->w-2, tb->h-2, ui_clr.bg_sec ); + + CLR clr = tb->active? ui_clr.txt : ui_clr.txt_inactive; + I32 ypos = y + 3; + I32 xpos = x + 2; + + I32 endposx, endposy; + + if( tb->allow_newl ) { + gui_textbox_draw_newline_str( tb, xpos, ypos, clr, &endposx, &endposy ); + } else { + gui_draw_str( xpos, ypos, ALIGN_L, FNT_JPN12, clr, tb->value ); + + I32 tw, th; + gui_draw_get_str_bounds( &tw, &th, FNT_JPN12, tb->value ); + endposx = xpos + tw; + endposy = ypos; + } + + gui_textbox_draw_blinker( tb, endposx, endposy, clr ); +} + +void gui_textbox_handle_mouse( GUI_TEXTBOX* tb ) { + I32 x = gui_relx( tb ); + I32 y = gui_rely( tb ) + TEXTBOX_TITLE_OFFSET; + I32 w = tb->w; + I32 h = tb->h; + + U8 m1 = gui_mbutton_down( 0 ); + I32 mx, my; + gui_cursor_pos( &mx, &my ); + + U8 inbounds = mx >= x && mx <= x + w && my >= y && my <= y + h; + + if( m1 ) { + if( inbounds ) + tb->m1held = 1; + else + tb->active = 0; + } else { + if( inbounds && tb->m1held ) { + tb->active = 1; + tb->blink = 1; + tb->blinktime = gui_time(); + } + tb->m1held = 0; + } +} + +void gui_textbox_loop_keys( GUI_TEXTBOX* tb ) { + U32 len = strlen( tb->value ); + + for( U32 i = 0; i < 0xff; ++i ) { + U8 key = tb->keys[i]; + U8 prev = tb->prevkeys[i]; + + if( !key && prev && i == tb->heldkey ) + tb->heldkey = 0; + else if( key && !prev ) { + if( is_printable_char( i ) && len < tb->maxlen - 1 ) { + if( tb->keys[SDLK_LSHIFT & 0xff] || tb->keys[SDLK_RSHIFT & 0xff] ) + tb->value[len] = (char)i + ('a' - 'A'); + else + tb->value[len] = (char)i; + tb->value[++len] = 0; + } + else if( i == '\b' ) { + if( len > 0 ) { + tb->value[--len] = 0; + } + } + else if( i == '\r' ) { + if( tb->allow_newl ) { + tb->value[len] = '\n'; + tb->value[++len] = 0; + } + else { + tb->active = 0; + } + } else { + if( i == '\x1b' ) + tb->active = 0; + if( tb->cb ) + tb->cb( tb ); + continue; + } + + tb->heldkey = i; + tb->heldtime = gui_time(); + if( tb->cb ) + tb->cb( tb ); + } + } +} + +void gui_textbox_handle_repeat( GUI_TEXTBOX* tb ) { + if( !tb->heldkey ) + return; + + F32 delta = gui_time() - tb->heldtime; + if( delta <= 0.75f ) + return; + + if( gui_time() - tb->repeattime > 0.05f ) { + U32 len = strlen( tb->value ); + if( is_printable_char( tb->heldkey ) && len < (tb->maxlen - 1) ) { + if( tb->keys[SDLK_LSHIFT & 0xff] || tb->keys[SDLK_RSHIFT & 0xff] ) + tb->value[len] = tb->heldkey + ('a' - 'A'); + else + tb->value[len] = tb->heldkey; + tb->value[++len] = 0; + if( tb->cb ) + tb->cb( tb ); + } + else if( tb->heldkey == '\b' && len > 0 ) { + tb->value[--len] = 0; + if( tb->cb ) + tb->cb( tb ); + } + + tb->repeattime = gui_time(); + } +} + +void gui_textbox_handle_keyboard( GUI_TEXTBOX* tb ) { + if( !tb->active ) + return; + + memcpy( tb->prevkeys, tb->keys, 0xff ); + memcpy( tb->keys, input.keys, 0xff ); + + gui_textbox_loop_keys( tb ); + gui_textbox_handle_repeat( tb ); +} + +void gui_textbox_input_fn( void* ptr ) { + GUI_TEXTBOX* tb = (GUI_TEXTBOX*)ptr; + + gui_textbox_handle_mouse( tb ); + gui_textbox_handle_keyboard( tb ); +} + +GUI_TEXTBOX* gui_textbox( I32 x, I32 y, I32 w, I32 h, const char* title, U32 maxlen, U8 allow_newl ) { + GUI_TEXTBOX* tb = new GUI_TEXTBOX; + + tb->x = x; + tb->y = y; + tb->w = w; + tb->h = h; + tb->xbound = tb->w; + tb->ybound = tb->h + TEXTBOX_TITLE_OFFSET; + tb->parent = _gui.cur_view; + tb->draw_fn = gui_textbox_draw_fn; + tb->input_fn = gui_textbox_input_fn; + strcpy( tb->name, title ); + + tb->cb = 0; + tb->active = 0; + tb->m1held = 0; + tb->heldkey = 0; + tb->heldtime = 0; + tb->repeattime = 0; + tb->maxlen = maxlen; + tb->allow_newl = allow_newl; + memset( tb->value, 0, GUI_TEXTBOX_MAX ); + memset( tb->keys, 0, 0xff ); + memset( tb->prevkeys, 0, 0xff ); + + gui_get_view()->children.push( tb ); + + return tb; +} diff --git a/src/gui/vectorinput.cpp b/src/gui/vectorinput.cpp new file mode 100644 index 0000000..9ebb0fa --- /dev/null +++ b/src/gui/vectorinput.cpp @@ -0,0 +1,120 @@ +#include "base.h" + +const I32 VECTORINPUT_TITLE_OFFSET = 15; + +void gui_vectorinput_draw_fn( void* ptr ) { + GUI_VECTORINPUT* input = (GUI_VECTORINPUT*)ptr; + + I32 x = gui_relx( input ); + I32 y = gui_rely( input ); + + gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, input->name ); + + GUI_VIEW* inputview = input->inputview; + inputview->draw_fn( inputview ); +} + +void gui_vectorinput_input_fn( void* ptr ) { + GUI_VECTORINPUT* input = (GUI_VECTORINPUT*)ptr; + + GUI_VIEW* inputview = input->inputview; + inputview->input_fn( inputview ); +} + +void gui_vectorinput_child_cb( void* ptr ) { + GUI_FLOATINPUT* child = (GUI_FLOATINPUT*)ptr; + // slider -> child view -> vectorinput + GUI_VECTORINPUT* parent = (GUI_VECTORINPUT*)child->parent->parent; + if( parent->cb ) + parent->cb( parent ); +} + +void __gui_internal_vectorinput_init( + GUI_VECTORINPUT *input, + I32 x, I32 y, I32 w, + const char *title, + F32 *pval, + U32 valc, + F32 min, + F32 max, + F32 step, + const char *letters, + const char *printfmt +) { + input->x = x; + input->y = y; + input->w = w; + input->h = 20; + strcpy( input->name, title ); + input->xbound = input->w; + input->ybound = input->h + VECTORINPUT_TITLE_OFFSET; + input->draw_fn = gui_vectorinput_draw_fn; + input->input_fn = gui_vectorinput_input_fn; + + GUI_VIEW* parent = gui_get_view(); + parent->children.push( input ); + input->parent = parent; + + gui_set_view( input ); + + input->cb = 0; + input->pval = pval; + input->letters = letters; + input->inputview = gui_view( 0, VECTORINPUT_TITLE_OFFSET, w, input->h ); + input->inputview->parent = input; + I32 wdiv = (input->w - 5 * (valc - 1)) / valc; + for( I32 i = 0; i < valc; ++i ) { + char letter[2] = { input->letters[i], 0 }; + GUI_FLOATINPUT* slider = gui_floatinput( + (wdiv + 5) * i, + 0, + wdiv, + letter, + &pval[i], + min, + max, + step, + printfmt + ); + + slider->cb = gui_vectorinput_child_cb; + } + + gui_set_view( parent ); +} + +GUI_VECTORINPUT* gui_vectorinput( + I32 x, + I32 y, + I32 w, + const char* title, + F32* pval, + U32 valc, + F32 min, + F32 max, + F32 step, + const char* letters, + const char* printfmt +) { + if( !gui_check_target() ) return 0; + if( valc > 25 ) { + dlog( "gui_vectorinput() : have you lost your mind?" ); + return 0; + } + + GUI_VECTORINPUT* input = new GUI_VECTORINPUT; + __gui_internal_vectorinput_init( + input, + x, y, w, + title, + pval, + valc, + min, + max, + step, + letters, + printfmt + ); + + return input; +} diff --git a/src/gui/view.cpp b/src/gui/view.cpp new file mode 100644 index 0000000..da470e7 --- /dev/null +++ b/src/gui/view.cpp @@ -0,0 +1,59 @@ +#include "base.h" + +void gui_view_draw_fn( void* ptr ) { + GUI_VIEW* view = (GUI_VIEW*)ptr; + + I32 x = gui_relx( view ); + I32 y = gui_rely( view ); + + gui_draw_push_clip( x, y, view->w, view->h ); + view->children.each( fn( GUI_BASE** childptr ) { + GUI_BASE* child = *childptr; + if( !child->enabled ) return; + + if( child->draw_fn ) child->draw_fn( child ); + else dlog( "gui_view_draw_fn(): child %p no draw_fn\n", child ); + } ); + gui_draw_pop_clip(); +} + +void gui_view_input_fn( void* ptr ) { + GUI_VIEW* view = (GUI_VIEW*)ptr; + + if( view->initheld ) { + U8 m1 = gui_mbutton_down( 0 ); + if( m1 ) + return; + + view->initheld = 0; + } + + gui_base_input_fn( view ); +} + +GUI_VIEW* gui_view( I32 x, I32 y, I32 w, I32 h ) { + GUI_VIEW* view = new GUI_VIEW; + view->x = x; + view->y = y; + view->xbound = view->w = w; + view->ybound = view->h = h; + strcpy( view->name, "BASE_VIEW" ); + + view->draw_fn = gui_view_draw_fn; + view->input_fn = gui_view_input_fn; + + view->initheld = 1; + + GUI_VIEW* curview = gui_get_view(); + if( !curview ) { + view->parent = gui_get_window(); + gui_get_window()->children.push( view ); + } + else { + view->parent = curview; + curview->children.push( view ); + } + + gui_set_view( view ); + return view; +} diff --git a/src/gui/window.cpp b/src/gui/window.cpp new file mode 100644 index 0000000..0183d8c --- /dev/null +++ b/src/gui/window.cpp @@ -0,0 +1,113 @@ +#include "base.h" + +#include "../render/gl.h" + +void gui_window_draw_fn( void* ptr ) { + GUI_WINDOW* wnd = (GUI_WINDOW*)ptr; + + CLR clr = gui_is_fg_window( wnd )? ui_clr.border : ui_clr.border_inactive; + gui_draw_frect( wnd->x, wnd->y, wnd->w, wnd->h, clr ); + gui_draw_frect( wnd->x + 1, wnd->y + 1, wnd->w - 2, wnd->h - 2, ui_clr.bg ); + + wnd->children.each( fn( GUI_BASE** ptr ) { + GUI_BASE* it = *ptr; + if( it->draw_fn ) it->draw_fn( it ); + else dlog( "window_draw_fn() : child %p no draw_fn", it ); + } ); +} + +GUI_WINDOW* gui_window( I32 x, I32 y, I32 w, I32 h ) { + GUI_WINDOW* wnd = new GUI_WINDOW; + wnd->x = x; + wnd->y = y; + wnd->xbound = wnd->w = w; + wnd->ybound = wnd->h = h; + wnd->draw_fn = gui_window_draw_fn; + strcpy( wnd->name, "BASE_WINDOW" ); + + _gui.windows.push( wnd ); + gui_set_window( wnd ); + gui_set_view( 0 ); + + gui_view( 1, 1, w - 2, h - 2 ); + return wnd; +} + +GUI_WINDOW* gui_window( I32 w, I32 h ) { + return gui_window( 0, 0, w, h ); +} + +void gui_title_draw_fn( void* ptr ) { + GUI_TITLE* t = (GUI_TITLE*)ptr; + + I32 relx = gui_relx( t ); + I32 rely = gui_rely( t ); + gui_draw_str( relx + 5, rely + 3, 0, FNT_JPN12, ui_clr.txt, t->name ); +} + +void gui_title_input_fn( void* ptr ) { + GUI_TITLE* t = (GUI_TITLE*)ptr; + GUI_WINDOW* w = gui_get_parent_wnd( t ); + + if( w->locked ) + return; + + I32 x = gui_relx( t ); + I32 y = gui_rely( t ); + + U8 m1 = gui_mbutton_down( 0 ); + I32 mx, my; + gui_cursor_pos( &mx, &my ); + + if( !m1 ) { + t->held = 0; + return; + } + + if( !t->held && + mx >= x && mx <= x + t->w && + my >= y && my <= y + t->h + ) { + I32 wx = w->x; + I32 wy = w->y; + + I32 moffx = mx - wx; + I32 moffy = my - wy; + + t->xoff = moffx; + t->yoff = moffy; + + t->held = 1; + } + else if( t->held ) { + w->x = mx - t->xoff; + w->y = my - t->yoff; + + if( w->x > _gui.gl2d->gl->canvas_size[0] - 5 ) + w->x = _gui.gl2d->gl->canvas_size[0] - 5; + if( w->x + w->w < 5 ) + w->x = 5 - w->w; + if( w->y > _gui.gl2d->gl->canvas_size[1] - 5 ) + w->y = _gui.gl2d->gl->canvas_size[1] - 5; + if( w->y + w->h < 5 ) + w->y = 5 - w->h; + } +} + +GUI_TITLE* gui_title( const char* title ) { + if( !gui_check_target() ) return 0; + + GUI_TITLE* t = new GUI_TITLE; + t->parent = gui_get_view(); + t->x = 0; + t->y = 0; + t->h = 25; + t->w = t->parent->w; + t->draw_fn = gui_title_draw_fn; + t->input_fn = gui_title_input_fn; + strcpy( t->name, title ); + + gui_get_view()->children.push( t ); + + return t; +} |
