@@ -3486,6 +3486,28 @@ bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const
34863486// Forward declaration for TempInput function used by SliderScalarRange2
34873487static bool TempInputScalarRange2(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data_min, void* p_data_max, const char* format, const char* format_max, const void* p_clamp_min, const void* p_clamp_max, int selected_handle);
34883488
3489+ // Helper to parse range format string "%.3f...%.3f" into separate min/max formats
3490+ // Returns pointer to start of max format (after dots), or NULL if no separator found
3491+ // Accepts any number of consecutive dots (2+) as separator
3492+ static const char* ParseRangeFormatSeparator(const char* format)
3493+ {
3494+ if (!format)
3495+ return NULL;
3496+ const char* p = format;
3497+ while (*p)
3498+ {
3499+ if (p[0] == '.' && p[1] == '.')
3500+ {
3501+ // Found at least "..", skip all consecutive dots
3502+ while (*p == '.')
3503+ p++;
3504+ return p; // Return start of max format
3505+ }
3506+ p++;
3507+ }
3508+ return NULL; // No separator found
3509+ }
3510+
34893511template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
34903512static bool SliderBehaviorRangeT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v_min_val, TYPE* v_max_val, TYPE v_min, TYPE v_max, const char* format, const char* format_max, ImGuiSliderFlags flags, ImRect* out_grab_bb_min, ImRect* out_grab_bb_max, int* p_handle, TYPE step)
34913513{
@@ -3554,8 +3576,11 @@ static bool SliderBehaviorRangeT(const ImRect& bb, ImGuiID id, ImGuiDataType dat
35543576
35553577 if (dist_min <= handle_threshold || dist_max <= handle_threshold)
35563578 {
3557- // Click near a handle
3558- *p_handle = (dist_min <= dist_max) ? 0 : 1;
3579+ // Click near a handle - if handles overlap, use undetermined state (-2)
3580+ if (ImAbs(grab_min_pos - grab_max_pos) < 1.0f)
3581+ *p_handle = -2; // Undetermined: will be resolved by drag direction
3582+ else
3583+ *p_handle = (dist_min <= dist_max) ? 0 : 1;
35593584 }
35603585 else if (click_pos > grab_min_pos && click_pos < grab_max_pos)
35613586 {
@@ -3613,6 +3638,19 @@ static bool SliderBehaviorRangeT(const ImRect& bb, ImGuiID id, ImGuiDataType dat
36133638 grab_max_pos = grab_pos_from_value(*v_max_val);
36143639 }
36153640 }
3641+ else if (*p_handle == -2)
3642+ {
3643+ // Undetermined state (handles overlapping): resolve based on drag direction
3644+ float delta = mouse_abs_pos - g.IO.MouseClickedPos[0][axis];
3645+ if (ImAbs(delta) > 1.0f)
3646+ {
3647+ // Resolved: positive delta = max handle, negative = min handle (for X axis)
3648+ *p_handle = (delta > 0.0f) ? 1 : 0;
3649+ if (axis == ImGuiAxis_Y)
3650+ *p_handle = 1 - *p_handle; // Invert for Y axis
3651+ }
3652+ // Don't set value until resolved
3653+ }
36163654 else
36173655 {
36183656 // Single handle dragging
@@ -3753,7 +3791,7 @@ static bool SliderBehaviorRange(const ImRect& bb, ImGuiID id, ImGuiDataType data
37533791 }
37543792}
37553793
3756- bool ImGui::SliderScalarRange2(const char* label, ImGuiDataType data_type, void* p_v_min, void* p_v_max, const void* p_min, const void* p_max, const char* format, const char* format_max, ImGuiSliderFlags flags, const void* p_step)
3794+ bool ImGui::SliderScalarRange2(const char* label, ImGuiDataType data_type, void* p_v_min, void* p_v_max, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, const void* p_step)
37573795{
37583796 ImGuiWindow* window = GetCurrentWindow();
37593797 if (window->SkipItems)
@@ -3773,11 +3811,27 @@ bool ImGui::SliderScalarRange2(const char* label, ImGuiDataType data_type, void*
37733811 if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
37743812 return false;
37753813
3776- // Default format
3814+ // Default format and parse "min...max" format string
37773815 if (format == NULL)
37783816 format = DataTypeGetInfo(data_type)->PrintFmt;
3779- if (format_max == NULL)
3780- format_max = format;
3817+
3818+ // Parse format string: "%.3f...%.3f" -> format_min="%.3f", format_max="%.3f"
3819+ char format_min_buf[64];
3820+ const char* format_min = format;
3821+ const char* format_max = format; // Default: same format for both
3822+ const char* separator = ParseRangeFormatSeparator(format);
3823+ if (separator)
3824+ {
3825+ // Copy min format (everything before the dots)
3826+ const char* dots = format;
3827+ while (dots < separator && !(dots[0] == '.' && dots[1] == '.'))
3828+ dots++;
3829+ size_t min_len = ImMin((size_t)(dots - format), sizeof(format_min_buf) - 1);
3830+ memcpy(format_min_buf, format, min_len);
3831+ format_min_buf[min_len] = '\0';
3832+ format_min = format_min_buf;
3833+ format_max = separator;
3834+ }
37813835
37823836 const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
37833837
@@ -3848,8 +3902,8 @@ bool ImGui::SliderScalarRange2(const char* label, ImGuiDataType data_type, void*
38483902
38493903 if (temp_input_is_active)
38503904 {
3851- // Ctrl+Click text input: shows selected handle value , user can type "XXX...YYYY" to set both
3852- return TempInputScalarRange2(frame_bb, id, label, data_type, p_v_min, p_v_max, format , format_max,
3905+ // Ctrl+Click text input: shows "min...max" format , user can type to set values
3906+ return TempInputScalarRange2(frame_bb, id, label, data_type, p_v_min, p_v_max, format_min , format_max,
38533907 (flags & ImGuiSliderFlags_AlwaysClamp) ? p_min : NULL,
38543908 (flags & ImGuiSliderFlags_AlwaysClamp) ? p_max : NULL,
38553909 *p_handle);
@@ -3862,7 +3916,7 @@ bool ImGui::SliderScalarRange2(const char* label, ImGuiDataType data_type, void*
38623916 // Slider behavior
38633917 ImRect grab_bb_min, grab_bb_max;
38643918 int handle_int = *p_handle;
3865- const bool value_changed = SliderBehaviorRange(frame_bb, id, data_type, p_v_min, p_v_max, p_min, p_max, format , format_max, flags, &grab_bb_min, &grab_bb_max, &handle_int, p_step);
3919+ const bool value_changed = SliderBehaviorRange(frame_bb, id, data_type, p_v_min, p_v_max, p_min, p_max, format_min , format_max, flags, &grab_bb_min, &grab_bb_max, &handle_int, p_step);
38663920 *p_handle = (ImS8)handle_int;
38673921 if (value_changed)
38683922 MarkItemEdited(id);
@@ -3894,7 +3948,7 @@ bool ImGui::SliderScalarRange2(const char* label, ImGuiDataType data_type, void*
38943948
38953949 // Display value
38963950 char value_buf[128];
3897- const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_v_min, format );
3951+ const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_v_min, format_min );
38983952 value_buf_end += ImFormatString((char*)value_buf_end, (size_t)(value_buf + IM_ARRAYSIZE(value_buf) - value_buf_end), " - ");
38993953 value_buf_end = value_buf_end + DataTypeFormatString((char*)value_buf_end, (size_t)(value_buf + IM_ARRAYSIZE(value_buf) - value_buf_end), data_type, p_v_max, format_max);
39003954 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
@@ -3906,14 +3960,14 @@ bool ImGui::SliderScalarRange2(const char* label, ImGuiDataType data_type, void*
39063960 return value_changed;
39073961}
39083962
3909- bool ImGui::SliderFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags, float step)
3963+ bool ImGui::SliderFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_min, float v_max, const char* format, ImGuiSliderFlags flags, float step)
39103964{
3911- return SliderScalarRange2(label, ImGuiDataType_Float, v_current_min, v_current_max, &v_min, &v_max, format, format_max, flags, step > 0.0f ? &step : NULL);
3965+ return SliderScalarRange2(label, ImGuiDataType_Float, v_current_min, v_current_max, &v_min, &v_max, format, flags, step > 0.0f ? &step : NULL);
39123966}
39133967
3914- bool ImGui::SliderIntRange2(const char* label, int* v_current_min, int* v_current_max, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags, int step)
3968+ bool ImGui::SliderIntRange2(const char* label, int* v_current_min, int* v_current_max, int v_min, int v_max, const char* format, ImGuiSliderFlags flags, int step)
39153969{
3916- return SliderScalarRange2(label, ImGuiDataType_S32, v_current_min, v_current_max, &v_min, &v_max, format, format_max, flags, step > 0 ? &step : NULL);
3970+ return SliderScalarRange2(label, ImGuiDataType_S32, v_current_min, v_current_max, &v_min, &v_max, format, flags, step > 0 ? &step : NULL);
39173971}
39183972
39193973bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
@@ -4219,10 +4273,11 @@ static bool TempInputScalarRange2(const ImRect& bb, ImGuiID id, const char* labe
42194273 if (format_max[0] == 0)
42204274 format_max = format;
42214275
4222- // Format initial value: just the selected handle's value (user can type "..." to edit both)
4223- void* p_selected = (selected_handle == 0) ? p_data_min : p_data_max;
4224- const char* selected_format = (selected_handle == 0) ? format : format_max;
4225- ImGui::DataTypeFormatString(data_buf, IM_COUNTOF(data_buf), data_type, p_selected, selected_format);
4276+ // Format initial value: show "min...max" format by default
4277+ char* p = data_buf;
4278+ p += ImGui::DataTypeFormatString(p, IM_COUNTOF(data_buf), data_type, p_data_min, format);
4279+ p += ImFormatString(p, (size_t)(data_buf + IM_COUNTOF(data_buf) - p), "...");
4280+ p += ImGui::DataTypeFormatString(p, (size_t)(data_buf + IM_COUNTOF(data_buf) - p), data_type, p_data_max, format_max);
42264281 ImStrTrimBlanks(data_buf);
42274282
42284283 ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
@@ -4236,11 +4291,30 @@ static bool TempInputScalarRange2(const ImRect& bb, ImGuiID id, const char* labe
42364291 memcpy(&data_backup_min, p_data_min, data_type_size);
42374292 memcpy(&data_backup_max, p_data_max, data_type_size);
42384293
4239- // Look for "..." separator - if present, update both values
4240- const char* separator = strstr(data_buf, "...");
4294+ // Look for separator: ".." (any number of dots) or " - "
4295+ const char* separator = NULL;
4296+ const char* separator_end = NULL;
4297+ for (const char* s = data_buf; *s; s++)
4298+ {
4299+ if (s[0] == '.' && s[1] == '.')
4300+ {
4301+ separator = s;
4302+ separator_end = s;
4303+ while (*separator_end == '.')
4304+ separator_end++;
4305+ break;
4306+ }
4307+ if (s[0] == ' ' && s[1] == '-' && s[2] == ' ')
4308+ {
4309+ separator = s;
4310+ separator_end = s + 3;
4311+ break;
4312+ }
4313+ }
4314+
42414315 if (separator)
42424316 {
4243- // Parse min value (before "..." )
4317+ // Parse min value (before separator )
42444318 char min_buf[32];
42454319 size_t min_len = (size_t)(separator - data_buf);
42464320 if (min_len >= IM_COUNTOF(min_buf))
@@ -4249,10 +4323,9 @@ static bool TempInputScalarRange2(const ImRect& bb, ImGuiID id, const char* labe
42494323 min_buf[min_len] = 0;
42504324 ImStrTrimBlanks(min_buf);
42514325
4252- // Parse max value (after "...")
4253- const char* max_str = separator + 3;
4326+ // Parse max value (after separator)
42544327 char max_buf[32];
4255- ImStrncpy(max_buf, max_str , IM_COUNTOF(max_buf));
4328+ ImStrncpy(max_buf, separator_end , IM_COUNTOF(max_buf));
42564329 ImStrTrimBlanks(max_buf);
42574330
42584331 // Apply values
@@ -4267,16 +4340,28 @@ static bool TempInputScalarRange2(const ImRect& bb, ImGuiID id, const char* labe
42674340 }
42684341 else
42694342 {
4270- // No separator - update only the selected handle
4271- ImGui::DataTypeApplyFromText(data_buf, data_type, p_selected, selected_format, NULL);
4272-
4273- // Ensure min <= max after update
4274- if (ImGui::DataTypeCompare(data_type, p_data_min, p_data_max) > 0)
4343+ // No separator - update selected handle only, or both if bar was selected
4344+ if (selected_handle == -1)
42754345 {
4276- if (selected_handle == 0)
4277- memcpy(p_data_min, p_data_max, data_type_size); // Clamp min to max
4278- else
4279- memcpy(p_data_max, p_data_min, data_type_size); // Clamp max to min
4346+ // Bar selected: set both to same value
4347+ ImGui::DataTypeApplyFromText(data_buf, data_type, p_data_min, format, NULL);
4348+ memcpy(p_data_max, p_data_min, data_type_size);
4349+ }
4350+ else
4351+ {
4352+ // Handle selected: update only that handle
4353+ void* p_target = (selected_handle == 0) ? p_data_min : p_data_max;
4354+ const char* target_format = (selected_handle == 0) ? format : format_max;
4355+ ImGui::DataTypeApplyFromText(data_buf, data_type, p_target, target_format, NULL);
4356+
4357+ // Ensure min <= max after update
4358+ if (ImGui::DataTypeCompare(data_type, p_data_min, p_data_max) > 0)
4359+ {
4360+ if (selected_handle == 0)
4361+ memcpy(p_data_min, p_data_max, data_type_size); // Clamp min to max
4362+ else
4363+ memcpy(p_data_max, p_data_min, data_type_size); // Clamp max to min
4364+ }
42804365 }
42814366 }
42824367
0 commit comments