From 1247ce251fbb9317ac4d97993a319743af533447 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Apr 2015 14:07:52 +0100 Subject: [PATCH] Added DragFloat(), DragInt() work in progress #180 --- imgui.cpp | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++---- imgui.h | 12 +++- 2 files changed, 161 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1c4c65bc..09032680 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -135,6 +135,7 @@ Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. + - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. - 2015/03/17 (1.36) - renamed GetItemRectMin()/GetItemRectMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function (will obsolete). - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function (will obsolete). @@ -559,6 +560,8 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f); Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f); Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input + Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f); + Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f); Colors[ImGuiCol_TitleBg] = ImVec4(0.50f, 0.50f, 1.00f, 0.45f); Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.40f, 0.40f, 0.80f, 0.15f); @@ -566,8 +569,6 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f); Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f); - Colors[ImGuiCol_CheckHovered] = ImVec4(0.60f, 0.40f, 0.40f, 0.45f); - Colors[ImGuiCol_CheckActive] = ImVec4(0.65f, 0.50f, 0.50f, 0.55f); Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); @@ -649,9 +650,11 @@ void ImGuiIO::AddInputCharacter(ImWchar c) const float PI = 3.14159265358979323846f; #ifdef INT_MAX -#define IM_INT_MAX INT_MAX +#define IM_INT_MIN INT_MIN +#define IM_INT_MAX INT_MAX #else -#define IM_INT_MAX 2147483647 +#define IM_INT_MIN (-2147483647-1) +#define IM_INT_MAX (2147483647) #endif // Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n. @@ -1138,6 +1141,9 @@ struct ImGuiState ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiStorage ColorEditModeStorage; // for user selection ImGuiID ActiveComboID; + ImVec2 DragLastMouseDelta; + float DragSpeedScaleSlow; + float DragSpeedScaleFast; float ScrollbarClickDeltaToGrabCenter; // distance between mouse and center of grab box, normalized in parent space char Tooltip[1024]; char* PrivateClipboard; // if no custom clipboard handler is defined @@ -1189,6 +1195,9 @@ struct ImGuiState ScalarAsInputTextId = 0; ActiveComboID = 0; + DragLastMouseDelta = ImVec2(0.0f, 0.0f); + DragSpeedScaleSlow = 0.01f; + DragSpeedScaleFast = 10.0f; ScrollbarClickDeltaToGrabCenter = 0.0f; memset(Tooltip, 0, sizeof(Tooltip)); PrivateClipboard = NULL; @@ -3716,6 +3725,8 @@ const char* ImGui::GetStyleColName(ImGuiCol idx) case ImGuiCol_Border: return "Border"; case ImGuiCol_BorderShadow: return "BorderShadow"; case ImGuiCol_FrameBg: return "FrameBg"; + case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; + case ImGuiCol_FrameBgActive: return "FrameBgActive"; case ImGuiCol_TitleBg: return "TitleBg"; case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; @@ -3723,8 +3734,6 @@ const char* ImGui::GetStyleColName(ImGuiCol idx) case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; case ImGuiCol_ComboBg: return "ComboBg"; - case ImGuiCol_CheckHovered: return "CheckHovered"; - case ImGuiCol_CheckActive: return "CheckActive"; case ImGuiCol_CheckMark: return "CheckMark"; case ImGuiCol_SliderGrab: return "SliderGrab"; case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; @@ -5207,11 +5216,11 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c int decimal_precision = 3; ParseFormat(display_format, decimal_precision); - const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id); const bool is_finite = (v_min != -FLT_MAX && v_min != FLT_MAX && v_max != -FLT_MAX && v_max != FLT_MAX); - // Tabbing or CTRL-clicking through slider turns into an input box + // Tabbing or CTRL-clicking on Slider turns it into an input box bool start_text_input = false; + const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id); if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) { SetActiveId(id); @@ -5427,6 +5436,138 @@ bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const return SliderIntN(label, v, 4, v_min, v_max, display_format); } +// 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) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + const ImGuiStyle& style = g.Style; + + // Draw frame + const ImU32 frame_col = window->Color(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + + bool value_changed = false; + + // Process clicking on the slider + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + const ImVec2 mouse_drag_delta = ImGui::GetMouseDragDelta(0); + + if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f) + { + float step = v_step; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + step = v_step * g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + step = v_step * g.DragSpeedScaleSlow; + + *v += (mouse_drag_delta.x - g.DragLastMouseDelta.x) * step; + *v = ImClamp(*v, v_min, v_max); + + g.DragLastMouseDelta.x = mouse_drag_delta.x; + value_changed = true; + } + } + else + { + SetActiveId(0); + } + } + + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float *v, float v_step, float v_min, float v_max, const char* display_format) +{ + ImGuiState& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = ImGui::CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y) + style.FramePadding*2.0f); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, &id)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + g.HoveredId = id; + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = 3; + ParseFormat(display_format, decimal_precision); + + // Tabbing or CTRL-clicking on Drag turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = window->FocusItemRegister(g.ActiveId == id); + if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) + { + SetActiveId(id); + FocusWindow(window); + g.DragLastMouseDelta = ImVec2(0.f, 0.f); + + const bool is_ctrl_down = g.IO.KeyCtrl; + if (tab_focus_requested || is_ctrl_down) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return SliderFloatAsInputText(label, v, id, decimal_precision); + + ItemSize(total_bb, style.FramePadding.y); + + // Actual drag behavior + const bool value_changed = DragScalarBehavior(frame_bb, id, v, v_step, v_min, v_max); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + const ImVec2 value_text_size = CalcTextSize(value_buf, value_buf_end, true); + RenderTextClipped(ImVec2(ImMax(frame_bb.Min.x + style.FramePadding.x, inner_bb.GetCenter().x - value_text_size.x*0.5f), frame_bb.Min.y + style.FramePadding.y), value_buf, value_buf_end, &value_text_size, frame_bb.Max); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float* v, float v_step, const char* display_format) +{ + return ImGui::DragFloat(label, v, v_step, -FLT_MAX, FLT_MAX, display_format); +} + +bool ImGui::DragInt(const char* label, int* v, int v_step, 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); + *v = (int)v_f; + return value_changed; +} + +bool ImGui::DragInt(const char* label, int* v, int v_step, const char* display_format) +{ + return ImGui::DragInt(label, v, v_step, IM_INT_MIN, IM_INT_MAX, display_format); +} + enum ImGuiPlotType { ImGuiPlotType_Lines, @@ -5598,7 +5739,7 @@ bool ImGui::Checkbox(const char* label, bool* v) if (pressed) *v = !(*v); - RenderFrame(check_bb.Min, check_bb.Max, window->Color((held && hovered) ? ImGuiCol_CheckActive : hovered ? ImGuiCol_CheckHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + RenderFrame(check_bb.Min, check_bb.Max, window->Color((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); if (*v) { const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); @@ -5659,7 +5800,7 @@ bool ImGui::RadioButton(const char* label, bool active) bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held, true); - window->DrawList->AddCircleFilled(center, radius, window->Color((held && hovered) ? ImGuiCol_CheckActive : hovered ? ImGuiCol_CheckHovered : ImGuiCol_FrameBg), 16); + window->DrawList->AddCircleFilled(center, radius, window->Color((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); if (active) { const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); @@ -6866,7 +7007,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::SliderInt(ids[n], &i[n], 0, 255, fmt[n]); + value_changed |= ImGui::DragInt(ids[n], &i[n], 1, 0, 255, fmt[n]); } ImGui::PopItemWidth(); ImGui::PopItemWidth(); diff --git a/imgui.h b/imgui.h index fbd0a092..f0220421 100644 --- a/imgui.h +++ b/imgui.h @@ -300,6 +300,12 @@ namespace ImGui IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), size_t stride = sizeof(float)); IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); + // Widgets: Drags (tip: ctrl+click on a drag box to input text) + IMGUI_API bool DragFloat(const char* label, float* v, float v_step = 1.0f, float v_min = -FLT_MAX, float v_max = FLT_MAX, const char* display_format = "%.3f"); + IMGUI_API bool DragFloat(const char* label, float* v, float v_step, const char* display_format); + IMGUI_API bool DragInt(const char* label, int* v, int v_step = 1, int v_min = -0x7fffffff-1, int v_max = 0x7fffffff, const char* display_format = "%.0f"); + IMGUI_API bool DragInt(const char* label, int* v, int v_step, const char* display_format = "%.0f"); + // Widgets: Sliders (tip: ctrl+click on a slider to input text) IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); @@ -484,6 +490,8 @@ enum ImGuiCol_ ImGuiCol_Border, ImGuiCol_BorderShadow, ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input + ImGuiCol_FrameBgHovered, + ImGuiCol_FrameBgActive, ImGuiCol_TitleBg, ImGuiCol_TitleBgCollapsed, ImGuiCol_ScrollbarBg, @@ -491,8 +499,6 @@ enum ImGuiCol_ ImGuiCol_ScrollbarGrabHovered, ImGuiCol_ScrollbarGrabActive, ImGuiCol_ComboBg, - ImGuiCol_CheckHovered, - ImGuiCol_CheckActive, ImGuiCol_CheckMark, ImGuiCol_SliderGrab, ImGuiCol_SliderGrabActive, @@ -553,7 +559,7 @@ enum ImGuiMouseCursor_ ImGuiMouseCursor_TextInput, ImGuiMouseCursor_Move, // Unused by ImGui ImGuiMouseCursor_ResizeNS, // Unused by ImGui - ImGuiMouseCursor_ResizeEW, // Unused by ImGui + ImGuiMouseCursor_ResizeEW, ImGuiMouseCursor_ResizeNESW, // Unused by ImGui ImGuiMouseCursor_ResizeNWSE, ImGuiMouseCursor_Count_