diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 801b483d..266c7ff5 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -126,6 +126,8 @@ Other Changes: - Drag and Drop: Increased payload type string to 32 characters instead of 8. (#143) - Drag and Drop: TreeNode as drop target displays rectangle over full frame. (#1597, #143) - DragFloat: Fix/workaround for backends which do not preserve a valid mouse position when dragged out of bounds. (#1559) +- InputFloat: Allow inputing value using scientific notation e.g. "1e+10". +- InputDouble: Added InputDouble() function. We use a format string instead of a decimal_precision parameter to also for "%e" and variants. (#1011) - Slider, Combo: Use ImGuiCol_FrameBgHovered color when hovered. (#1456) [@stfx] - Combo: BeginCombo(): Added ImGuiComboFlags_NoArrowButton to disable the arrow button and only display the wide value preview box. - Combo: BeginCombo(): Added ImGuiComboFlags_NoPreview to disable the preview and only display a square arrow button. diff --git a/imgui.cpp b/imgui.cpp index 03cf76b1..a16cd5c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -725,7 +725,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size); static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size); -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2); +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format); namespace ImGui @@ -8380,6 +8380,8 @@ static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, ImFormatString(buf, buf_size, display_format, *(int*)data_ptr); else if (data_type == ImGuiDataType_Float) ImFormatString(buf, buf_size, display_format, *(float*)data_ptr); + else if (data_type == ImGuiDataType_Double) + ImFormatString(buf, buf_size, display_format, *(double*)data_ptr); } static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size) @@ -8398,27 +8400,45 @@ static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, else ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr); } + else if (data_type == ImGuiDataType_Double) + { + if (decimal_precision < 0) + ImFormatString(buf, buf_size, "%f", *(double*)data_ptr); + else + ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(double*)data_ptr); + } } -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1 +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) { + IM_ASSERT(op == '+' || op == '-'); if (data_type == ImGuiDataType_Int) { - if (op == '+') - *(int*)value1 = *(int*)value1 + *(const int*)value2; - else if (op == '-') - *(int*)value1 = *(int*)value1 - *(const int*)value2; + if (op == '+') *(int*)output = *(int*)arg1 + *(const int*)arg2; + else if (op == '-') *(int*)output = *(int*)arg1 - *(const int*)arg2; } else if (data_type == ImGuiDataType_Float) { - if (op == '+') - *(float*)value1 = *(float*)value1 + *(const float*)value2; - else if (op == '-') - *(float*)value1 = *(float*)value1 - *(const float*)value2; + if (op == '+') *(float*)output = *(float*)arg1 + *(const float*)arg2; + else if (op == '-') *(float*)output = *(float*)arg1 - *(const float*)arg2; + } + else if (data_type == ImGuiDataType_Double) + { + if (op == '+') *(double*)output = *(double*)arg1 + *(const double*)arg2; + else if (op == '-') *(double*)output = *(double*)arg1 - *(const double*)arg2; } } +static size_t GDataTypeSize[ImGuiDataType_COUNT] = +{ + sizeof(int), + sizeof(float), + sizeof(double), + sizeof(float) * 2, +}; + // User can input math operators (e.g. +100) to edit a numerical values. +// NB: This is _not_ a full expression evaluator. We should probably add one though.. static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format) { while (ImCharIsSpace(*buf)) @@ -8440,45 +8460,56 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b if (!buf[0]) return false; + IM_ASSERT(data_type < ImGuiDataType_COUNT); + int data_backup[2]; + IM_ASSERT(GDataTypeSize[data_type] <= sizeof(data_backup)); + memcpy(data_backup, data_ptr, GDataTypeSize[data_type]); + if (data_type == ImGuiDataType_Int) { if (!scalar_format) scalar_format = "%d"; int* v = (int*)data_ptr; - const int old_v = *v; int arg0i = *v; if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1) return false; - // 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 arg1f = 0.0f; if (op == '+') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f); } // Add (use "+-" to subtract) else if (op == '*') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f); } // Multiply else if (op == '/') { if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }// Divide - else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign constant (read as integer so big values are not lossy) - return (old_v != *v); + else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign integer constant } else if (data_type == ImGuiDataType_Float) { // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in scalar_format = "%f"; float* v = (float*)data_ptr; - const float old_v = *v; - float arg0f = *v; + float arg0f = *v, arg1f = 0.0f; if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1) return false; - - float arg1f = 0.0f; if (sscanf(buf, scalar_format, &arg1f) < 1) return false; if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) else if (op == '*') { *v = arg0f * arg1f; } // Multiply else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide else { *v = arg1f; } // Assign constant - return (old_v != *v); } - - return false; + else if (data_type == ImGuiDataType_Double) + { + scalar_format = "%lf"; + double* v = (double*)data_ptr; + double arg0f = *v, arg1f = 0.0f; + if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1) + return false; + if (sscanf(buf, scalar_format, &arg1f) < 1) + return false; + if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) + else if (op == '*') { *v = arg0f * arg1f; } // Multiply + else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide + else { *v = arg1f; } // Assign constant + } + return memcmp(data_backup, data_ptr, GDataTypeSize[data_type]) != 0; } // Create text input in place of a slider (when CTRL+Clicking on slider) @@ -10463,13 +10494,13 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) { - DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); + DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); value_changed = true; } SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) { - DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); + DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); value_changed = true; } } @@ -10488,13 +10519,24 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data 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 < 0) - 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, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision); extra_flags |= ImGuiInputTextFlags_CharsScientific; - 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); + if (decimal_precision < 0) + { + // 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 + return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), "%f", extra_flags); + } + else + { + char display_format[16]; + ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%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::InputDouble(const char* label, double* v, double step, double step_fast, const char* display_format, ImGuiInputTextFlags extra_flags) +{ + extra_flags |= ImGuiInputTextFlags_CharsScientific; + return InputScalarEx(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), display_format, extra_flags); } bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) diff --git a/imgui.h b/imgui.h index a07e6344..a79705bd 100644 --- a/imgui.h +++ b/imgui.h @@ -357,6 +357,7 @@ namespace ImGui IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0f, double step_fast = 0.0f, const char* display_format = "%.6f", ImGuiInputTextFlags extra_flags = 0); // Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds) IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for logarithmic sliders diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9be5fef0..b3d42391 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -335,8 +335,8 @@ void ImGui::ShowDemoWindow(bool* p_open) { static char str0[128] = "Hello, world!"; - static int i0=123; - static float f0=0.001f; + static int i0 = 123; + static float f0 = 0.001f; ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); ImGui::SameLine(); ShowHelpMarker("Hold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n"); @@ -345,12 +345,17 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); + // NB: You can use the %e notation as well. + static double d0 = 999999.000001; + ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.6f"); + ImGui::SameLine(); ShowHelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); + static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; ImGui::InputFloat3("input float3", vec4a); } { - static int i1=50, i2=42; + static int i1 = 50, i2 = 42; ImGui::DragInt("drag int", &i1, 1); ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); diff --git a/imgui_internal.h b/imgui_internal.h index 8114faae..479f25be 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -248,7 +248,9 @@ enum ImGuiDataType { ImGuiDataType_Int, ImGuiDataType_Float, - ImGuiDataType_Float2 + ImGuiDataType_Double, + ImGuiDataType_Float2, + ImGuiDataType_COUNT }; enum ImGuiInputSource @@ -258,7 +260,7 @@ enum ImGuiInputSource ImGuiInputSource_Nav, ImGuiInputSource_NavKeyboard, // Only used occasionally for storage, not tested/handled by most code ImGuiInputSource_NavGamepad, // " - ImGuiInputSource_COUNT, + ImGuiInputSource_COUNT }; // FIXME-NAV: Clarify/expose various repeat delay/rate