Skip to content

Commit d08ba62

Browse files
committed
SliderRange2: Address maintainer feedback
- Single format string API: removed format_max parameter, format can be "%.3f" (same for both) or "%.3f...%.3f" (different for min/max) - Accept any number of dots (.., ..., ....) as separator when parsing - Accept " - " as separator in addition to dots - Ctrl+Click shows "min...max" format for editing both values - Unknown state (-2) when handles overlap: drag direction determines which handle to use - Display uses " - " separator by default (customizable via format)
1 parent 5743047 commit d08ba62

2 files changed

Lines changed: 121 additions & 36 deletions

File tree

imgui.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -704,9 +704,9 @@ namespace ImGui
704704
IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
705705
IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
706706
IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
707-
IMGUI_API bool 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 = NULL, const char* format_max = NULL, ImGuiSliderFlags flags = 0, const void* p_step = NULL);
708-
IMGUI_API bool SliderFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_min = 0.0f, float v_max = 1.0f, const char* format = "%.3f", const char* format_max = NULL, ImGuiSliderFlags flags = 0, float step = 0.0f);
709-
IMGUI_API bool SliderIntRange2(const char* label, int* v_current_min, int* v_current_max, int v_min = 0, int v_max = 100, const char* format = "%d", const char* format_max = NULL, ImGuiSliderFlags flags = 0, int step = 0);
707+
IMGUI_API bool 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 = NULL, ImGuiSliderFlags flags = 0, const void* p_step = NULL);
708+
IMGUI_API bool SliderFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_min = 0.0f, float v_max = 1.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0, float step = 0.0f);
709+
IMGUI_API bool SliderIntRange2(const char* label, int* v_current_min, int* v_current_max, int v_min = 0, int v_max = 100, const char* format = "%d", ImGuiSliderFlags flags = 0, int step = 0);
710710
IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
711711
IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
712712
IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);

imgui_widgets.cpp

Lines changed: 118 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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
34873487
static 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+
34893511
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
34903512
static 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

39193973
bool 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

Comments
 (0)