DragFloat, SliderFloat: Rounding scalar using the provided format string instead of parsed precision. (#648)

This commit is contained in:
omar 2018-04-30 16:44:57 +02:00
parent 6881d065b8
commit 92f0165f85
3 changed files with 45 additions and 46 deletions

View File

@ -61,6 +61,7 @@ Other Changes:
This allow using custom formats to display values in scientific notation, and is generally more consistent with other API. Obsoleted functions using the optional "int decimal_precision" parameter. (#648) This allow using custom formats to display values in scientific notation, and is generally more consistent with other API. Obsoleted functions using the optional "int decimal_precision" parameter. (#648)
- DragFloat, DragInt: Cancel mouse tweak when current value is initially past the min/max boundaries and mouse is pushing in the same direction (keyboard/gamepad version already did this). - DragFloat, DragInt: Cancel mouse tweak when current value is initially past the min/max boundaries and mouse is pushing in the same direction (keyboard/gamepad version already did this).
- DragFloat, SliderFloat: Fixes to allow input of scientific notation numbers when using CTRL+Click to input the value. (~#648, #1011) - DragFloat, SliderFloat: Fixes to allow input of scientific notation numbers when using CTRL+Click to input the value. (~#648, #1011)
- DragFloat, SliderFloat: Rounding-on-write uses the provided format string instead of parsing the precision from the string, which allows for finer uses of %e %g etc. (#648, #642)
- Style: Changed default style.DisplaySafeAreaPadding values from (4,4) to (3,3) so it is smaller than FramePadding and has no effect on main menu bar on a computer. (#1439) - Style: Changed default style.DisplaySafeAreaPadding values from (4,4) to (3,3) so it is smaller than FramePadding and has no effect on main menu bar on a computer. (#1439)
- Misc: Added IMGUI_CHECKVERSION() macro to compare version string and data structure sizes in order to catch issues with mismatching compilation unit settings. (#1695, #1769) - Misc: Added IMGUI_CHECKVERSION() macro to compare version string and data structure sizes in order to catch issues with mismatching compilation unit settings. (#1695, #1769)
- Demo: Fixed Overlay: Added a context menu item to enable freely moving the window. - Demo: Fixed Overlay: Added a context menu item to enable freely moving the window.

View File

@ -8586,6 +8586,19 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c
return false; return false;
} }
const char* ImGui::ParseFormatTrimDecorationsLeading(const char* fmt)
{
while (char c = fmt[0])
{
if (c == '%' && fmt[1] != '%')
return fmt;
else if (c == '%')
fmt++;
fmt++;
}
return fmt;
}
// Extract the format out of a format string with leading or trailing decorations // Extract the format out of a format string with leading or trailing decorations
// fmt = "blah blah" -> return fmt // fmt = "blah blah" -> return fmt
// fmt = "%.3f" -> return fmt // fmt = "%.3f" -> return fmt
@ -8594,25 +8607,21 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c
const char* ImGui::ParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size) const char* ImGui::ParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size)
{ {
// We don't use strchr() because our strings are usually very short and often start with '%' // We don't use strchr() because our strings are usually very short and often start with '%'
const char* fmt_start = fmt; const char* fmt_start = ParseFormatTrimDecorationsLeading(fmt);
if (fmt_start[0] != '%')
return fmt;
fmt = fmt_start;
while (char c = *fmt++) while (char c = *fmt++)
{ {
if (c != '%') continue; // Looking for % if (c >= 'A' && c <= 'Z' && (c != 'L')) // L is a type modifier, other letters qualify as types aka end of the format
if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%" break;
fmt_start = fmt - 1; if (c >= 'a' && c <= 'z' && (c != 'h' && c != 'j' && c != 'l' && c != 't' && c != 'w' && c != 'z')) // h/j/l/t/w/z are type modifiers, other letters qualify as types aka end of the format
while ((c = *fmt++) != 0) break;
{
if (c >= 'A' && c <= 'Z' && (c != 'L')) // L is a type modifier, other letters qualify as types aka end of the format
break;
if (c >= 'a' && c <= 'z' && (c != 'h' && c != 'j' && c != 'l' && c != 't' && c != 'w' && c != 'z')) // h/j/l/t/w/z are type modifiers, other letters qualify as types aka end of the format
break;
}
if (fmt[0] == 0) // If we only have leading decoration, we don't need to copy the data.
return fmt_start;
ImStrncpy(buf, fmt_start, ImMin((int)(fmt + 1 - fmt_start), buf_size));
return buf;
} }
return fmt_start; if (fmt[0] == 0) // If we only have leading decoration, we don't need to copy the data.
return fmt_start;
ImStrncpy(buf, fmt_start, ImMin((int)(fmt + 1 - fmt_start), buf_size));
return buf;
} }
// Parse display precision back from the display format string // Parse display precision back from the display format string
@ -8644,22 +8653,11 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision); return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
} }
float ImGui::RoundScalar(float value, int decimal_precision) float ImGui::RoundScalarWithFormat(const char* format, float value)
{ {
// Round past decimal precision char buf[64];
// So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0 ImFormatString(buf, IM_ARRAYSIZE(buf), ParseFormatTrimDecorationsLeading(format), value);
// FIXME: Investigate better rounding methods return (float)atof(buf);
if (decimal_precision < 0)
return value;
const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision);
bool negative = value < 0.0f;
value = fabsf(value);
float remainder = fmodf(value, min_step);
if (remainder <= min_step*0.5f)
value -= remainder;
else
value += (min_step - remainder);
return negative ? -value : value;
} }
static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos) static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
@ -8687,7 +8685,7 @@ static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float
return (v_clamped - v_min) / (v_max - v_min); return (v_clamped - v_min) / (v_max - v_min);
} }
bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags) bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, const char* format, float power, ImGuiSliderFlags flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
@ -8700,11 +8698,12 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v
const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
const bool is_decimal = ParseFormatPrecision(format, 3) > 0;
const float grab_padding = 2.0f; const float grab_padding = 2.0f;
const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f); const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
float grab_sz; float grab_sz;
if (decimal_precision != 0) if (is_decimal)
grab_sz = ImMin(style.GrabMinSize, slider_sz); grab_sz = ImMin(style.GrabMinSize, slider_sz);
else else
grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit
@ -8759,7 +8758,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v
else if (delta != 0.0f) else if (delta != 0.0f)
{ {
clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
if (decimal_precision == 0 && !is_non_linear) if (!is_decimal && !is_non_linear)
{ {
if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_TweakSlow)) if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_TweakSlow))
delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps
@ -8814,7 +8813,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v
} }
// Round past decimal precision // Round past decimal precision
new_value = RoundScalar(new_value, decimal_precision); new_value = RoundScalarWithFormat(format, new_value);
if (*v != new_value) if (*v != new_value)
{ {
*v = new_value; *v = new_value;
@ -8868,7 +8867,6 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
if (!format) if (!format)
format = "%.3f"; format = "%.3f";
int decimal_precision = ParseFormatPrecision(format, 3);
// Tabbing or CTRL-clicking on Slider turns it into an input box // Tabbing or CTRL-clicking on Slider turns it into an input box
bool start_text_input = false; bool start_text_input = false;
@ -8890,7 +8888,7 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
// Actual slider behavior + render grab // Actual slider behavior + render grab
ItemSize(total_bb, style.FramePadding.y); ItemSize(total_bb, style.FramePadding.y);
const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision); const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, format, power);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
char value_buf[64]; char value_buf[64];
@ -8924,7 +8922,6 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float
if (!format) if (!format)
format = "%.3f"; format = "%.3f";
int decimal_precision = ParseFormatPrecision(format, 3);
if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
{ {
@ -8935,7 +8932,7 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float
} }
// Actual slider behavior + render grab // Actual slider behavior + render grab
bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical); bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
// For the vertical slider we allow centered text to overlap the frame padding // For the vertical slider we allow centered text to overlap the frame padding
@ -9061,7 +9058,7 @@ bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const
return SliderIntN(label, v, 4, v_min, v_max, format); return SliderIntN(label, v, 4, v_min, v_max, format);
} }
bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power) bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, const char* format, float power)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
@ -9106,6 +9103,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s
} }
if (g.ActiveIdSource == ImGuiInputSource_Nav) if (g.ActiveIdSource == ImGuiInputSource_Nav)
{ {
int decimal_precision = ParseFormatPrecision(format, 3);
adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x;
v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
} }
@ -9141,7 +9139,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s
// Round to user desired precision, then apply // Round to user desired precision, then apply
bool value_changed = false; bool value_changed = false;
v_cur = RoundScalar(v_cur, decimal_precision); v_cur = RoundScalarWithFormat(format, v_cur);
if (*v != v_cur) if (*v != v_cur)
{ {
*v = v_cur; *v = v_cur;
@ -9177,7 +9175,6 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
if (!format) if (!format)
format = "%.3f"; format = "%.3f";
int decimal_precision = ParseFormatPrecision(format, 3);
// Tabbing or CTRL-clicking on Drag turns it into an input box // Tabbing or CTRL-clicking on Drag turns it into an input box
bool start_text_input = false; bool start_text_input = false;
@ -9199,7 +9196,7 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
// Actual drag behavior // Actual drag behavior
ItemSize(total_bb, style.FramePadding.y); ItemSize(total_bb, style.FramePadding.y);
const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power); const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, format, power);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
char value_buf[64]; char value_buf[64];

View File

@ -1096,11 +1096,11 @@ namespace ImGui
IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0);
IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius);
IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0); IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, const char* format, float power, ImGuiSliderFlags flags = 0);
IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* format, float power); IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* format, float power);
IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* format); IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* format);
IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power); IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, const char* format, float power);
IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* format, float power); IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* format, float power);
IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* format); IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* format);
@ -1119,9 +1119,10 @@ namespace ImGui
IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size);
IMGUI_API const char* ParseFormatTrimDecorationsLeading(const char* format);
IMGUI_API const char* ParseFormatTrimDecorations(const char* format, char* buf, int buf_size); IMGUI_API const char* ParseFormatTrimDecorations(const char* format, char* buf, int buf_size);
IMGUI_API int ParseFormatPrecision(const char* format, int default_value); IMGUI_API int ParseFormatPrecision(const char* format, int default_value);
IMGUI_API float RoundScalar(float value, int decimal_precision); IMGUI_API float RoundScalarWithFormat(const char* format, float value);
// Shade functions (write over already created vertices) // Shade functions (write over already created vertices)
IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);