#include "base.h" #include const I32 FLOATINPUT_STEPPER_W = 11; static U8 gui_floatinput_has_stepper( GUI_FLOATINPUT* input ) { return input->w >= 46; } static I32 gui_floatinput_content_w( GUI_FLOATINPUT* input ) { I32 reserve = gui_floatinput_has_stepper( input ) ? ( FLOATINPUT_STEPPER_W + 1 ) : 0; I32 cw = input->w - reserve; return cw < 1 ? 1 : cw; } static void gui_floatinput_clamp_and_snap( GUI_FLOATINPUT* input ) { if( isfinite( input->min ) && *input->pval < input->min ) *input->pval = input->wraparound ? input->max : input->min; if( isfinite( input->max ) && *input->pval > input->max ) *input->pval = input->wraparound ? input->min : input->max; F32 step = input->step; if( !isfinite( step ) || fabsf( step ) < 0.000001f ) return; F32 rmn = remainderf( *input->pval, step ); *input->pval -= rmn; } static void gui_floatinput_draw_stepper( GUI_FLOATINPUT* input ) { if( !gui_floatinput_has_stepper( input ) ) return; I32 x = gui_relx( input ); I32 y = gui_rely( input ); I32 w = input->w; I32 h = input->h; I32 sx = x + w - FLOATINPUT_STEPPER_W; I32 sy = y + 1; I32 sh = h - 2; I32 top_h = sh / 2; I32 bot_h = sh - top_h; CLR border = gui_is_fg_window( input )? ui_clr.border : ui_clr.border_inactive; gui_draw_frect( sx, sy, FLOATINPUT_STEPPER_W, sh, border ); gui_draw_frect( sx + 1, sy + 1, FLOATINPUT_STEPPER_W - 2, sh - 2, ui_clr.bg_alt ); gui_draw_line( sx + 1, sy + top_h, sx + FLOATINPUT_STEPPER_W - 1, sy + top_h, border ); I32 th = 0; gui_draw_get_str_bounds( 0, &th, FNT_JPN12, "+" ); I32 plus_y = sy + ( top_h - th ) / 2; I32 minus_y = sy + top_h + ( bot_h - th ) / 2; I32 cx = sx + FLOATINPUT_STEPPER_W / 2; gui_draw_str( cx, plus_y, ALIGN_C, FNT_JPN12, ui_clr.txt, "+" ); gui_draw_str( cx, minus_y, ALIGN_C, FNT_JPN12, ui_clr.txt, "-" ); } 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 = gui_floatinput_content_w( input ); 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 = gui_floatinput_content_w( input ); F32 val = *input->pval; gui_draw_str( x + 2, y + 2, ALIGN_L, FNT_JPN12, ui_clr.txt, input->name ); if( input->drawval ) gui_draw_str( x + w - 2, y + 2, ALIGN_R, FNT_JPN12, ui_clr.txt, input->valfmt, val ); I32 t1w, t2w = 0, t3w; gui_draw_get_str_bounds( &t1w, 0, FNT_JPN12, input->name ); if( input->drawval ) 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 ); gui_floatinput_draw_stepper( input ); } void gui_floatinput_input_bound( GUI_FLOATINPUT* input ) { if( !gui_mbutton_down( GUI_MBTNLEFT ) ) return; F32 oldval = *input->pval; I32 x = gui_relx( input ); I32 w = gui_floatinput_content_w( input ); 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; if( oldval != *input->pval ) input->lastchange = *input->pval - oldval; } void gui_floatinput_input_unbound( GUI_FLOATINPUT* input ) { if( !gui_mbutton_down( GUI_MBTNLEFT ) ) return; I32 mx, my; gui_cursor_pos( &mx, &my ); F32 oldval = *input->pval; 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; if( oldval != *input->pval ) input->lastchange = *input->pval - oldval; } 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; input->lastchange = *input->pval - oldval; 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 m2 = gui_mbutton_down( 1 ); 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; U8 has_stepper = gui_floatinput_has_stepper( input ); I32 step_x = x + w - FLOATINPUT_STEPPER_W; U8 in_stepper = has_stepper && inbounds && mx >= step_x; if( inbounds ) gui_floatinput_input_scroll( input ); if( in_stepper ) { if( !m1 && !m2 ) { input->held = 0; input->heldoutbounds = 0; return; } if( !input->held && !input->heldoutbounds ) { U8 fine = !!m2; I32 split_y = y + 1 + ( h - 2 ) / 2; U8 inc = my < split_y; F32 step = fine ? 0.1f : 1.f; F32 val = *input->pval; *input->pval += inc ? step : -step; gui_floatinput_clamp_and_snap( input ); input->lastchange = *input->pval - val; if( input->cb ) input->cb( input ); input->held = 1; } return; } 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 ); gui_floatinput_clamp_and_snap( 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->lastchange = 0; input->cb = 0; input->pval = pval; input->min = min; input->max = max; input->step = step; input->valfmt = valfmt; input->drawval = 1; input->wraparound = 0; input->customclr = 0; input->held = 0; GUI_VIEW* parent = gui_get_view(); parent->children.push( input ); input->parent = parent; return input; }