|
@@ -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;
|
|
|
|
|
|
- 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 == '/')
|
|
|
+ // 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 (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::InputScalarAsWidgetReplacement(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 InputFloatReplaceWidget(frame_bb, label, v, id, decimal_precision);
|
|
|
+ return InputScalarAsWidgetReplacement(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::InputInt(const char* label, int *v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
|
|
|
+bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, 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;
|
|
|
+ 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)
|
|
|
+{
|
|
|
+ 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)
|