summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
authornavewindre <boneyaard@gmail.com>2025-09-03 20:10:09 +0200
committernavewindre <boneyaard@gmail.com>2025-09-03 20:10:09 +0200
commitf8b92ce3aa08b1445c9f956d8166830946562d12 (patch)
tree94e63a5aec9f8f52b577f56799e0c9201fd976a5 /src/gui
a
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/base.cpp405
-rw-r--r--src/gui/base.h314
-rw-r--r--src/gui/button.cpp66
-rw-r--r--src/gui/checkbox.cpp78
-rw-r--r--src/gui/colorinput.cpp59
-rw-r--r--src/gui/console.h0
-rw-r--r--src/gui/floatinput.cpp257
-rw-r--r--src/gui/label.cpp40
-rw-r--r--src/gui/list.cpp98
-rw-r--r--src/gui/textbox.cpp246
-rw-r--r--src/gui/vectorinput.cpp120
-rw-r--r--src/gui/view.cpp59
-rw-r--r--src/gui/window.cpp113
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;
+}