@ -393,7 +393,6 @@
- input text : add ImGuiInputTextFlags_EnterToApply ? ( off # 218 )
- input text multi - line : way to dynamically grow the buffer without forcing the user to initially allocate for worse case ( follow up on # 200 )
- input text multi - line : line numbers ? status bar ? ( follow up on # 200 )
! - input number : large int not reliably supported because of int < > float conversions .
- input number : optional range min / max for Input * ( ) functions
- input number : holding [ - ] / [ + ] buttons could increase the step speed non - linearly ( or user - controlled )
- input number : use mouse wheel to step up / down
@ -5474,8 +5473,58 @@ ImGuiID ImGui::GetID(const void* ptr_id)
return GImGui - > CurrentWindow - > GetID ( ptr_id ) ;
}
enum ImGuiDataTypeOp
{
ImGuiDataTypeOp_Add ,
ImGuiDataTypeOp_Sub
} ;
static inline void DataTypeFormat ( ImGuiDataType data_type , void * data_ptr , const char * display_format , char * buf , int buf_size )
{
if ( data_type = = ImGuiDataType_Int )
ImFormatString ( buf , buf_size , display_format , * ( int * ) data_ptr ) ;
else if ( data_type = = ImGuiDataType_Float )
ImFormatString ( buf , buf_size , display_format , * ( float * ) data_ptr ) ;
}
static inline void DataTypeFormat ( ImGuiDataType data_type , void * data_ptr , int decimal_precision , char * buf , int buf_size )
{
if ( data_type = = ImGuiDataType_Int )
{
if ( decimal_precision < 0 )
ImFormatString ( buf , buf_size , " %d " , * ( int * ) data_ptr ) ;
else
ImFormatString ( buf , buf_size , " %.*d " , decimal_precision , * ( int * ) data_ptr ) ;
}
else if ( data_type = = ImGuiDataType_Float )
{
if ( decimal_precision < 0 )
ImFormatString ( buf , buf_size , " %f " , * ( float * ) data_ptr ) ; // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
else
ImFormatString ( buf , buf_size , " %.*f " , decimal_precision , * ( float * ) data_ptr ) ;
}
}
static void DataTypeApplyOp ( ImGuiDataType data_type , ImGuiDataTypeOp op , void * value1 , const void * value2 ) // Store into value1
{
if ( data_type = = ImGuiDataType_Int )
{
if ( op = = ImGuiDataTypeOp_Add )
* ( int * ) value1 = * ( int * ) value1 + * ( const int * ) value2 ;
else if ( op = = ImGuiDataTypeOp_Sub )
* ( int * ) value1 = * ( int * ) value1 - * ( const int * ) value2 ;
}
else if ( data_type = = ImGuiDataType_Float )
{
if ( op = = ImGuiDataTypeOp_Add )
* ( float * ) value1 = * ( float * ) value1 + * ( const float * ) value2 ;
else if ( op = = ImGuiDataTypeOp_Sub )
* ( float * ) value1 = * ( float * ) value1 - * ( const float * ) value2 ;
}
}
// User can input math operators (e.g. +100) to edit a numerical values.
static void InputTextApplyArithmeticOp ( const char * buf , const char * initial_value_buf , float * v )
static void DataTypeApplyOpFromText ( const char * buf , const char * initial_value_buf , ImGuiDataType data_type , void * data_ptr , const char * scalar_format )
{
while ( ImCharIsSpace ( * buf ) )
buf + + ;
@ -5496,45 +5545,55 @@ static void InputTextApplyArithmeticOp(const char* buf, const char* initial_valu
if ( ! buf [ 0 ] )
return ;
float ref_v = * v ;
if ( op )
if ( sscanf ( initial_value_buf , " %f " , & ref_v ) < 1 )
if ( data_type = = ImGuiDataType_Int )
{
if ( ! scalar_format )
scalar_format = " %d " ;
int * v = ( int * ) data_ptr ;
int ref_v = * v ;
if ( op & & sscanf ( initial_value_buf , scalar_format , & ref_v ) < 1 )
return ;
// Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
float op_v = 0.0f ;
if ( sscanf ( buf , " %f " , & op_v ) < 1 )
return ;
if ( op = = ' + ' )
* v = ref_v + op_v ; // add (uses "+-" to substract)
else if ( op = = ' * ' )
* v = ref_v * op_v ; // multiply
else if ( op = = ' / ' )
if ( op = = ' + ' ) { if ( sscanf ( buf , " %f " , & op_v ) = = 1 ) * v = ( int ) ( ref_v + op_v ) ; } // Add (use "+-" to subtract)
else if ( op = = ' * ' ) { if ( sscanf ( buf , " %f " , & op_v ) = = 1 ) * v = ( int ) ( ref_v * op_v ) ; } // Multiply
else if ( op = = ' / ' ) { if ( sscanf ( buf , " %f " , & op_v ) = = 1 & & op_v ! = 0.0f ) * v = ( int ) ( ref_v / op_v ) ; } // Divide
else { if ( sscanf ( buf , scalar_format , & ref_v ) = = 1 ) * v = ref_v ; } // Assign constant
}
else if ( data_type = = ImGuiDataType_Float )
{
if ( op_v = = 0.0f ) // divide
if ( ! scalar_format )
scalar_format = " %f " ;
float * v = ( float * ) data_ptr ;
float ref_v = * v ;
if ( op & & sscanf ( initial_value_buf , scalar_format , & ref_v ) < 1 )
return ;
* v = ref_v / op_v ;
float op_v = 0.0f ;
if ( sscanf ( buf , scalar_format , & op_v ) < 1 )
return ;
if ( op = = ' + ' ) { * v = ref_v + op_v ; } // Add (use "+-" to subtract)
else if ( op = = ' * ' ) { * v = ref_v * op_v ; } // Multiply
else if ( op = = ' / ' ) { if ( op_v ! = 0.0f ) * v = ref_v / op_v ; } // Divide
else { * v = op_v ; } // Assign constant
}
else
* v = op_v ; // Constant
}
// Create text input in place of a slider (when CTRL+Clicking on slider)
bool ImGui : : InputFloatReplaceWidget ( const ImRect & aabb , const char * label , float * v , ImGuiID id , int decimal_precision )
bool ImGui : : Input ScalarAsWidgetReplacemen t( const ImRect & aabb , const char * label , ImGuiDataType data_type , void * data_ptr , ImGuiID id , int decimal_precision )
{
ImGuiState & g = * GImGui ;
ImGuiWindow * window = GetCurrentWindow ( ) ;
char text_buf [ 64 ] ;
ImFormatString ( text_buf , IM_ARRAYSIZE ( text_buf ) , " %.*f " , decimal_precision , * v ) ;
// Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
SetActiveID ( g . ScalarAsInputTextId , window ) ;
g . HoveredId = 0 ;
// Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
FocusableItemUnregister ( window ) ;
bool value_changed = InputTextEx ( label , text_buf , ( int ) IM_ARRAYSIZE ( text_buf ) , aabb . GetSize ( ) - g . Style . FramePadding * 2.0f , ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll ) ;
char buf [ 32 ] ;
DataTypeFormat ( data_type , data_ptr , decimal_precision , buf , IM_ARRAYSIZE ( buf ) ) ;
bool value_changed = InputTextEx ( label , buf , IM_ARRAYSIZE ( buf ) , aabb . GetSize ( ) - g . Style . FramePadding * 2.0f , ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll ) ;
if ( g . ScalarAsInputTextId = = 0 )
{
// First frame
@ -5548,7 +5607,7 @@ bool ImGui::InputFloatReplaceWidget(const ImRect& aabb, const char* label, float
g . ScalarAsInputTextId = 0 ;
}
if ( value_changed )
InputTextApplyArithmeticOp( text_buf , g . InputTextState . InitialText . Data , v ) ;
DataTypeApplyOpFromText( buf , GImGui - > InputTextState . InitialText . begin ( ) , data_type , data_ptr , NULL ) ;
return value_changed ;
}
@ -5576,10 +5635,10 @@ inline int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
inline float ImGui : : RoundScalar ( float value , int decimal_precision )
{
// Round past decimal precision
// 0: 1, 1: 0.1, 2: 0.01, etc.
// So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
// FIXME: Investigate better rounding methods
float min_step = 1.0f / powf ( 10.0f , ( float ) decimal_precision ) ;
static const float min_steps [ 10 ] = { 1.0f , 0.1f , 0.01f , 0.001f , 0.0001f , 0.00001f , 0.000001f , 0.0000001f , 0.00000001f , 0.000000001f } ;
float min_step = ( decimal_precision > = 0 & & decimal_precision < 10 ) ? min_steps [ decimal_precision ] : ( 1.0f / powf ( 10.0f , ( float ) decimal_precision ) ) ;
bool negative = value < 0.0f ;
value = fabsf ( value ) ;
float remainder = fmodf ( value , min_step ) ;
@ -5767,7 +5826,7 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
}
}
if ( start_text_input | | ( g . ActiveId = = id & & g . ScalarAsInputTextId = = id ) )
return Input FloatReplaceWidget( frame_bb , label , v , id , decimal_precision ) ;
return Input ScalarAsWidgetReplacement( frame_bb , label , ImGuiDataType_Float , v , id , decimal_precision ) ;
ItemSize ( total_bb , style . FramePadding . y ) ;
@ -6067,11 +6126,10 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
}
}
if ( start_text_input | | ( g . ActiveId = = id & & g . ScalarAsInputTextId = = id ) )
return InputFloatReplaceWidget ( frame_bb , label , v , id , decimal_precision ) ;
ItemSize ( total_bb , style . FramePadding . y ) ;
return InputScalarAsWidgetReplacement ( frame_bb , label , ImGuiDataType_Float , v , id , decimal_precision ) ;
// Actual drag behavior
ItemSize ( total_bb , style . FramePadding . y ) ;
const bool value_changed = DragBehavior ( frame_bb , id , v , v_speed , v_min , v_max , decimal_precision , power ) ;
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
@ -7243,7 +7301,8 @@ bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, co
return ret ;
}
bool ImGui : : InputFloat ( const char * label , float * v , float step , float step_fast , int decimal_precision , ImGuiInputTextFlags extra_flags )
// NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
bool ImGui : : InputScalarEx ( const char * label , ImGuiDataType data_type , void * data_ptr , void * step_ptr , void * step_fast_ptr , const char * scalar_format , ImGuiInputTextFlags extra_flags )
{
ImGuiWindow * window = GetCurrentWindow ( ) ;
if ( window - > SkipItems )
@ -7252,42 +7311,42 @@ bool ImGui::InputFloat(const char* label, float *v, float step, float step_fast,
ImGuiState & g = * GImGui ;
const ImGuiStyle & style = g . Style ;
const float w = ImGui : : CalcItemWidth ( ) ;
const ImVec2 label_size = CalcTextSize( label , NULL , true ) ;
const ImVec2 label_size = ImGui: : CalcTextSize( label , NULL , true ) ;
const ImRect frame_bb ( window - > DC . CursorPos , window - > DC . CursorPos + ImVec2 ( w , label_size . y ) + style . FramePadding * 2.0f ) ;
ImGui : : BeginGroup ( ) ;
ImGui : : PushID ( label ) ;
const ImVec2 button_sz = ImVec2 ( g . FontSize , g . FontSize ) + style . FramePadding * 2 ;
if ( step > 0.0f )
if ( step _ptr )
ImGui : : PushItemWidth ( ImMax ( 1.0f , w - ( button_sz . x + style . ItemInnerSpacing . x ) * 2 ) ) ;
char buf [ 64 ] ;
if ( decimal_precision < 0 )
ImFormatString ( buf , IM_ARRAYSIZE ( buf ) , " %f " , * v ) ; // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
else
ImFormatString ( buf , IM_ARRAYSIZE ( buf ) , " %.*f " , decimal_precision , * v ) ;
DataTypeFormat ( data_type , data_ptr , scalar_format , buf , IM_ARRAYSIZE ( buf ) ) ;
bool value_changed = false ;
const ImGuiInputTextFlags flags = extra_flags | ( ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll ) ;
if ( ImGui : : InputText ( " " , buf , IM_ARRAYSIZE ( buf ) , flags ) )
if ( ! ( extra_flags & ImGuiInputTextFlags_CharsHexadecimal ) )
extra_flags | = ImGuiInputTextFlags_CharsDecimal ;
extra_flags | = ImGuiInputTextFlags_AutoSelectAll ;
if ( ImGui : : InputText ( " " , buf , IM_ARRAYSIZE ( buf ) , extra_flags ) )
{
InputTextApplyArithmeticOp ( buf , g . InputTextState . InitialText . Data , v ) ;
DataTypeApplyOpFromText( buf , GImGui - > InputTextState . InitialText . begin ( ) , data_type , data_ptr , scalar_format ) ;
value_changed = true ;
}
// Step buttons
if ( step > 0.0f )
if ( step _ptr )
{
ImGui : : PopItemWidth ( ) ;
ImGui : : SameLine ( 0 , style . ItemInnerSpacing . x ) ;
if ( ButtonEx ( " - " , button_sz , ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups ) )
{
* v - = g . IO . KeyCtrl & & step_fast > 0.0f ? step_fast : step ;
DataTypeApplyOp ( data_type , ImGuiDataTypeOp_Sub , data_ptr , g . IO . KeyCtrl & & step_fast_ptr ? step_fast_ptr : step_ptr ) ;
value_changed = true ;
}
ImGui : : SameLine ( 0 , style . ItemInnerSpacing . x ) ;
if ( ButtonEx ( " + " , button_sz , ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups ) )
{
* v + = g . IO . KeyCtrl & & step_fast > 0.0f ? step_fast : step ;
DataTypeApplyOp ( data_type , ImGuiDataTypeOp_Add , data_ptr , g . IO . KeyCtrl & & step_fast_ptr ? step_fast_ptr : step_ptr ) ;
value_changed = true ;
}
}
@ -7304,13 +7363,20 @@ bool ImGui::InputFloat(const char* label, float *v, float step, float step_fast,
return value_changed ;
}
bool ImGui : : InputFloat ( const char * label , float * v , float step , float step_fast , int decimal_precision , ImGuiInputTextFlags extra_flags )
{
char display_format [ 16 ] ;
if ( decimal_precision )
strcpy ( display_format , " %f " ) ; // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
else
ImFormatString ( display_format , 16 , " %%%df " , decimal_precision ) ;
return InputScalarEx ( label , ImGuiDataType_Float , ( void * ) v , ( void * ) ( step > 0.0f ? & step : NULL ) , ( void * ) ( step_fast > 0.0f ? & step_fast : NULL ) , display_format , extra_flags ) ;
}
bool ImGui : : InputInt ( const char * label , int * v , int step , int step_fast , ImGuiInputTextFlags extra_flags )
{
float f = ( float ) * v ;
const bool value_changed = ImGui : : InputFloat ( label , & f , ( float ) step , ( float ) step_fast , 0 , extra_flags ) ;
if ( value_changed )
* v = ( int ) f ;
return value_changed ;
const char * scalar_format = ( extra_flags & ImGuiInputTextFlags_CharsHexadecimal ) ? " %08X " : " %d " ;
return InputScalarEx ( label , ImGuiDataType_Int , ( void * ) v , ( void * ) ( step > 0.0f ? & step : NULL ) , ( void * ) ( step_fast > 0.0f ? & step_fast : NULL ) , scalar_format , extra_flags ) ;
}
bool ImGui : : InputFloatN ( const char * label , float * v , int components , int decimal_precision , ImGuiInputTextFlags extra_flags )