From f5ad3629124d717cf5ba5251a71dc686a6156fb2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 12 Apr 2015 19:15:55 +0100 Subject: [PATCH] DragFloat() latch value internally, allows finer control, honor precision settings, slow step with integer works #180 --- imgui.cpp | 77 ++++++++++++++++++++++++++++++++++++------------------- imgui.h | 4 +-- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8f584ba1f..33f78cd00 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1108,10 +1108,11 @@ struct ImGuiState ImGuiWindow* FocusedWindow; // Will catch keyboard inputs ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) - ImGuiID HoveredId; - ImGuiID ActiveId; + ImGuiID HoveredId; // Hovered widget + ImGuiID ActiveId; // Active widget ImGuiID ActiveIdPreviousFrame; bool ActiveIdIsAlive; + bool ActiveIdIsJustActivated; // Set when bool ActiveIdIsFocusedOnly; // Set only by active widget. Denote focus but no active interaction. ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. Only valid if ActiveID is the "#MOVE" identifier of a window. float SettingsDirtyTimer; @@ -1144,6 +1145,7 @@ struct ImGuiState ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiStorage ColorEditModeStorage; // for user selection ImGuiID ActiveComboID; + float DragCurrentValue; ImVec2 DragLastMouseDelta; float DragSpeedScaleSlow; float DragSpeedScaleFast; @@ -1182,6 +1184,7 @@ struct ImGuiState ActiveId = 0; ActiveIdPreviousFrame = 0; ActiveIdIsAlive = false; + ActiveIdIsJustActivated = false; ActiveIdIsFocusedOnly = false; MovedWindow = NULL; SettingsDirtyTimer = 0.0f; @@ -1199,6 +1202,7 @@ struct ImGuiState ScalarAsInputTextId = 0; ActiveComboID = 0; + DragCurrentValue = 0.0f; DragLastMouseDelta = ImVec2(0.0f, 0.0f); DragSpeedScaleSlow = 0.01f; DragSpeedScaleFast = 10.0f; @@ -1314,6 +1318,7 @@ static void SetActiveId(ImGuiID id) ImGuiState& g = *GImGui; g.ActiveId = id; g.ActiveIdIsFocusedOnly = false; + g.ActiveIdIsJustActivated = true; } static void RegisterAliveId(ImGuiID id) @@ -1935,6 +1940,7 @@ void ImGui::NewFrame() SetActiveId(0); g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = false; + g.ActiveIdIsJustActivated = false; if (!g.ActiveId) g.MovedWindow = NULL; @@ -5055,7 +5061,7 @@ static bool SliderFloatAsInputText(const char* label, float* v, ImGuiID id, int } // Parse display precision back from the display format string -static void ParseFormat(const char* fmt, int& decimal_precision) +static inline void ParseFormat(const char* fmt, int& decimal_precision) { while ((fmt = strchr(fmt, '%')) != NULL) { @@ -5073,6 +5079,21 @@ static void ParseFormat(const char* fmt, int& decimal_precision) } } +static inline float 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 + const float min_step = 1.0f / powf(10.0f, (float)decimal_precision); + const float remainder = fmodf(value, min_step); + if (remainder <= min_step*0.5f) + value -= remainder; + else + value += (min_step - remainder); + return value; +} + static bool SliderBehavior(const ImRect& frame_bb, const ImRect& slider_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, bool horizontal) { ImGuiState& g = *GImGui; @@ -5151,14 +5172,7 @@ static bool SliderBehavior(const ImRect& frame_bb, const ImRect& slider_bb, ImGu } // 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 - const float min_step = 1.0f / powf(10.0f, (float)decimal_precision); - const float remainder = fmodf(new_value, min_step); - if (remainder <= min_step*0.5f) - new_value -= remainder; - else - new_value += (min_step - remainder); + new_value = RoundScalar(new_value, decimal_precision); if (*v != new_value) { @@ -5464,7 +5478,7 @@ bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const } // FIXME-WIP: Work in progress. May change API / behavior. -static bool DragScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_step, float v_min, float v_max) +static bool DragScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -5481,23 +5495,34 @@ static bool DragScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, flo { if (g.IO.MouseDown[0]) { - const ImVec2 mouse_drag_delta = ImGui::GetMouseDragDelta(0); + if (g.ActiveIdIsJustActivated) + { + // Lock current value on click + g.DragCurrentValue = *v; + g.DragLastMouseDelta = ImVec2(0.f, 0.f); + } + const ImVec2 mouse_drag_delta = ImGui::GetMouseDragDelta(0, 1.0f); if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f) { - float step = v_step; + float step = v_speed; if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) - step = v_step * g.DragSpeedScaleFast; + step = v_speed * g.DragSpeedScaleFast; if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) - step = v_step * g.DragSpeedScaleSlow; + step = v_speed * g.DragSpeedScaleSlow; - *v += (mouse_drag_delta.x - g.DragLastMouseDelta.x) * step; + g.DragCurrentValue += (mouse_drag_delta.x - g.DragLastMouseDelta.x) * step; + g.DragLastMouseDelta.x = mouse_drag_delta.x; if (v_min < v_max) - *v = ImClamp(*v, v_min, v_max); + g.DragCurrentValue = ImClamp(g.DragCurrentValue, v_min, v_max); - g.DragLastMouseDelta.x = mouse_drag_delta.x; - value_changed = true; + float new_value = RoundScalar(g.DragCurrentValue, decimal_precision); + if (*v != new_value) + { + *v = new_value; + value_changed = true; + } } } else @@ -5509,7 +5534,7 @@ static bool DragScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, flo return value_changed; } -bool ImGui::DragFloat(const char* label, float *v, float v_step, float v_min, float v_max, const char* display_format) +bool ImGui::DragFloat(const char* label, float *v, float v_speed, float v_min, float v_max, const char* display_format) { ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -5548,7 +5573,6 @@ bool ImGui::DragFloat(const char* label, float *v, float v_step, float v_min, fl { SetActiveId(id); FocusWindow(window); - g.DragLastMouseDelta = ImVec2(0.f, 0.f); if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0]) { @@ -5562,7 +5586,7 @@ bool ImGui::DragFloat(const char* label, float *v, float v_step, float v_min, fl ItemSize(total_bb, style.FramePadding.y); // Actual drag behavior - const bool value_changed = DragScalarBehavior(frame_bb, id, v, v_step, v_min, v_max); + const bool value_changed = DragScalarBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision); // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; @@ -5576,12 +5600,13 @@ bool ImGui::DragFloat(const char* label, float *v, float v_step, float v_min, fl return value_changed; } -bool ImGui::DragInt(const char* label, int* v, int v_step, int v_min, int v_max, const char* display_format) +// NB: v_speed is float to allow adjusting the drag speed with more precision +bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format) { if (!display_format) display_format = "%.0f"; float v_f = (float)*v; - bool value_changed = ImGui::DragFloat(label, &v_f, (float)v_step, (float)v_min, (float)v_max, display_format); + bool value_changed = ImGui::DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format); *v = (int)v_f; return value_changed; } @@ -7029,7 +7054,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) ImGui::SameLine(0, (int)style.ItemInnerSpacing.x); if (n + 1 == components) ImGui::PushItemWidth(w_item_last); - value_changed |= ImGui::DragInt(ids[n], &i[n], 1, 0, 255, fmt[n]); + value_changed |= ImGui::DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]); } ImGui::PopItemWidth(); ImGui::PopItemWidth(); diff --git a/imgui.h b/imgui.h index 9a01fe858..70b98cb2f 100644 --- a/imgui.h +++ b/imgui.h @@ -316,8 +316,8 @@ namespace ImGui // Widgets: Drags (tip: ctrl+click on a drag box to input text) // ImGui 1.38+ work-in-progress, may change name or API. - IMGUI_API bool DragFloat(const char* label, float* v, float v_step = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f"); // If v_max >= v_max we have no bound - IMGUI_API bool DragInt(const char* label, int* v, float v_step = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_max >= v_max we have no bound + IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f"); // If v_max >= v_max we have no bound + IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_max >= v_max we have no bound // Widgets: Input IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);