DragFloat() latch value internally, allows finer control, honor precision settings, slow step with integer works #180

This commit is contained in:
ocornut 2015-04-12 19:15:55 +01:00
parent 0dc3d07e7f
commit f5ad362912
2 changed files with 53 additions and 28 deletions

View File

@ -1108,10 +1108,11 @@ struct ImGuiState
ImGuiWindow* FocusedWindow; // Will catch keyboard inputs ImGuiWindow* FocusedWindow; // Will catch keyboard inputs
ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredWindow; // Will catch mouse inputs
ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only)
ImGuiID HoveredId; ImGuiID HoveredId; // Hovered widget
ImGuiID ActiveId; ImGuiID ActiveId; // Active widget
ImGuiID ActiveIdPreviousFrame; ImGuiID ActiveIdPreviousFrame;
bool ActiveIdIsAlive; bool ActiveIdIsAlive;
bool ActiveIdIsJustActivated; // Set when
bool ActiveIdIsFocusedOnly; // Set only by active widget. Denote focus but no active interaction. 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. 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; float SettingsDirtyTimer;
@ -1144,6 +1145,7 @@ struct ImGuiState
ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiStorage ColorEditModeStorage; // for user selection ImGuiStorage ColorEditModeStorage; // for user selection
ImGuiID ActiveComboID; ImGuiID ActiveComboID;
float DragCurrentValue;
ImVec2 DragLastMouseDelta; ImVec2 DragLastMouseDelta;
float DragSpeedScaleSlow; float DragSpeedScaleSlow;
float DragSpeedScaleFast; float DragSpeedScaleFast;
@ -1182,6 +1184,7 @@ struct ImGuiState
ActiveId = 0; ActiveId = 0;
ActiveIdPreviousFrame = 0; ActiveIdPreviousFrame = 0;
ActiveIdIsAlive = false; ActiveIdIsAlive = false;
ActiveIdIsJustActivated = false;
ActiveIdIsFocusedOnly = false; ActiveIdIsFocusedOnly = false;
MovedWindow = NULL; MovedWindow = NULL;
SettingsDirtyTimer = 0.0f; SettingsDirtyTimer = 0.0f;
@ -1199,6 +1202,7 @@ struct ImGuiState
ScalarAsInputTextId = 0; ScalarAsInputTextId = 0;
ActiveComboID = 0; ActiveComboID = 0;
DragCurrentValue = 0.0f;
DragLastMouseDelta = ImVec2(0.0f, 0.0f); DragLastMouseDelta = ImVec2(0.0f, 0.0f);
DragSpeedScaleSlow = 0.01f; DragSpeedScaleSlow = 0.01f;
DragSpeedScaleFast = 10.0f; DragSpeedScaleFast = 10.0f;
@ -1314,6 +1318,7 @@ static void SetActiveId(ImGuiID id)
ImGuiState& g = *GImGui; ImGuiState& g = *GImGui;
g.ActiveId = id; g.ActiveId = id;
g.ActiveIdIsFocusedOnly = false; g.ActiveIdIsFocusedOnly = false;
g.ActiveIdIsJustActivated = true;
} }
static void RegisterAliveId(ImGuiID id) static void RegisterAliveId(ImGuiID id)
@ -1935,6 +1940,7 @@ void ImGui::NewFrame()
SetActiveId(0); SetActiveId(0);
g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdPreviousFrame = g.ActiveId;
g.ActiveIdIsAlive = false; g.ActiveIdIsAlive = false;
g.ActiveIdIsJustActivated = false;
if (!g.ActiveId) if (!g.ActiveId)
g.MovedWindow = NULL; 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 // 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) 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) 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; ImGuiState& g = *GImGui;
@ -5151,14 +5172,7 @@ static bool SliderBehavior(const ImRect& frame_bb, const ImRect& slider_bb, ImGu
} }
// Round past decimal precision // Round past decimal precision
// 0->1, 1->0.1, 2->0.01, etc. new_value = RoundScalar(new_value, decimal_precision);
// 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);
if (*v != new_value) 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. // 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; ImGuiState& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
@ -5481,25 +5495,36 @@ static bool DragScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, flo
{ {
if (g.IO.MouseDown[0]) 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) 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) 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) 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) 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; float new_value = RoundScalar(g.DragCurrentValue, decimal_precision);
if (*v != new_value)
{
*v = new_value;
value_changed = true; value_changed = true;
} }
} }
}
else else
{ {
SetActiveId(0); SetActiveId(0);
@ -5509,7 +5534,7 @@ static bool DragScalarBehavior(const ImRect& frame_bb, ImGuiID id, float* v, flo
return value_changed; 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; ImGuiState& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
@ -5548,7 +5573,6 @@ bool ImGui::DragFloat(const char* label, float *v, float v_step, float v_min, fl
{ {
SetActiveId(id); SetActiveId(id);
FocusWindow(window); FocusWindow(window);
g.DragLastMouseDelta = ImVec2(0.f, 0.f);
if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0]) 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); ItemSize(total_bb, style.FramePadding.y);
// Actual drag behavior // 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. // 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];
@ -5576,12 +5600,13 @@ bool ImGui::DragFloat(const char* label, float *v, float v_step, float v_min, fl
return value_changed; 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) if (!display_format)
display_format = "%.0f"; display_format = "%.0f";
float v_f = (float)*v; 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; *v = (int)v_f;
return value_changed; 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); ImGui::SameLine(0, (int)style.ItemInnerSpacing.x);
if (n + 1 == components) if (n + 1 == components)
ImGui::PushItemWidth(w_item_last); 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();
ImGui::PopItemWidth(); ImGui::PopItemWidth();

View File

@ -316,8 +316,8 @@ namespace ImGui
// Widgets: Drags (tip: ctrl+click on a drag box to input text) // Widgets: Drags (tip: ctrl+click on a drag box to input text)
// ImGui 1.38+ work-in-progress, may change name or API. // 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 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_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 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 // 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); IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);