From 98b4b69386de0b4e5e56cb2dea31ac1bc203e13b Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Fri, 15 Mar 2019 00:55:53 +0100 Subject: [PATCH] Update imgui to 1.69. --- imgui/imgui.cpp | 449 ++++++---- imgui/imgui.h | 124 +-- imgui/imgui_demo.cpp | 481 ++++++----- imgui/imgui_draw.cpp | 27 +- imgui/imgui_internal.h | 169 ++-- imgui/imgui_widgets.cpp | 1207 +++++++++++++++++---------- profiler/src/imgui_freetype.h | 2 +- profiler/src/imgui_impl_glfw.cpp | 4 +- profiler/src/imgui_impl_opengl3.cpp | 76 +- profiler/src/imgui_impl_opengl3.h | 25 +- 10 files changed, 1594 insertions(+), 970 deletions(-) diff --git a/imgui/imgui.cpp b/imgui/imgui.cpp index da0b068d..a1020234 100644 --- a/imgui/imgui.cpp +++ b/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.68 +// dear imgui, v1.69 // (main code and documentation) // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. @@ -58,9 +58,9 @@ CODE // [SECTION] FORWARD DECLARATIONS // [SECTION] CONTEXT AND MEMORY ALLOCATORS // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) -// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions) -// [SECTION] MISC HELPER/UTILITIES (ImText* functions) -// [SECTION] MISC HELPER/UTILITIES (Color functions) +// [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions) +// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) +// [SECTION] MISC HELPERS/UTILITIES (Color functions) // [SECTION] ImGuiStorage // [SECTION] ImGuiTextFilter // [SECTION] ImGuiTextBuffer @@ -364,6 +364,8 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). + - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value! - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! @@ -769,7 +771,7 @@ CODE Q: How can I use my own math types instead of ImVec2/ImVec4? A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions. - This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2. + This way you'll be able to use your own types everywhere, e.g. passing glm::vec2 to ImGui functions instead of ImVec2. Q: How can I load a different font than the default? A: Use the font atlas to load the TTF/OTF file you want: @@ -873,7 +875,8 @@ CODE A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags. (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse) Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. - - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows. + - You can call ImGui::GetBackgroundDrawList() or ImGui::GetForegroundDrawList() and use those draw list to display + contents behind or over every other imgui windows. - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data. @@ -955,7 +958,7 @@ CODE #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // #pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic. #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' @@ -1022,7 +1025,7 @@ static void NavUpdateWindowingList(); static void NavUpdateMoveResult(); static float NavUpdatePageUpPageDown(int allowed_dir_flags); static inline void NavUpdateAnyRequestFlag(); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); +static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); @@ -1226,7 +1229,7 @@ void ImGuiIO::ClearInputCharacters() } //----------------------------------------------------------------------------- -// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions) +// [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions) //----------------------------------------------------------------------------- ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) @@ -1479,27 +1482,27 @@ ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed) // - If we reach ### in the string we discard the hash so far and reset to the seed. // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. -ImU32 ImHashStr(const char* data, size_t data_size, ImU32 seed) +ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed) { seed = ~seed; ImU32 crc = seed; - const unsigned char* src = (const unsigned char*)data; + const unsigned char* data = (const unsigned char*)data_p; const ImU32* crc32_lut = GCrc32LookupTable; if (data_size != 0) { while (data_size-- != 0) { - unsigned char c = *src++; - if (c == '#' && src[0] == '#' && src[1] == '#') + unsigned char c = *data++; + if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') crc = seed; crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; } } else { - while (unsigned char c = *src++) + while (unsigned char c = *data++) { - if (c == '#' && src[0] == '#' && src[1] == '#') + if (c == '#' && data[0] == '#' && data[1] == '#') crc = seed; crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; } @@ -1749,7 +1752,7 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e } //----------------------------------------------------------------------------- -// [SECTION] MISC HELPER/UTILTIES (Color functions) +// [SECTION] MISC HELPERS/UTILTIES (Color functions) // Note: The Convert functions are early design which are not consistent with other API. //----------------------------------------------------------------------------- @@ -2549,10 +2552,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) NavLastIds[0] = NavLastIds[1] = 0; NavRectRel[0] = NavRectRel[1] = ImRect(); NavLastChildNavWindow = NULL; - - FocusIdxAllCounter = FocusIdxTabCounter = -1; - FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; - FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; } ImGuiWindow::~ImGuiWindow() @@ -2892,26 +2891,39 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged return false; } -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop) +// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. +bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { ImGuiContext& g = *GImGui; + // Increment counters const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->FocusIdxAllCounter++; + window->DC.FocusCounterAll++; if (is_tab_stop) - window->FocusIdxTabCounter++; + window->DC.FocusCounterTab++; - // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. - // Note that we can always TAB out of a widget that doesn't allow tabbing in. - if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - - if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) - return true; - if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. + // (Note that we can always TAB out of a widget that doesn't allow tabbing in) + if (g.ActiveId == id && g.FocusTabPressed && !(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_KeyTab_)) && g.FocusRequestNextWindow == NULL) { - g.NavJustTabbedId = id; - return true; + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. + } + + // Handle focus requests + if (g.FocusRequestCurrWindow == window) + { + if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll) + return true; + if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab) + { + g.NavJustTabbedId = id; + return true; + } + + // If another item is about to be focused, we clear our own active id + if (g.ActiveId == id) + ClearActiveID(); } return false; @@ -2919,8 +2931,8 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop void ImGui::FocusableItemUnregister(ImGuiWindow* window) { - window->FocusIdxAllCounter--; - window->FocusIdxTabCounter--; + window->DC.FocusCounterAll--; + window->DC.FocusCounterTab--; } ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) @@ -3066,15 +3078,20 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -static ImDrawList* GetOverlayDrawList(ImGuiWindow*) +ImDrawList* ImGui::GetBackgroundDrawList() { - // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches. - return &GImGui->OverlayDrawList; + return &GImGui->BackgroundDrawList; } -ImDrawList* ImGui::GetOverlayDrawList() +static ImDrawList* GetForegroundDrawList(ImGuiWindow*) { - return &GImGui->OverlayDrawList; + // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + return &GImGui->ForegroundDrawList; +} + +ImDrawList* ImGui::GetForegroundDrawList() +{ + return &GImGui->ForegroundDrawList; } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -3367,12 +3384,13 @@ void ImGui::NewFrame() // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) IM_ASSERT(g.Initialized); IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); + IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!"); - IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); + IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); for (int n = 0; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); @@ -3420,10 +3438,15 @@ void ImGui::NewFrame() g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - g.OverlayDrawList.Clear(); - g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); - g.OverlayDrawList.PushClipRectFullScreen(); - g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); + g.BackgroundDrawList.Clear(); + g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.BackgroundDrawList.PushClipRectFullScreen(); + g.BackgroundDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); + + g.ForegroundDrawList.Clear(); + g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.ForegroundDrawList.PushClipRectFullScreen(); + g.ForegroundDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); // Mark rendering data as invalid to prevent user who may have a handle on it to use it. g.DrawData.Clear(); @@ -3499,13 +3522,34 @@ void ImGui::NewFrame() UpdateMouseWheel(); // Pressing TAB activate widget focus - if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); + if (g.ActiveId == 0 && g.FocusTabPressed) { + // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also + // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. + g.FocusRequestNextWindow = g.NavWindow; + g.FocusRequestNextCounterAll = INT_MAX; if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); + g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); else - g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; + g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0; } + + // Turn queued focus request into current one + g.FocusRequestCurrWindow = NULL; + g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX; + if (g.FocusRequestNextWindow != NULL) + { + ImGuiWindow* window = g.FocusRequestNextWindow; + g.FocusRequestCurrWindow = window; + if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1) + g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1); + if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1) + g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1); + g.FocusRequestNextWindow = NULL; + g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX; + } + g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible @@ -3601,11 +3645,10 @@ void ImGui::Shutdown(ImGuiContext* context) g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); g.DrawDataBuilder.ClearFreeMemory(); - g.OverlayDrawList.ClearFreeMemory(); + g.BackgroundDrawList.ClearFreeMemory(); + g.ForegroundDrawList.ClearFreeMemory(); g.PrivateClipboard.clear(); - g.InputTextState.TextW.clear(); - g.InputTextState.InitialText.clear(); - g.InputTextState.TempBuffer.clear(); + g.InputTextState.ClearFreeMemory(); for (int i = 0; i < g.SettingsWindows.Size; i++) IM_DELETE(g.SettingsWindows[i].Name); @@ -3617,7 +3660,7 @@ void ImGui::Shutdown(ImGuiContext* context) fclose(g.LogFile); g.LogFile = NULL; } - g.LogClipboard.clear(); + g.LogBuffer.clear(); g.Initialized = false; } @@ -3860,6 +3903,9 @@ void ImGui::Render() // Gather ImDrawList to render (for each active window) g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0; g.DrawDataBuilder.Clear(); + if (!g.BackgroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList); + ImGuiWindow* windows_to_render_front_most[2]; windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL; @@ -3876,10 +3922,10 @@ void ImGui::Render() // Draw software mouse cursor if requested if (g.IO.MouseDrawCursor) - RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor); + RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor); - if (!g.OverlayDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); + if (!g.ForegroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList); // Setup ImDrawData structure for end-user SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); @@ -4724,7 +4770,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); bool hovered, held; ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); - //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); + //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -4749,7 +4795,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); + //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; @@ -5150,12 +5196,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - // Prepare for item focus requests - window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); - window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); - window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; - window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; - // Apply scrolling window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); @@ -5265,9 +5305,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Scrollbars if (window->ScrollbarX) - Scrollbar(ImGuiLayoutType_Horizontal); + Scrollbar(ImGuiAxis_X); if (window->ScrollbarY) - Scrollbar(ImGuiLayoutType_Vertical); + Scrollbar(ImGuiAxis_Y); // Render resize grips (after their input handling so we don't have a frame of latency) if (!(flags & ImGuiWindowFlags_NoResize)) @@ -5327,10 +5367,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; window->DC.NavLayerActiveMaskNext = 0x00; window->DC.MenuBarAppending = false; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; + window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1; window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -6443,9 +6483,11 @@ void ImGui::ActivateItem(ImGuiID id) void ImGui::SetKeyboardFocusHere(int offset) { IM_ASSERT(offset >= -1); // -1 is allowed but not below - ImGuiWindow* window = GetCurrentWindow(); - window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; - window->FocusIdxTabRequestNext = INT_MAX; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset; + g.FocusRequestNextCounterTab = INT_MAX; } void ImGui::SetItemDefaultFocus() @@ -6528,13 +6570,13 @@ ImGuiID ImGui::GetID(const void* ptr_id) bool ImGui::IsRectVisible(const ImVec2& size) { - ImGuiWindow* window = GImGui->CurrentWindow;; + ImGuiWindow* window = GImGui->CurrentWindow; return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); } bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) { - ImGuiWindow* window = GImGui->CurrentWindow;; + ImGuiWindow* window = GImGui->CurrentWindow; return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); } @@ -6552,7 +6594,6 @@ void ImGui::BeginGroup() group_data.BackupGroupOffset = window->DC.GroupOffset; group_data.BackupCurrentLineSize = window->DC.CurrentLineSize; group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; - group_data.BackupLogLinePosY = window->DC.LogLinePosY; group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; group_data.AdvanceCursor = true; @@ -6561,14 +6602,15 @@ void ImGui::BeginGroup() window->DC.Indent = window->DC.GroupOffset; window->DC.CursorMaxPos = window->DC.CursorPos; window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f); - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return + if (g.LogEnabled) + g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return } void ImGui::EndGroup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls + IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls ImGuiGroupData& group_data = window->DC.GroupStack.back(); @@ -6581,7 +6623,8 @@ void ImGui::EndGroup() window->DC.GroupOffset = group_data.BackupGroupOffset; window->DC.CurrentLineSize = group_data.BackupCurrentLineSize; window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return + if (g.LogEnabled) + g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return if (group_data.AdvanceCursor) { @@ -7022,8 +7065,8 @@ ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*) ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) { ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); - //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); - //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); + //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); + //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); // Combo Box policy (we want a connecting edge) if (policy == ImGuiPopupPositionPolicy_ComboBox) @@ -7115,11 +7158,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) return window->Pos; } -//----------------------------------------------------------------------------- -// [SECTION] VIEWPORTS, PLATFORM WINDOWS -//----------------------------------------------------------------------------- - -// (this section is filled in the 'viewport' and 'docking' branches) //----------------------------------------------------------------------------- // [SECTION] KEYBOARD/GAMEPAD NAVIGATION @@ -7222,7 +7260,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); + ImDrawList* draw_list = ImGui::GetForegroundDrawList(window); draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); @@ -7234,7 +7272,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); + ImDrawList* draw_list = ImGui::GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); } @@ -7315,7 +7353,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // Process Move Request (scoring for navigation) // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav)) + if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav))) { ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; #if IMGUI_DEBUG_NAV_SCORING @@ -7353,7 +7391,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. g.NavLayer = window->DC.NavLayerCurrent; g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; + g.NavIdTabCounter = window->DC.FocusCounterTab; window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) } } @@ -7540,7 +7578,7 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) { ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1)); - //GetOverlayDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] + //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] if (window_rect.Contains(item_rect)) return; @@ -7593,9 +7631,13 @@ static void ImGui::NavUpdate() NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); - if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; + NAV_MAP_KEY(ImGuiKey_Tab, ImGuiNavInput_KeyTab_ ); + if (g.IO.KeyCtrl) + g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; + if (g.IO.KeyShift) + g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; + if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. + g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; #undef NAV_MAP_KEY } memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); @@ -7827,11 +7869,15 @@ static void ImGui::NavUpdate() g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] + //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] g.NavScoringCount = 0; #if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + if (g.NavWindow) + { + ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); + if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + } #endif } @@ -7970,7 +8016,9 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) g.NavWindowingToggleLayer = false; } -// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) +// Windowing management mode +// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) +// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; @@ -8073,6 +8121,7 @@ static void ImGui::NavUpdateWindowing() g.NavDisableMouseHover = true; apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window); + ClearActiveID(); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); @@ -8156,10 +8205,18 @@ void ImGui::NextColumn() return; ImGuiContext& g = *GImGui; + ImGuiColumnsSet* columns = window->DC.ColumnsSet; + + if (columns->Count == 1) + { + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + IM_ASSERT(columns->Current == 0); + return; + } + PopItemWidth(); PopClipRect(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); if (++columns->Current < columns->Count) { @@ -8169,6 +8226,7 @@ void ImGui::NextColumn() } else { + // New line window->DC.ColumnsOffset.x = 0.0f; window->DrawList->ChannelsSetCurrent(0); columns->Current = 0; @@ -8323,7 +8381,7 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(columns_count > 1); + IM_ASSERT(columns_count >= 1); IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. @@ -8377,8 +8435,11 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag column->ClipRect.ClipWith(window->ClipRect); } - window->DrawList->ChannelsSplit(columns->Count); - PushColumnClipRect(); + if (columns->Count > 1) + { + window->DrawList->ChannelsSplit(columns->Count); + PushColumnClipRect(); + } PushItemWidth(GetColumnWidth() * 0.65f); } @@ -8390,8 +8451,11 @@ void ImGui::EndColumns() IM_ASSERT(columns != NULL); PopItemWidth(); - PopClipRect(); - window->DrawList->ChannelsMerge(); + if (columns->Count > 1) + { + PopClipRect(); + window->DrawList->ChannelsMerge(); + } columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); window->DC.CursorPos.y = columns->LineMaxY; @@ -8760,11 +8824,6 @@ void ImGui::EndDragDropTarget() g.DragDropWithinSourceOrTarget = false; } -//----------------------------------------------------------------------------- -// [SECTION] DOCKING -//----------------------------------------------------------------------------- - -// (this section is filled in the 'docking' branch) //----------------------------------------------------------------------------- // [SECTION] LOGGING/CAPTURING @@ -8785,7 +8844,7 @@ void ImGui::LogText(const char* fmt, ...) if (g.LogFile) vfprintf(g.LogFile, fmt, args); else - g.LogClipboard.appendfv(fmt, args); + g.LogBuffer.appendfv(fmt, args); va_end(args); } @@ -8799,17 +8858,20 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* if (!text_end) text_end = FindRenderedTextEnd(text, text_end); - const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1); + const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); if (ref_pos) - window->DC.LogLinePosY = ref_pos->y; + g.LogLinePosY = ref_pos->y; + if (log_new_line) + g.LogLineFirstItem = true; const char* text_remaining = text; - if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth - g.LogStartDepth = window->DC.TreeDepth; - const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); + if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogDepthRef = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); for (;;) { // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. + // We don't add a trailing \n to allow a subsequent item on the same line to be captured. const char* line_start = text_remaining; const char* line_end = ImStreolRange(line_start, text_end); const bool is_first_line = (line_start == text); @@ -8818,9 +8880,18 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* { const int char_count = (int)(line_end - line_start); if (log_new_line || !is_first_line) - LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start); - else + LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); + else if (g.LogLineFirstItem) + LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); + else LogText(" %.*s", char_count, line_start); + g.LogLineFirstItem = false; + } + else if (log_new_line) + { + // An empty "" string at a different Y position should output a carriage return. + LogText(IM_NEWLINE); + break; } if (is_last_line) @@ -8829,64 +8900,71 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* } } -// Start logging ImGui output to TTY -void ImGui::LogToTTY(int max_depth) +// Start logging/capturing text output +void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) { ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; ImGuiWindow* window = g.CurrentWindow; - + IM_ASSERT(g.LogEnabled == false); IM_ASSERT(g.LogFile == NULL); - g.LogFile = stdout; + IM_ASSERT(g.LogBuffer.empty()); g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; + g.LogType = type; + g.LogDepthRef = window->DC.TreeDepth; + g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); + g.LogLinePosY = FLT_MAX; + g.LogLineFirstItem = true; } -// Start logging ImGui output to given file -void ImGui::LogToFile(int max_depth, const char* filename) +void ImGui::LogToTTY(int auto_open_depth) { ImGuiContext& g = *GImGui; if (g.LogEnabled) return; - ImGuiWindow* window = g.CurrentWindow; + LogBegin(ImGuiLogType_TTY, auto_open_depth); + g.LogFile = stdout; +} +// Start logging/capturing text output to given file +void ImGui::LogToFile(int auto_open_depth, const char* filename) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + + // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still + // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. + // By opening the file in binary mode "ab" we have consistent output everywhere. if (!filename) - { filename = g.IO.LogFilename; - if (!filename) - return; - } - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = ImFileOpen(filename, "ab"); - if (!g.LogFile) + if (!filename || !filename[0]) + return; + FILE* f = ImFileOpen(filename, "ab"); + if (f == NULL) { IM_ASSERT(0); return; } - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; + + LogBegin(ImGuiLogType_File, auto_open_depth); + g.LogFile = f; } -// Start logging ImGui output to clipboard -void ImGui::LogToClipboard(int max_depth) +// Start logging/capturing text output to clipboard +void ImGui::LogToClipboard(int auto_open_depth) { ImGuiContext& g = *GImGui; if (g.LogEnabled) return; - ImGuiWindow* window = g.CurrentWindow; + LogBegin(ImGuiLogType_Clipboard, auto_open_depth); +} - IM_ASSERT(g.LogFile == NULL); - g.LogFile = NULL; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; +void ImGui::LogToBuffer(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_Buffer, auto_open_depth); } void ImGui::LogFinish() @@ -8896,23 +8974,33 @@ void ImGui::LogFinish() return; LogText(IM_NEWLINE); - if (g.LogFile != NULL) + switch (g.LogType) { - if (g.LogFile == stdout) - fflush(g.LogFile); - else - fclose(g.LogFile); - g.LogFile = NULL; - } - if (g.LogClipboard.size() > 1) - { - SetClipboardText(g.LogClipboard.begin()); - g.LogClipboard.clear(); + case ImGuiLogType_TTY: + fflush(g.LogFile); + break; + case ImGuiLogType_File: + fclose(g.LogFile); + break; + case ImGuiLogType_Buffer: + break; + case ImGuiLogType_Clipboard: + if (!g.LogBuffer.empty()) + SetClipboardText(g.LogBuffer.begin()); + break; + case ImGuiLogType_None: + IM_ASSERT(0); + break; } + g.LogEnabled = false; + g.LogType = ImGuiLogType_None; + g.LogFile = NULL; + g.LogBuffer.clear(); } // Helper to display logging buttons +// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) void ImGui::LogButtons() { ImGuiContext& g = *GImGui; @@ -8923,18 +9011,18 @@ void ImGui::LogButtons() const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); PushItemWidth(80.0f); PushAllowKeyboardFocus(false); - SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); + SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); PopAllowKeyboardFocus(); PopItemWidth(); PopID(); // Start logging at the end of the function so that the buttons don't appear in the log if (log_to_tty) - LogToTTY(g.LogAutoExpandMaxDepth); + LogToTTY(); if (log_to_file) - LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); + LogToFile(); if (log_to_clipboard) - LogToClipboard(g.LogAutoExpandMaxDepth); + LogToClipboard(); } //----------------------------------------------------------------------------- @@ -9156,6 +9244,21 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting } } + +//----------------------------------------------------------------------------- +// [SECTION] VIEWPORTS, PLATFORM WINDOWS +//----------------------------------------------------------------------------- + +// (this section is filled in the 'docking' branch) + + +//----------------------------------------------------------------------------- +// [SECTION] DOCKING +//----------------------------------------------------------------------------- + +// (this section is filled in the 'docking' branch) + + //----------------------------------------------------------------------------- // [SECTION] PLATFORM DEPENDENT HELPERS //----------------------------------------------------------------------------- @@ -9309,9 +9412,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) return; } - ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list + ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list if (window && IsItemHovered()) - overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); + fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; @@ -9333,8 +9436,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImRect vtxs_rect; for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); - clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); - vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); + clip_rect.Floor(); fg_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); + vtxs_rect.Floor(); fg_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); } if (!pcmd_node_open) continue; @@ -9358,10 +9461,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Selectable(buf, false); if (ImGui::IsItemHovered()) { - ImDrawListFlags backup_flags = overlay_draw_list->Flags; - overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. - overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); - overlay_draw_list->Flags = backup_flags; + ImDrawListFlags backup_flags = fg_draw_list->Flags; + fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. + fg_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); + fg_draw_list->Flags = backup_flags; } } ImGui::TreePop(); @@ -9498,9 +9601,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) char buf[32]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); float font_size = ImGui::GetFontSize() * 2; - ImDrawList* overlay_draw_list = GetOverlayDrawList(window); - overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); - overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); + ImDrawList* fg_draw_list = GetForegroundDrawList(window); + fg_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); + fg_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); } } ImGui::End(); diff --git a/imgui/imgui.h b/imgui/imgui.h index 3cd00a35..fdbbb89e 100644 --- a/imgui/imgui.h +++ b/imgui/imgui.h @@ -1,9 +1,9 @@ -// dear imgui, v1.68 +// dear imgui, v1.69 // (headers) // See imgui.cpp file for documentation. // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. -// Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. +// Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui /* @@ -26,7 +26,7 @@ Index of this file: #pragma once -// Configuration file (edit imconfig.h or define IMGUI_USER_CONFIG to your own filename) +// Configuration file with compile-time options (edit imconfig.h or define IMGUI_USER_CONFIG to your own filename) #ifdef IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG #endif @@ -44,9 +44,9 @@ Index of this file: #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp // Version -// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY00 then bounced up to XYY01 when release tagging happens) -#define IMGUI_VERSION "1.68" -#define IMGUI_VERSION_NUM 16801 +// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) +#define IMGUI_VERSION "1.69" +#define IMGUI_VERSION_NUM 16900 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) // Define attributes of all API symbols declarations (e.g. for DLL under Windows) @@ -150,6 +150,10 @@ typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Scalar data types +typedef signed char ImS8; // 8-bit signed integer == char +typedef unsigned char ImU8; // 8-bit unsigned integer +typedef signed short ImS16; // 16-bit signed integer +typedef unsigned short ImU16; // 16-bit unsigned integer typedef signed int ImS32; // 32-bit signed integer == int typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) #if defined(_MSC_VER) && !defined(__clang__) @@ -215,12 +219,12 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create demo/test window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create about window. display Dear ImGui version, credits and build/system information. - IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics window. display Dear ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. + IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics/debug window. display Dear ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). - IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.23" + IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.23" (essentially the compiled value for IMGUI_VERSION) // Styles IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) @@ -263,7 +267,7 @@ namespace ImGui IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API float GetContentRegionAvailWidth(); // + IMGUI_API float GetContentRegionAvailWidth(); // == GetContentRegionAvail().x IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates IMGUI_API float GetWindowContentRegionWidth(); // @@ -293,7 +297,7 @@ namespace ImGui IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. - IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. + IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. // Parameters stacks (shared) IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font @@ -385,7 +389,7 @@ namespace ImGui // - Most widgets return true when the value has been changed or when pressed/selected IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text - IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) + IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding @@ -446,6 +450,7 @@ namespace ImGui // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc. IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0); IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); @@ -459,7 +464,8 @@ namespace ImGui IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little colored preview square that can be left-clicked to open a picker, and right-clicked to open an option menu.) - // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can the pass the address of a first float element out of a contiguous structure, e.g. &myvector.x + // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. + // - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); @@ -470,7 +476,7 @@ namespace ImGui // Widgets: Trees // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents. IMGUI_API bool TreeNode(const char* label); - IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to completely decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). + IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); @@ -528,7 +534,7 @@ namespace ImGui // Tooltips IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). IMGUI_API void EndTooltip(); - IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). overidde any previous call to SetTooltip(). + IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); // Popups, Modals @@ -572,9 +578,9 @@ namespace ImGui // Logging/Capture // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging. - IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty (stdout) - IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file - IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard + IMGUI_API void LogToTTY(int auto_open_depth = -1); // start logging to tty (stdout) + IMGUI_API void LogToFile(int auto_open_depth = -1, const char* filename = NULL); // start logging to file + IMGUI_API void LogToClipboard(int auto_open_depth = -1); // start logging to OS clipboard IMGUI_API void LogFinish(); // stop logging (close file, etc.) IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) @@ -582,7 +588,7 @@ namespace ImGui // Drag and Drop // [BETA API] API may evolve! IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() - IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. + IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. @@ -610,9 +616,9 @@ namespace ImGui IMGUI_API bool IsItemActivated(); // was the last item just made active (item was previously inactive). IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing. IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). - IMGUI_API bool IsAnyItemHovered(); - IMGUI_API bool IsAnyItemActive(); - IMGUI_API bool IsAnyItemFocused(); + IMGUI_API bool IsAnyItemHovered(); // is any item hovered? + IMGUI_API bool IsAnyItemActive(); // is any item active? + IMGUI_API bool IsAnyItemFocused(); // is any item focused? IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item @@ -623,9 +629,10 @@ namespace ImGui IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. IMGUI_API double GetTime(); // get global imgui time. incremented by io.DeltaTime every frame. IMGUI_API int GetFrameCount(); // get global imgui frame count. incremented by 1 every frame. - IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, useful to quickly draw overlays shapes/text - IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances - IMGUI_API const char* GetStyleColorName(ImGuiCol idx); + IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances. + IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); @@ -652,7 +659,7 @@ namespace ImGui IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. - IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // + IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse position at the time of opening popup we have BeginPopup() into IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position. This is locked and return 0.0f until the mouse moves past a distance threshold at least once. If lock_threshold < -1.0f uses io.MouseDraggingThreshold @@ -671,12 +678,12 @@ namespace ImGui // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. - IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); + IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. - // Memory Utilities + // Memory Allocators // - All those functions are not reliant on the current context. - // - If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again. + // - If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again because we use global storage for those. IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL); IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); @@ -694,7 +701,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window - ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programatically) + ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame @@ -704,7 +711,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state - ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programatically giving it focus) + ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus) ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) @@ -737,7 +744,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider looking at the IsItemDeactivatedAfterEdit() function. ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Callback on pressing TAB (for completion handling) ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Callback on pressing Up/Down arrows (for history handling) ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Callback on each iteration. User code may query cursor position, modify text buffer. @@ -826,7 +833,7 @@ enum ImGuiTabItemFlags_ { ImGuiTabItemFlags_None = 0, ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. - ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programatically make the tab selected when calling BeginTabItem() + ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() }; @@ -884,10 +891,14 @@ enum ImGuiDragDropFlags_ // A primary data type enum ImGuiDataType_ { + ImGuiDataType_S8, // char + ImGuiDataType_U8, // unsigned char + ImGuiDataType_S16, // short + ImGuiDataType_U16, // unsigned short ImGuiDataType_S32, // int ImGuiDataType_U32, // unsigned int - ImGuiDataType_S64, // long long, __int64 - ImGuiDataType_U64, // unsigned long long, unsigned __int64 + ImGuiDataType_S64, // long long / __int64 + ImGuiDataType_U64, // unsigned long long / unsigned __int64 ImGuiDataType_Float, // float ImGuiDataType_Double, // double ImGuiDataType_COUNT @@ -958,6 +969,7 @@ enum ImGuiNavInput_ // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt + ImGuiNavInput_KeyTab_, // tab // = Tab key ImGuiNavInput_KeyLeft_, // move left // = Arrow keys ImGuiNavInput_KeyRight_, // move right ImGuiNavInput_KeyUp_, // move up @@ -1059,7 +1071,7 @@ enum ImGuiCol_ // NB: if changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type. enum ImGuiStyleVar_ { - // Enum name ......................// Member in ImGuiStyle structure (see ImGuiStyle for descriptions) + // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) ImGuiStyleVar_Alpha, // float Alpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding @@ -1095,7 +1107,7 @@ enum ImGuiStyleVar_ enum ImGuiColorEditFlags_ { ImGuiColorEditFlags_None = 0, - ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (read 3 components from the input pointer). + ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer). ImGuiColorEditFlags_NoPicker = 1 << 2, // // ColorEdit: disable picker when clicking on colored square. ImGuiColorEditFlags_NoOptions = 1 << 3, // // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. ImGuiColorEditFlags_NoSmallPreview = 1 << 4, // // ColorEdit, ColorPicker: disable colored square preview next to the inputs. (e.g. to show only the inputs) @@ -1105,24 +1117,35 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small colored square preview instead. ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. - // User Options (right-click on widget to change some of them). You can set application defaults using SetColorEditOptions(). The idea is that you probably don't want to override them in most of your calls, let the user choose and/or call SetColorEditOptions() during startup. + // User Options (right-click on widget to change some of them). ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. ImGuiColorEditFlags_AlphaPreview = 1 << 17, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). - ImGuiColorEditFlags_RGB = 1 << 20, // [Inputs] // ColorEdit: choose one among RGB/HSV/HEX. ColorPicker: choose any combination using RGB/HSV/HEX. - ImGuiColorEditFlags_HSV = 1 << 21, // [Inputs] // " - ImGuiColorEditFlags_HEX = 1 << 22, // [Inputs] // " + ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. + ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " + ImGuiColorEditFlags_DisplayHex = 1 << 22, // [Display] // " ImGuiColorEditFlags_Uint8 = 1 << 23, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. ImGuiColorEditFlags_Float = 1 << 24, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. - ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [PickerMode] // ColorPicker: bar for Hue, rectangle for Sat/Value. - ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [PickerMode] // ColorPicker: wheel for Hue, triangle for Sat/Value. + ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [Picker] // ColorPicker: bar for Hue, rectangle for Sat/Value. + ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [Picker] // ColorPicker: wheel for Hue, triangle for Sat/Value. + ImGuiColorEditFlags_InputRGB = 1 << 27, // [Input] // ColorEdit, ColorPicker: input and output data in RGB format. + ImGuiColorEditFlags_InputHSV = 1 << 28, // [Input] // ColorEdit, ColorPicker: input and output data in HSV format. + + // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to + // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. + ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks - ImGuiColorEditFlags__InputsMask = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX, + ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_DisplayHSV|ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_Float, ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel|ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_PickerHueBar // Change application default using SetColorEditOptions() + ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_InputHSV + + // Obsolete names (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + , ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex +#endif }; // Enumeration for GetMouseCursor() @@ -1308,7 +1331,7 @@ struct ImGuiIO bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) - bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the a per-window ImGuiWindowFlags_ResizeFromAnySide flag) + bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsMoveFromTitleBarOnly; // = false // [BETA] Set to true to only allow moving windows when clicked+dragged from the title bar. Windows without a title bar are not affected. //------------------------------------------------------------------ @@ -1485,6 +1508,8 @@ struct ImGuiPayload #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.69 (from Mar 2019) + static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.66 (from Sep 2018) static inline void SetScrollHere(float center_ratio=0.5f){ SetScrollHereY(center_ratio); } // OBSOLETED in 1.63 (between Aug 2018 and Sept 2018) @@ -1528,7 +1553,7 @@ typedef ImGuiInputTextCallbackData ImGuiTextEditCallbackData; // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. struct ImNewDummy {}; inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; } -inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symetrical new() +inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symmetrical new() #define IM_PLACEMENT_NEW(_PTR) new(ImNewDummy(), _PTR) #define IM_NEW(_TYPE) new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } } @@ -1694,7 +1719,7 @@ struct ImGuiListClipper #define IM_COL32_BLACK IM_COL32(0,0,0,255) // Opaque black #define IM_COL32_BLACK_TRANS IM_COL32(0,0,0,0) // Transparent black = 0x00000000 -// Helper: ImColor() implicity converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) +// Helper: ImColor() implicitly converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) // Prefer using IM_COL32() macros if you want a guaranteed compile-time ImU32 for usage with ImDrawList API. // **Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class. MAY OBSOLETE. // **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed. @@ -1842,8 +1867,8 @@ struct ImDrawList IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,1), ImU32 col = 0xFFFFFFFF); IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,0), const ImVec2& uv_c = ImVec2(1,1), const ImVec2& uv_d = ImVec2(0,1), ImU32 col = 0xFFFFFFFF); IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col, float rounding, int rounding_corners = ImDrawCornerFlags_All); - IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness); - IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. + IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, bool closed, float thickness); + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. IMGUI_API void AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() @@ -2022,6 +2047,7 @@ struct ImFontAtlas IMGUI_API const ImWchar* GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters + IMGUI_API const ImWchar* GetGlyphRangesVietnamese(); // Default + Vietname characters //------------------------------------------- // Custom Rectangles/Glyphs API diff --git a/imgui/imgui_demo.cpp b/imgui/imgui_demo.cpp index d2fa3a3b..b54475db 100644 --- a/imgui/imgui_demo.cpp +++ b/imgui/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.68 +// dear imgui, v1.69 // (demo code) // Message to the person tempted to delete this file when integrating Dear ImGui into their code base: @@ -126,7 +126,7 @@ static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleMenuFile(); // Helper to display a little (?) mark which shows a tooltip when hovered. -static void ShowHelpMarker(const char* desc) +static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) @@ -311,9 +311,9 @@ void ImGui::ShowDemoWindow(bool* p_open) { ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); - ImGui::SameLine(); ShowHelpMarker("Required back-end to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); + ImGui::SameLine(); HelpMarker("Required back-end to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); - ImGui::SameLine(); ShowHelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); + ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouse); if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) // Create a way to restore this flag otherwise we could be stuck completely! { @@ -326,21 +326,21 @@ void ImGui::ShowDemoWindow(bool* p_open) io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); - ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); + ImGui::SameLine(); HelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); - ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); + ImGui::SameLine(); HelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); - ImGui::SameLine(); ShowHelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); + ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); - ImGui::SameLine(); ShowHelpMarker("Instruct Dear ImGui to render a mouse cursor for you. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); + ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor for you. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); ImGui::TreePop(); ImGui::Separator(); } if (ImGui::TreeNode("Backend Flags")) { - ShowHelpMarker("Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities."); + HelpMarker("Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities."); ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying the back-end flags. ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasGamepad); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors); @@ -359,7 +359,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::TreeNode("Capture/Logging")) { ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded."); - ShowHelpMarker("Try opening any of the contents below in this window and then click one of the \"Log To\" button."); + HelpMarker("Try opening any of the contents below in this window and then click one of the \"Log To\" button."); ImGui::LogButtons(); ImGui::TextWrapped("You can also call ImGui::LogText() to output directly to the log without a visual output."); if (ImGui::Button("Copy \"Hello, world!\" to clipboard")) @@ -476,17 +476,20 @@ static void ShowDemoWindowWidgets() const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); - ImGui::SameLine(); ShowHelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); + ImGui::SameLine(); HelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); } { static char str0[128] = "Hello, world!"; - static int i0 = 123; ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); - ImGui::SameLine(); ShowHelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp)."); + ImGui::SameLine(); HelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp)."); + static char str1[128] = ""; + ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); + + static int i0 = 123; ImGui::InputInt("input int", &i0); - ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); + ImGui::SameLine(); HelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); static float f0 = 0.001f; ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f"); @@ -496,7 +499,7 @@ static void ShowDemoWindowWidgets() static float f1 = 1.e10f; ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); - ImGui::SameLine(); ShowHelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); + ImGui::SameLine(); HelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; ImGui::InputFloat3("input float3", vec4a); @@ -505,7 +508,7 @@ static void ShowDemoWindowWidgets() { static int i1 = 50, i2 = 42; ImGui::DragInt("drag int", &i1, 1); - ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); + ImGui::SameLine(); HelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%"); @@ -517,7 +520,7 @@ static void ShowDemoWindowWidgets() { static int i1=0; ImGui::SliderInt("slider int", &i1, -1, 3); - ImGui::SameLine(); ShowHelpMarker("CTRL+click to input value."); + ImGui::SameLine(); HelpMarker("CTRL+click to input value."); static float f1=0.123f, f2=0.0f; ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); @@ -530,7 +533,7 @@ static void ShowDemoWindowWidgets() static float col1[3] = { 1.0f,0.0f,0.2f }; static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; ImGui::ColorEdit3("color 1", col1); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); + ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); ImGui::ColorEdit4("color 2", col2); } @@ -573,7 +576,7 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Advanced, with Selectable nodes")) { - ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); + HelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); static bool align_label_with_current_x_position = false; ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); ImGui::Text("Hello!"); @@ -660,7 +663,7 @@ static void ShowDemoWindowWidgets() ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); ImGui::TextDisabled("Disabled"); - ImGui::SameLine(); ShowHelpMarker("The TextDisabled color is stored in ImGuiStyle."); + ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle."); ImGui::TreePop(); } @@ -767,6 +770,7 @@ static void ShowDemoWindowWidgets() // Expose flags as checkbox for the demo static ImGuiComboFlags flags = 0; ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", (unsigned int*)&flags, ImGuiComboFlags_PopupAlignLeft); + ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", (unsigned int*)&flags, ImGuiComboFlags_NoArrowButton)) flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", (unsigned int*)&flags, ImGuiComboFlags_NoPreview)) @@ -837,7 +841,7 @@ static void ShowDemoWindowWidgets() } if (ImGui::TreeNode("Selection State: Multiple Selection")) { - ShowHelpMarker("Hold CTRL and click to select multiple items."); + HelpMarker("Hold CTRL and click to select multiple items."); static bool selection[5] = { false, false, false, false, false }; for (int n = 0; n < 5; n++) { @@ -897,7 +901,7 @@ static void ShowDemoWindowWidgets() } if (ImGui::TreeNode("Alignment")) { - ShowHelpMarker("Alignment applies when a selectable is larger than its text content.\nBy default, Selectables uses style.SelectableTextAlign but it can be overriden on a per-item basis using PushStyleVar()."); + HelpMarker("Alignment applies when a selectable is larger than its text content.\nBy default, Selectables uses style.SelectableTextAlign but it can be overriden on a per-item basis using PushStyleVar()."); static bool selected[3*3] = { true, false, true, false, true, false, true, false, true }; for (int y = 0; y < 3; y++) { @@ -917,46 +921,89 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Filtered Text Input")) + if (ImGui::TreeNode("Text Input")) { - static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); - static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); - static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); - static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; - static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + if (ImGui::TreeNode("Multi-line Text Input")) + { + // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize + // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. + static char text[1024 * 16] = + "/*\n" + " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" + " the hexadecimal encoding of one offending instruction,\n" + " more formally, the invalid operand with locked CMPXCHG8B\n" + " instruction bug, is a design flaw in the majority of\n" + " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" + " processors (all in the P5 microarchitecture).\n" + "*/\n\n" + "label:\n" + "\tlock cmpxchg8b eax\n"; - ImGui::Text("Password input"); - static char bufpass[64] = "password123"; - ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); - ImGui::SameLine(); ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; + HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp)"); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", (unsigned int*)&flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", (unsigned int*)&flags, ImGuiInputTextFlags_AllowTabInput); + ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", (unsigned int*)&flags, ImGuiInputTextFlags_CtrlEnterForNewLine); + ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); + ImGui::TreePop(); + } - ImGui::TreePop(); - } + if (ImGui::TreeNode("Filtered Text Input")) + { + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); + static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); + struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); - if (ImGui::TreeNode("Multi-line Text Input")) - { - // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize - // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. - static bool read_only = false; - static char text[1024*16] = - "/*\n" - " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" - " the hexadecimal encoding of one offending instruction,\n" - " more formally, the invalid operand with locked CMPXCHG8B\n" - " instruction bug, is a design flaw in the majority of\n" - " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" - " processors (all in the P5 microarchitecture).\n" - "*/\n\n" - "label:\n" - "\tlock cmpxchg8b eax\n"; + ImGui::Text("Password input"); + static char bufpass[64] = "password123"; + ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); + ImGui::InputTextWithHint("password (w/ hint)", "", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Resize Callback")) + { + // If you have a custom string type you would typically create a ImGui::InputText() wrapper than takes your type as input. + // See misc/cpp/imgui_stdlib.h and .cpp for an implementation of this using std::string. + HelpMarker("Demonstrate using ImGuiInputTextFlags_CallbackResize to wire your resizable string type to InputText().\n\nSee misc/cpp/imgui_stdlib.h for an implementation of this for std::string."); + struct Funcs + { + static int MyResizeCallback(ImGuiInputTextCallbackData* data) + { + if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) + { + ImVector* my_str = (ImVector*)data->UserData; + IM_ASSERT(my_str->begin() == data->Buf); + my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1 + data->Buf = my_str->begin(); + } + return 0; + } + + // Tip: Because ImGui:: is a namespace you can add your own function into the namespace from your own source files. + static bool MyInputTextMultiline(const char* label, ImVector* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0) + { + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); + return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str); + } + }; + + // For this demo we are using ImVector as a string container. + // Note that because we need to store a terminating zero character, our size/capacity are 1 more than usually reported by a typical string class. + static ImVector my_str; + if (my_str.empty()) + my_str.push_back(0); + Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16)); + ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); + ImGui::TreePop(); + } - ShowHelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp)"); - ImGui::Checkbox("Read-only", &read_only); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } @@ -1036,22 +1083,22 @@ static void ShowDemoWindowWidgets() ImGui::Checkbox("With Alpha Preview", &alpha_preview); ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); ImGui::Checkbox("With Drag and Drop", &drag_and_drop); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); ShowHelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); + ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); + ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); ImGui::Text("Color widget:"); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); + ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_HSV | misc_flags); + ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); ImGui::Text("Color widget with Float Display:"); ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); ImGui::Text("Color button with Picker:"); - ImGui::SameLine(); ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); + ImGui::SameLine(); HelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); ImGui::Text("Color button with Custom Picker Popup:"); @@ -1127,7 +1174,7 @@ static void ShowDemoWindowWidgets() static bool side_preview = true; static bool ref_color = false; static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); - static int inputs_mode = 2; + static int display_mode = 0; static int picker_mode = 0; ImGui::Checkbox("With Alpha", &alpha); ImGui::Checkbox("With Alpha Bar", &alpha_bar); @@ -1142,28 +1189,39 @@ static void ShowDemoWindowWidgets() ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); } } - ImGui::Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0"); + ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0"); + ImGui::SameLine(); HelpMarker("ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, but the user can change it with a right-click.\n\nColorPicker defaults to displaying RGB+HSV+Hex if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); - ImGui::SameLine(); ShowHelpMarker("User can right-click the picker to change mode."); + ImGui::SameLine(); HelpMarker("User can right-click the picker to change mode."); ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; - if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; - if (inputs_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; - if (inputs_mode == 2) flags |= ImGuiColorEditFlags_RGB; - if (inputs_mode == 3) flags |= ImGuiColorEditFlags_HSV; - if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX; + if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() + if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; + if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; + if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; + if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays + if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode + if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV; + if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex; ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); ImGui::Text("Programmatically set defaults:"); - ImGui::SameLine(); ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); + ImGui::SameLine(); HelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV | ImGuiColorEditFlags_PickerHueBar); + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar); if (ImGui::Button("Default: Float + HDR + Hue Wheel")) ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); + // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) + static ImVec4 color_stored_as_hsv(0.23f, 1.0f, 1.0f, 1.0f); + ImGui::Spacing(); + ImGui::Text("HSV encoded colors"); + ImGui::SameLine(); HelpMarker("By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the added benefit that you can manipulate hue values with the picker even when saturation or value are zero."); + ImGui::Text("Color widget with InputHSV:"); + ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); + ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); + ImGui::DragFloat4("Raw HSV values", (float*)&color_stored_as_hsv, 0.01f, 0.0f, 1.0f); + ImGui::TreePop(); } @@ -1197,6 +1255,10 @@ static void ShowDemoWindowWidgets() ImS64 LLONG_MAX = 9223372036854775807LL; ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1); #endif + const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127; + const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255; + const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767; + const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535; const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2; const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2; const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2; @@ -1205,6 +1267,10 @@ static void ShowDemoWindowWidgets() const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0; // State + static char s8_v = 127; + static ImU8 u8_v = 255; + static short s16_v = 32767; + static ImU16 u16_v = 65535; static ImS32 s32_v = -1; static ImU32 u32_v = (ImU32)-1; static ImS64 s64_v = -1; @@ -1215,17 +1281,25 @@ static void ShowDemoWindowWidgets() const float drag_speed = 0.2f; static bool drag_clamp = false; ImGui::Text("Drags:"); - ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); ShowHelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); + ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); + ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL); + ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms"); + ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); + ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f); - ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); ShowHelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); + ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); HelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f); ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f); ImGui::Text("Sliders"); + ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); + ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); + ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d"); + ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u"); ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); @@ -1248,6 +1322,10 @@ static void ShowDemoWindowWidgets() static bool inputs_step = true; ImGui::Text("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); + ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d"); + ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u"); + ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d"); + ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u"); ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); @@ -1439,20 +1517,23 @@ static void ShowDemoWindowWidgets() static int item_type = 1; static bool b = false; static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; + static char str[16] = {}; ImGui::RadioButton("Text", &item_type, 0); ImGui::RadioButton("Button", &item_type, 1); ImGui::RadioButton("Checkbox", &item_type, 2); ImGui::RadioButton("SliderFloat", &item_type, 3); - ImGui::RadioButton("ColorEdit4", &item_type, 4); - ImGui::RadioButton("ListBox", &item_type, 5); + ImGui::RadioButton("InputText", &item_type, 4); + ImGui::RadioButton("ColorEdit4", &item_type, 5); + ImGui::RadioButton("ListBox", &item_type, 6); ImGui::Separator(); bool ret = false; if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button if (item_type == 2) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 3) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item - if (item_type == 4) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 5) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + if (item_type == 4) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) + if (item_type == 5) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 6) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } ImGui::BulletText( "Return value = %d\n" "IsItemFocused() = %d\n" @@ -1559,7 +1640,7 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Child windows")) { - ShowHelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); + HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); static bool disable_mouse_wheel = false; static bool disable_menu = false; ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); @@ -1643,31 +1724,31 @@ static void ShowDemoWindowLayout() { static float f = 0.0f; ImGui::Text("PushItemWidth(100)"); - ImGui::SameLine(); ShowHelpMarker("Fixed width."); + ImGui::SameLine(); HelpMarker("Fixed width."); ImGui::PushItemWidth(100); ImGui::DragFloat("float##1", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(GetWindowWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of window width."); + ImGui::SameLine(); HelpMarker("Half of window width."); ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); ImGui::DragFloat("float##2", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(GetContentRegionAvailWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); + ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() * 0.5f); ImGui::DragFloat("float##3", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(-100)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge minus 100"); + ImGui::SameLine(); HelpMarker("Align to right edge minus 100"); ImGui::PushItemWidth(-100); ImGui::DragFloat("float##4", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(-1)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge"); + ImGui::SameLine(); HelpMarker("Align to right edge"); ImGui::PushItemWidth(-1); ImGui::DragFloat("float##5", &f); ImGui::PopItemWidth(); @@ -1836,7 +1917,7 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Groups")) { - ShowHelpMarker("Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it."); + HelpMarker("Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it."); ImGui::BeginGroup(); { ImGui::BeginGroup(); @@ -1880,7 +1961,7 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Text Baseline Alignment")) { - ShowHelpMarker("This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets."); + HelpMarker("This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets."); ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); ImGui::Text("Hello\nWorld"); ImGui::SameLine(); @@ -1935,7 +2016,7 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Scrolling")) { - ShowHelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given position."); + HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given position."); static bool track = true; static int track_line = 50, scroll_to_px = 200; @@ -1977,7 +2058,7 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Horizontal Scrolling")) { - ShowHelpMarker("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\nYou may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); + HelpMarker("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\nYou may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); static int lines = 7; ImGui::SliderInt("Lines", &lines, 1, 15); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); @@ -2149,7 +2230,7 @@ static void ShowDemoWindowPopups() // We can also use OpenPopupOnItemClick() which is the same as BeginPopupContextItem() but without the Begin call. // So here we will make it that clicking on the text field with the right mouse button (1) will toggle the visibility of the popup above. - ImGui::Text("(You can also right-click me to the same popup as above.)"); + ImGui::Text("(You can also right-click me to open the same popup as above.)"); ImGui::OpenPopupOnItemClick("item context menu", 1); // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). @@ -2430,7 +2511,7 @@ static void ShowDemoWindowColumns() } bool node_open = ImGui::TreeNode("Tree within single cell"); - ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); + ImGui::SameLine(); HelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); if (node_open) { ImGui::Columns(2, "tree items"); @@ -2513,7 +2594,7 @@ static void ShowDemoWindowMisc() ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); ImGui::PushAllowKeyboardFocus(false); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - //ImGui::SameLine(); ShowHelperMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); + //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); ImGui::PopAllowKeyboardFocus(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); @@ -2591,7 +2672,7 @@ static void ShowDemoWindowMisc() ImGui::Text("Current mouse cursor = %d: %s", ImGui::GetMouseCursor(), mouse_cursors_names[ImGui::GetMouseCursor()]); ImGui::Text("Hover to see mouse cursors:"); - ImGui::SameLine(); ShowHelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); + ImGui::SameLine(); HelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); for (int i = 0; i < ImGuiMouseCursor_COUNT; i++) { char label[32]; @@ -2769,7 +2850,7 @@ void ImGui::ShowFontSelector(const char* label) ImGui::EndCombo(); } ImGui::SameLine(); - ShowHelpMarker( + HelpMarker( "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n" "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" "- Read FAQ and documentation in misc/fonts/ for more details.\n" @@ -2812,7 +2893,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) if (ImGui::Button("Revert Ref")) style = *ref; ImGui::SameLine(); - ShowHelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export Colors\" below to save them somewhere."); + HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export Colors\" below to save them somewhere."); ImGui::Separator(); @@ -2845,9 +2926,9 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); ImGui::Text("Alignment"); ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); - ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a selectable is larger than its text content."); - ImGui::Text("Safe Area Padding"); ImGui::SameLine(); ShowHelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); + ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); + ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); + ImGui::Text("Safe Area Padding"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::EndTabItem(); } @@ -2882,7 +2963,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::RadioButton("Opaque", &alpha_flags, 0); ImGui::SameLine(); ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); ImGui::SameLine(); - ShowHelpMarker("In the color list:\nLeft-click on colored square to open color picker,\nRight-click to open edit options menu."); + HelpMarker("In the color list:\nLeft-click on colored square to open color picker,\nRight-click to open edit options menu."); ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); ImGui::PushItemWidth(-160); @@ -2914,7 +2995,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) { ImGuiIO& io = ImGui::GetIO(); ImFontAtlas* atlas = io.Fonts; - ShowHelpMarker("Read FAQ and misc/fonts/README.txt for details on font loading."); + HelpMarker("Read FAQ and misc/fonts/README.txt for details on font loading."); ImGui::PushItemWidth(120); for (int i = 0; i < atlas->Fonts.Size; i++) { @@ -2928,7 +3009,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::Text("The quick brown fox jumps over the lazy dog"); ImGui::PopFont(); ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font - ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); + ImGui::SameLine(); HelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, "%.0f"); ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); @@ -2997,7 +3078,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) if (ImGui::BeginTabItem("Rendering")) { - ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); ShowHelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); + ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, "%.2f", 2.0f); @@ -3671,7 +3752,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) return; } - ShowHelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); + HelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2,2)); ImGui::Columns(2); @@ -3950,97 +4031,121 @@ static void ShowExampleAppCustomRendering(bool* p_open) // In this example we are not using the maths operators! ImDrawList* draw_list = ImGui::GetWindowDrawList(); - // Primitives - ImGui::Text("Primitives"); - static float sz = 36.0f; - static float thickness = 4.0f; - static ImVec4 col = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); - ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); - ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); - ImGui::ColorEdit4("Color", &col.x); + if (ImGui::BeginTabBar("##TabBar")) { - const ImVec2 p = ImGui::GetCursorScreenPos(); - const ImU32 col32 = ImColor(col); - float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; - for (int n = 0; n < 2; n++) + // Primitives + if (ImGui::BeginTabItem("Primitives")) { - // First line uses a thickness of 1.0, second line uses the configurable thickness - float th = (n == 0) ? 1.0f : thickness; - draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 6, th); x += sz+spacing; // Hexagon - draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 20, th); x += sz+spacing; // Circle - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 0.0f, ImDrawCornerFlags_All, th); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_All, th); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight, th); x += sz+spacing; - draw_list->AddTriangle(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32, th); x += sz+spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y ), col32, th); x += sz+spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y+sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, th); x += sz+spacing; // Diagonal line - draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x+sz*1.3f,y+sz*0.3f), ImVec2(x+sz-sz*1.3f,y+sz-sz*0.3f), ImVec2(x+sz, y+sz), col32, th); - x = p.x + 4; - y += sz+spacing; - } - draw_list->AddCircleFilled(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 6); x += sz+spacing; // Hexagon - draw_list->AddCircleFilled(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 32); x += sz+spacing; // Circle - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight); x += sz+spacing; - draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+thickness), col32); x += sz+spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+thickness, y+sz), col32); x += spacing+spacing; // Vertical line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+1, y+1), col32); x += sz; // Pixel (faster than AddLine) - draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x+sz, y+sz), IM_COL32(0,0,0,255), IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255)); - ImGui::Dummy(ImVec2((sz+spacing)*8, (sz+spacing)*3)); - } - ImGui::Separator(); - { - static ImVector points; - static bool adding_line = false; - ImGui::Text("Canvas example"); - if (ImGui::Button("Clear")) points.clear(); - if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } - ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); - - // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() - // But you can also draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). - // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). - ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! - ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available - if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; - if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; - draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255)); - draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255, 255, 255, 255)); - - bool adding_preview = false; - ImGui::InvisibleButton("canvas", canvas_size); - ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); - if (adding_line) - { - adding_preview = true; - points.push_back(mouse_pos_in_canvas); - if (!ImGui::IsMouseDown(0)) - adding_line = adding_preview = false; - } - if (ImGui::IsItemHovered()) - { - if (!adding_line && ImGui::IsMouseClicked(0)) + static float sz = 36.0f; + static float thickness = 4.0f; + static ImVec4 col = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); + ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); + ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); + ImGui::ColorEdit4("Color", &col.x); + const ImVec2 p = ImGui::GetCursorScreenPos(); + const ImU32 col32 = ImColor(col); + float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; + for (int n = 0; n < 2; n++) { + // First line uses a thickness of 1.0, second line uses the configurable thickness + float th = (n == 0) ? 1.0f : thickness; + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6, th); x += sz + spacing; // Hexagon + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 20, th); x += sz + spacing; // Circle + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 0.0f, ImDrawCornerFlags_All, th); x += sz + spacing; + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_All, th); x += sz + spacing; + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight, th); x += sz + spacing; + draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32, th); x += sz + spacing; + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col32, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, th); x += sz + spacing; // Diagonal line + draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col32, th); + x = p.x + 4; + y += sz + spacing; + } + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6); x += sz + spacing; // Hexagon + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 32); x += sz + spacing; // Circle + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32); x += sz + spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f); x += sz + spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight); x += sz + spacing; + draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32); x += sz + spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col32); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col32); x += spacing + spacing; // Vertical line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col32); x += sz; // Pixel (faster than AddLine) + draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); + ImGui::Dummy(ImVec2((sz + spacing) * 9.5f, (sz + spacing) * 3)); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Canvas")) + { + static ImVector points; + static bool adding_line = false; + if (ImGui::Button("Clear")) points.clear(); + if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } + ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); + + // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() + // But you can also draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). + // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). + ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available + if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; + if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; + draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255)); + draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255, 255, 255, 255)); + + bool adding_preview = false; + ImGui::InvisibleButton("canvas", canvas_size); + ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); + if (adding_line) + { + adding_preview = true; points.push_back(mouse_pos_in_canvas); - adding_line = true; + if (!ImGui::IsMouseDown(0)) + adding_line = adding_preview = false; } - if (ImGui::IsMouseClicked(1) && !points.empty()) + if (ImGui::IsItemHovered()) { - adding_line = adding_preview = false; - points.pop_back(); - points.pop_back(); + if (!adding_line && ImGui::IsMouseClicked(0)) + { + points.push_back(mouse_pos_in_canvas); + adding_line = true; + } + if (ImGui::IsMouseClicked(1) && !points.empty()) + { + adding_line = adding_preview = false; + points.pop_back(); + points.pop_back(); + } } + draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) + for (int i = 0; i < points.Size - 1; i += 2) + draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i + 1].x, canvas_pos.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); + draw_list->PopClipRect(); + if (adding_preview) + points.pop_back(); + ImGui::EndTabItem(); } - draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) - for (int i = 0; i < points.Size - 1; i += 2) - draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i + 1].x, canvas_pos.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); - draw_list->PopClipRect(); - if (adding_preview) - points.pop_back(); + + if (ImGui::BeginTabItem("BG/FG draw lists")) + { + static bool draw_bg = true; + static bool draw_fg = true; + ImGui::Checkbox("Draw in Background draw list", &draw_bg); + ImGui::Checkbox("Draw in Foreground draw list", &draw_fg); + ImVec2 window_pos = ImGui::GetWindowPos(); + ImVec2 window_size = ImGui::GetWindowSize(); + ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f); + if (draw_bg) + ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 32, 10); + if (draw_fg) + ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 32, 10); + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); } + ImGui::End(); } diff --git a/imgui/imgui_draw.cpp b/imgui/imgui_draw.cpp index 075ea0d2..be1604f8 100644 --- a/imgui/imgui_draw.cpp +++ b/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.68 +// dear imgui, v1.69 // (drawing and font code) /* @@ -55,7 +55,7 @@ Index of this file: #ifdef __clang__ #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // #if __has_warning("-Wzero-as-null-pointer-constant") #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 @@ -83,7 +83,7 @@ Index of this file: //------------------------------------------------------------------------- // Compile time options: -//#define IMGUI_STB_NAMESPACE ImGuiStb +//#define IMGUI_STB_NAMESPACE ImStb //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION @@ -163,7 +163,7 @@ namespace IMGUI_STB_NAMESPACE #endif #ifdef IMGUI_STB_NAMESPACE -} // namespace ImGuiStb +} // namespace ImStb using namespace IMGUI_STB_NAMESPACE; #endif @@ -407,7 +407,7 @@ ImDrawList* ImDrawList::CloneOutput() const // Using macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug builds #define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : _Data->ClipRectFullscreen) -#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL) +#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : (ImTextureID)NULL) void ImDrawList::AddDrawCmd() { @@ -2346,6 +2346,23 @@ const ImWchar* ImFontAtlas::GetGlyphRangesThai() return &ranges[0]; } +const ImWchar* ImFontAtlas::GetGlyphRangesVietnamese() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + 0x0102, 0x0103, + 0x0110, 0x0111, + 0x0128, 0x0129, + 0x0168, 0x0169, + 0x01A0, 0x01A1, + 0x01AF, 0x01B0, + 0x1EA0, 0x1EF9, + 0, + }; + return &ranges[0]; +} + //----------------------------------------------------------------------------- // [SECTION] ImFontGlyphRangesBuilder //----------------------------------------------------------------------------- diff --git a/imgui/imgui_internal.h b/imgui/imgui_internal.h index e5b65040..9cd39490 100644 --- a/imgui/imgui_internal.h +++ b/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.68 +// dear imgui, v1.69 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -83,6 +83,7 @@ struct ImGuiWindowSettings; // Storage for window settings stored in .in // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior() +typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -90,13 +91,13 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags: typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for Separator() - internal typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior() -typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() +typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() //------------------------------------------------------------------------- // STB libraries includes //------------------------------------------------------------------------- -namespace ImGuiStb +namespace ImStb { #undef STB_TEXTEDIT_STRING @@ -104,9 +105,11 @@ namespace ImGuiStb #define STB_TEXTEDIT_STRING ImGuiInputTextState #define STB_TEXTEDIT_CHARTYPE ImWchar #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f +#define STB_TEXTEDIT_UNDOSTATECOUNT 99 +#define STB_TEXTEDIT_UNDOCHARCOUNT 999 #include "imstb_textedit.h" -} // namespace ImGuiStb +} // namespace ImStb //----------------------------------------------------------------------------- // Context pointer @@ -223,12 +226,15 @@ static inline double ImAtof(const char* s) static inline float ImFloorStd(float x) { return floorf(x); } // we already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by stb_truetype) static inline float ImCeil(float x) { return ceilf(x); } #endif -// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support for variety of types: signed/unsigned int/long long float/double, using templates here but we could also redefine them 6 times +// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support for variety of types: signed/unsigned int/long long float/double +// (Exceptionally using templates here but we could also redefine them for variety of types) template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } +template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } +template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } // - Misc maths helpers static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } @@ -242,6 +248,7 @@ static inline float ImLengthSqr(const ImVec4& lhs) static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)f; } static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } +static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } @@ -275,7 +282,8 @@ struct IMGUI_API ImPool T* GetByIndex(ImPoolIdx n) { return &Data[n]; } ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Data.Data && p < Data.Data + Data.Size); return (ImPoolIdx)(p - Data.Data); } T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Data[*p_idx]; *p_idx = FreeIdx; return Add(); } - void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Data[idx].~T(); } Map.Clear(); Data.clear(); FreeIdx = 0; } + bool Contains(const T* p) const { return (p >= Data.Data && p < Data.Data + Data.Size); } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Data[idx].~T(); } Map.Clear(); Data.clear(); FreeIdx = 0; } T* Add() { int idx = FreeIdx; if (idx == Data.Size) { Data.resize(Data.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Data[idx]; } IM_PLACEMENT_NEW(&Data[idx]) T(); return &Data[idx]; } void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } void Remove(ImGuiID key, ImPoolIdx idx) { Data[idx].~T(); *(int*)&Data[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } @@ -375,6 +383,12 @@ enum ImGuiItemStatusFlags_ #endif }; +enum ImGuiTextFlags_ +{ + ImGuiTextFlags_None = 0, + ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0 +}; + // FIXME: this is in development, not exposed/functional as a generic feature yet. // Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiLayoutType_ @@ -383,6 +397,15 @@ enum ImGuiLayoutType_ ImGuiLayoutType_Vertical = 1 }; +enum ImGuiLogType +{ + ImGuiLogType_None = 0, + ImGuiLogType_TTY, + ImGuiLogType_File, + ImGuiLogType_Buffer, + ImGuiLogType_Clipboard +}; + // X/Y enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiAxis { @@ -537,7 +560,6 @@ struct ImGuiGroupData ImVec1 BackupGroupOffset; ImVec2 BackupCurrentLineSize; float BackupCurrentLineTextBaseOffset; - float BackupLogLinePosY; ImGuiID BackupActiveIdIsAlive; bool BackupActiveIdPreviousFrameIsAlive; bool AdvanceCursor; @@ -546,10 +568,9 @@ struct ImGuiGroupData // Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. struct IMGUI_API ImGuiMenuColumns { - int Count; float Spacing; float Width, NextWidth; - float Pos[4], NextWidths[4]; + float Pos[3], NextWidths[3]; ImGuiMenuColumns(); void Update(int count, float spacing, bool clear); @@ -561,16 +582,17 @@ struct IMGUI_API ImGuiMenuColumns struct IMGUI_API ImGuiInputTextState { ImGuiID ID; // widget id owning the text state + int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 len is valid even if TextA is not. ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) - ImVector TempBuffer; // temporary buffer for callback and other other operations. size=capacity. - int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. + ImVector TextA; // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity. + ImVector InitialTextA; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) int BufCapacityA; // end-user buffer capacity - float ScrollX; - ImGuiStb::STB_TexteditState StbState; - float CursorAnim; - bool CursorFollow; - bool SelectedAllMouseLock; + float ScrollX; // horizontal scrolling/offset + ImStb::STB_TexteditState Stb; // state for stb_textedit.h + float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately + bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) + bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection // Temporarily set when active ImGuiInputTextFlags UserFlags; @@ -578,11 +600,14 @@ struct IMGUI_API ImGuiInputTextState void* UserCallbackData; ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } + void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking - void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } - bool HasSelection() const { return StbState.select_start != StbState.select_end; } - void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } - void SelectAll() { StbState.select_start = 0; StbState.cursor = StbState.select_end = CurLenW; StbState.has_preferred_x = 0; } + void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } + bool HasSelection() const { return Stb.select_start != Stb.select_end; } + void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } + void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } + int GetUndoAvailCount() const { return Stb.undostate.undo_point; } + int GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation }; @@ -617,7 +642,7 @@ struct ImGuiPopupRef ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() ImGuiWindow* ParentWindow; // Set on OpenPopup() int OpenFrameCount; // Set on OpenPopup() - ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differenciate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) + ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup }; @@ -748,8 +773,17 @@ struct ImGuiNextWindowData struct ImGuiTabBarSortItem { - int Index; - float Width; + int Index; + float Width; +}; + +struct ImGuiTabBarRef +{ + ImGuiTabBar* Ptr; // Either field can be set, not both. Dock node tab bars are loose while BeginTabBar() ones are in a pool. + int IndexInMainPool; + + ImGuiTabBarRef(ImGuiTabBar* ptr) { Ptr = ptr; IndexInMainPool = -1; } + ImGuiTabBarRef(int index_in_main_pool) { Ptr = NULL; IndexInMainPool = index_in_main_pool; } }; //----------------------------------------------------------------------------- @@ -857,11 +891,21 @@ struct ImGuiContext ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + // Tabbing system (older than Nav, active even if Nav is disabled. FIXME-NAV: This needs a redesign!) + ImGuiWindow* FocusRequestCurrWindow; // + ImGuiWindow* FocusRequestNextWindow; // + int FocusRequestCurrCounterAll; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) + int FocusRequestCurrCounterTab; // Tab item being requested for focus, stored as an index + int FocusRequestNextCounterAll; // Stored for next frame + int FocusRequestNextCounterTab; // " + bool FocusTabPressed; // + // Render ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user ImDrawDataBuilder DrawDataBuilder; float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays + ImDrawList BackgroundDrawList; // First draw list to be rendered. + ImDrawList ForegroundDrawList; // Last draw list to be rendered. This is where we the render software mouse cursor (if io.MouseDrawCursor is set) and most debug overlays. ImGuiMouseCursor MouseCursor; // Drag and Drop @@ -882,8 +926,9 @@ struct ImGuiContext unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads // Tab bars - ImPool TabBars; - ImVector CurrentTabBar; + ImPool TabBars; + ImGuiTabBar* CurrentTabBar; + ImVector CurrentTabBarStack; ImVector TabSortByWidthBuffer; // Widget state @@ -916,10 +961,14 @@ struct ImGuiContext // Logging bool LogEnabled; + ImGuiLogType LogType; FILE* LogFile; // If != NULL log to stdout/ file - ImGuiTextBuffer LogClipboard; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - int LogStartDepth; - int LogAutoExpandMaxDepth; + ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. + float LogLinePosY; + bool LogLineFirstItem; + int LogDepthRef; + int LogDepthToExpand; + int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. @@ -930,7 +979,7 @@ struct ImGuiContext int WantTextInputNextFrame; char TempBuffer[1024*3+1]; // Temporary text buffer - ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL) + ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(NULL), ForegroundDrawList(NULL) { Initialized = false; FrameScopeActive = FrameScopePushedImplicitWindow = false; @@ -997,9 +1046,16 @@ struct ImGuiContext NavMoveRequestForward = ImGuiNavForward_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; + FocusRequestCurrWindow = FocusRequestNextWindow = NULL; + FocusRequestCurrCounterAll = FocusRequestCurrCounterTab = INT_MAX; + FocusRequestNextCounterAll = FocusRequestNextCounterTab = INT_MAX; + FocusTabPressed = false; + DimBgRatio = 0.0f; - OverlayDrawList._Data = &DrawListSharedData; - OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging + BackgroundDrawList._Data = &DrawListSharedData; + BackgroundDrawList._OwnerName = "##Background"; // Give it a name for debugging + ForegroundDrawList._Data = &DrawListSharedData; + ForegroundDrawList._OwnerName = "##Foreground"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSourceOrTarget = false; @@ -1013,6 +1069,8 @@ struct ImGuiContext DragDropAcceptFrameCount = -1; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); + CurrentTabBar = NULL; + ScalarAsInputTextId = 0; ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; DragCurrentAccumDirty = false; @@ -1029,9 +1087,12 @@ struct ImGuiContext SettingsDirtyTimer = 0.0f; LogEnabled = false; + LogType = ImGuiLogType_None; LogFile = NULL; - LogStartDepth = 0; - LogAutoExpandMaxDepth = 2; + LogLinePosY = FLT_MAX; + LogLineFirstItem = false; + LogDepthRef = 0; + LogDepthToExpand = LogDepthToExpandDefault = 2; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); FramerateSecPerFrameIdx = 0; @@ -1057,7 +1118,6 @@ struct IMGUI_API ImGuiWindowTempData float CurrentLineTextBaseOffset; ImVec2 PrevLineSize; float PrevLineTextBaseOffset; - float LogLinePosY; int TreeDepth; ImU32 TreeDepthMayJumpToParentOnPop; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31 ImGuiID LastItemId; @@ -1076,6 +1136,8 @@ struct IMGUI_API ImGuiWindowTempData ImGuiStorage* StateStorage; ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() + int FocusCounterAll; // Counter for focus/tabbing system. Start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign) + int FocusCounterTab; // (same, but only count widgets which you can Tab through) // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] @@ -1097,7 +1159,6 @@ struct IMGUI_API ImGuiWindowTempData CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); CurrentLineSize = PrevLineSize = ImVec2(0.0f, 0.0f); CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; - LogLinePosY = -1.0f; TreeDepth = 0; TreeDepthMayJumpToParentOnPop = 0x00; LastItemId = 0; @@ -1112,8 +1173,10 @@ struct IMGUI_API ImGuiWindowTempData MenuBarOffset = ImVec2(0.0f, 0.0f); StateStorage = NULL; LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; - ItemWidth = 0.0f; + FocusCounterAll = FocusCounterTab = -1; + ItemFlags = ImGuiItemFlags_Default_; + ItemWidth = 0.0f; TextWrapPos = -1.0f; memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); @@ -1198,15 +1261,6 @@ struct IMGUI_API ImGuiWindow ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space - // Navigation / Focus - // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext - int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() - int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) - int FocusIdxAllRequestCurrent; // Item being requested for focus - int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus - int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) - int FocusIdxTabRequestNext; // " - public: ImGuiWindow(ImGuiContext* context, const char* name); ~ImGuiWindow(); @@ -1245,7 +1299,7 @@ struct ImGuiItemHoveredDataBackup enum ImGuiTabBarFlagsPrivate_ { - ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node + ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around] ImGuiTabBarFlags_IsFocused = 1 << 21, ImGuiTabBarFlags_SaveSettings = 1 << 22 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs }; @@ -1286,6 +1340,8 @@ struct ImGuiTabBar float OffsetNextTab; // Distance from BarRect.Min.x, incremented with each BeginTabItem() call, not used if ImGuiTabBarFlags_Reorderable if set. float ScrollingAnim; float ScrollingTarget; + float ScrollingTargetDistToVisibility; + float ScrollingSpeed; ImGuiTabBarFlags Flags; ImGuiID ReorderRequestTabId; int ReorderRequestDir; @@ -1376,7 +1432,7 @@ namespace ImGui IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop = true); // Return true if focus is requested + IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); @@ -1384,6 +1440,10 @@ namespace ImGui IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); + // Logging/Capture + IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. + IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer + // Popups, Modals, Tooltips IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void ClosePopupToLevel(int remaining, bool apply_focus_to_window_under); @@ -1459,12 +1519,13 @@ namespace ImGui IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, int count, ImU32 col); // Widgets + IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags); - IMGUI_API void Scrollbar(ImGuiLayoutType direction); - IMGUI_API ImGuiID GetScrollbarID(ImGuiLayoutType direction); + IMGUI_API void Scrollbar(ImGuiAxis axis); + IMGUI_API ImGuiID GetScrollbarID(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API void VerticalSeparator(); // Vertical separator, for menu bars (use current line height). Not exposed because it is misleading and it doesn't have an effect on regular layout. // Widgets low-level behaviors @@ -1479,13 +1540,13 @@ namespace ImGui // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " - template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, const T v_min, const T v_max, const char* format, float power, ImGuiDragFlags flags); - template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, const T v_min, const T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); + template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, float power, ImGuiDragFlags flags); + template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); template IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos); template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); // InputText - IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); // Color diff --git a/imgui/imgui_widgets.cpp b/imgui/imgui_widgets.cpp index 2ac389c1..23411258 100644 --- a/imgui/imgui_widgets.cpp +++ b/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.68 +// dear imgui, v1.69 // (widgets code) /* @@ -74,22 +74,30 @@ Index of this file: //------------------------------------------------------------------------- // Those MIN/MAX values are not define because we need to point to them -static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); -static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) -static const ImU32 IM_U32_MIN = 0; -static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) +static const signed char IM_S8_MIN = -128; +static const signed char IM_S8_MAX = 127; +static const unsigned char IM_U8_MIN = 0; +static const unsigned char IM_U8_MAX = 0xFF; +static const signed short IM_S16_MIN = -32768; +static const signed short IM_S16_MAX = 32767; +static const unsigned short IM_U16_MIN = 0; +static const unsigned short IM_U16_MAX = 0xFFFF; +static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); +static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) +static const ImU32 IM_U32_MIN = 0; +static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) #ifdef LLONG_MIN -static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); -static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); +static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); +static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); #else -static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; -static const ImS64 IM_S64_MAX = 9223372036854775807LL; +static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; +static const ImS64 IM_S64_MAX = 9223372036854775807LL; #endif -static const ImU64 IM_U64_MIN = 0; +static const ImU64 IM_U64_MIN = 0; #ifdef ULLONG_MAX -static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); +static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); #else -static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); +static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); #endif //------------------------------------------------------------------------- @@ -124,7 +132,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const // - BulletTextV() //------------------------------------------------------------------------- -void ImGui::TextUnformatted(const char* text, const char* text_end) +void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -138,7 +146,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); const float wrap_pos_x = window->DC.TextWrapPos; - const bool wrap_enabled = wrap_pos_x >= 0.0f; + const bool wrap_enabled = (wrap_pos_x >= 0.0f); if (text_end - text > 2000 && !wrap_enabled) { // Long text! @@ -148,69 +156,66 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. const char* line = text; const float line_height = GetTextLineHeight(); - const ImRect clip_rect = window->ClipRect; ImVec2 text_size(0,0); - if (text_pos.y <= clip_rect.Max.y) + // Lines to skip (can't skip when logging text) + ImVec2 pos = text_pos; + if (!g.LogEnabled) { - ImVec2 pos = text_pos; - - // Lines to skip (can't skip when logging text) - if (!g.LogEnabled) + int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) { - int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); - if (lines_skippable > 0) - { - int lines_skipped = 0; - while (line < text_end && lines_skipped < lines_skippable) - { - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - } - - // Lines to render - if (line < text_end) - { - ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); - while (line < text_end) - { - if (IsClippedEx(line_rect, 0, false)) - break; - - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - const ImVec2 line_size = CalcTextSize(line, line_end, false); - text_size.x = ImMax(text_size.x, line_size.x); - RenderText(pos, line, line_end, false); - line = line_end + 1; - line_rect.Min.y += line_height; - line_rect.Max.y += line_height; - pos.y += line_height; - } - - // Count remaining lines int lines_skipped = 0; - while (line < text_end) + while (line < text_end && lines_skipped < lines_skippable) { const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); line = line_end + 1; lines_skipped++; } pos.y += lines_skipped * line_height; } - - text_size.y += (pos - text_pos).y; } + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + if (IsClippedEx(line_rect, 0, false)) + break; + + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + RenderText(pos, line, line_end, false); + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + text_size.y = (pos - text_pos).y; + ImRect bb(text_pos, text_pos + text_size); ItemSize(text_size); ItemAdd(bb, 0); @@ -220,7 +225,6 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - // Account of baseline offset ImRect bb(text_pos, text_pos + text_size); ItemSize(text_size); if (!ItemAdd(bb, 0)) @@ -231,6 +235,11 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) } } +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); +} + void ImGui::Text(const char* fmt, ...) { va_list args; @@ -247,7 +256,7 @@ void ImGui::TextV(const char* fmt, va_list args) ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - TextUnformatted(g.TempBuffer, text_end); + TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); } void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) @@ -710,11 +719,9 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) return pressed; } -ImGuiID ImGui::GetScrollbarID(ImGuiLayoutType direction) +ImGuiID ImGui::GetScrollbarID(ImGuiWindow* window, ImGuiAxis axis) { - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - return window->GetID((direction == ImGuiLayoutType_Horizontal) ? "#SCROLLX" : "#SCROLLY"); + return window->GetIDNoKeepAlive(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY"); } // Vertical/Horizontal scrollbar @@ -722,15 +729,16 @@ ImGuiID ImGui::GetScrollbarID(ImGuiLayoutType direction) // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. -void ImGui::Scrollbar(ImGuiLayoutType direction) +void ImGui::Scrollbar(ImGuiAxis axis) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const bool horizontal = (direction == ImGuiLayoutType_Horizontal); + const bool horizontal = (axis == ImGuiAxis_X); const ImGuiStyle& style = g.Style; - const ImGuiID id = GetScrollbarID(direction); - + const ImGuiID id = GetScrollbarID(window, axis); + KeepAliveID(id); + // Render background bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; @@ -748,7 +756,7 @@ void ImGui::Scrollbar(ImGuiLayoutType direction) // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the resize grab) float alpha = 1.0f; - if ((direction == ImGuiLayoutType_Vertical) && bb_height < g.FontSize + g.Style.FramePadding.y * 2.0f) + if ((axis == ImGuiAxis_Y) && bb_height < g.FontSize + g.Style.FramePadding.y * 2.0f) { alpha = ImSaturate((bb_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f)); if (alpha <= 0.0f) @@ -1147,7 +1155,7 @@ void ImGui::Separator() // Those flags should eventually be overridable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected if (flags & ImGuiSeparatorFlags_Vertical) { VerticalSeparator(); @@ -1496,17 +1504,21 @@ struct ImGuiDataTypeInfo static const ImGuiDataTypeInfo GDataTypeInfo[] = { - { sizeof(int), "%d", "%d" }, - { sizeof(unsigned int), "%u", "%u" }, + { sizeof(char), "%d", "%d" }, // ImGuiDataType_S8 + { sizeof(unsigned char), "%u", "%u" }, + { sizeof(short), "%d", "%d" }, // ImGuiDataType_S16 + { sizeof(unsigned short), "%u", "%u" }, + { sizeof(int), "%d", "%d" }, // ImGuiDataType_S32 + { sizeof(unsigned int), "%u", "%u" }, #ifdef _MSC_VER - { sizeof(ImS64), "%I64d","%I64d" }, - { sizeof(ImU64), "%I64u","%I64u" }, + { sizeof(ImS64), "%I64d","%I64d" }, // ImGuiDataType_S64 + { sizeof(ImU64), "%I64u","%I64u" }, #else - { sizeof(ImS64), "%lld", "%lld" }, - { sizeof(ImU64), "%llu", "%llu" }, + { sizeof(ImS64), "%lld", "%lld" }, // ImGuiDataType_S64 + { sizeof(ImU64), "%llu", "%llu" }, #endif - { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg - { sizeof(double), "%f", "%lf" }, + { sizeof(float), "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) + { sizeof(double), "%f", "%lf" }, // ImGuiDataType_Double }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); @@ -1536,47 +1548,71 @@ static const char* PatchFormatStringFloatToInt(const char* fmt) static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) { - if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument + // Signedness doesn't matter when pushing integer arguments + if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr); - if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) // Signedness doesn't matter when pushing the argument + if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr); if (data_type == ImGuiDataType_Float) return ImFormatString(buf, buf_size, format, *(const float*)data_ptr); if (data_type == ImGuiDataType_Double) return ImFormatString(buf, buf_size, format, *(const double*)data_ptr); + if (data_type == ImGuiDataType_S8) + return ImFormatString(buf, buf_size, format, *(const ImS8*)data_ptr); + if (data_type == ImGuiDataType_U8) + return ImFormatString(buf, buf_size, format, *(const ImU8*)data_ptr); + if (data_type == ImGuiDataType_S16) + return ImFormatString(buf, buf_size, format, *(const ImS16*)data_ptr); + if (data_type == ImGuiDataType_U16) + return ImFormatString(buf, buf_size, format, *(const ImU16*)data_ptr); IM_ASSERT(0); return 0; } -// FIXME: Adding support for clamping on boundaries of the data type would be nice. static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) { IM_ASSERT(op == '+' || op == '-'); switch (data_type) { + case ImGuiDataType_S8: + if (op == '+') { *(ImS8*)output = ImAddClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + if (op == '-') { *(ImS8*)output = ImSubClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + return; + case ImGuiDataType_U8: + if (op == '+') { *(ImU8*)output = ImAddClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + if (op == '-') { *(ImU8*)output = ImSubClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + return; + case ImGuiDataType_S16: + if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + return; + case ImGuiDataType_U16: + if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + return; case ImGuiDataType_S32: - if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2; - else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2; + if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } + if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } return; case ImGuiDataType_U32: - if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2; - else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2; + if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } + if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } return; case ImGuiDataType_S64: - if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2; - else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2; + if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } + if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } return; case ImGuiDataType_U64: - if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2; - else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2; + if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } + if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } return; case ImGuiDataType_Float: - if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2; - else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2; + if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; } + if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; } return; case ImGuiDataType_Double: - if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2; - else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2; + if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; } + if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; } return; case ImGuiDataType_COUNT: break; } @@ -1615,6 +1651,7 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b if (format == NULL) format = GDataTypeInfo[data_type].ScanFmt; + // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. int arg1i = 0; if (data_type == ImGuiDataType_S32) { @@ -1629,12 +1666,6 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant } - else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) - { - // Assign constant - // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future. - sscanf(buf, format, data_ptr); - } else if (data_type == ImGuiDataType_Float) { // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in @@ -1664,6 +1695,29 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide else { *v = arg1f; } // Assign constant } + else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) + { + // All other types assign constant + // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. + sscanf(buf, format, data_ptr); + } + else + { + // Small types need a 32-bit buffer to receive the result from scanf() + int v32; + sscanf(buf, format, &v32); + if (data_type == ImGuiDataType_S8) + *(ImS8*)data_ptr = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); + else if (data_type == ImGuiDataType_U8) + *(ImU8*)data_ptr = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX); + else if (data_type == ImGuiDataType_S16) + *(ImS16*)data_ptr = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX); + else if (data_type == ImGuiDataType_U16) + *(ImU16*)data_ptr = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX); + else + IM_ASSERT(0); + } + return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0; } @@ -1846,6 +1900,10 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_s switch (data_type) { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, v_min ? *(const ImS8*) v_min : IM_S8_MIN, v_max ? *(const ImS8*)v_max : IM_S8_MAX, format, power, flags); if (r) *(ImS8*)v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, v_min ? *(const ImU8*) v_min : IM_U8_MIN, v_max ? *(const ImU8*)v_max : IM_U8_MAX, format, power, flags); if (r) *(ImU8*)v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, v_min ? *(const ImS16*)v_min : IM_S16_MIN, v_max ? *(const ImS16*)v_max : IM_S16_MAX, format, power, flags); if (r) *(ImS16*)v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, v_min ? *(const ImU16*)v_min : IM_U16_MIN, v_max ? *(const ImU16*)v_max : IM_U16_MAX, format, power, flags); if (r) *(ImU16*)v = (ImU16)v32; return r; } case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power, flags); case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power, flags); case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power, flags); @@ -1892,14 +1950,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa // Tabbing or CTRL-clicking on Drag turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + const bool focus_requested = FocusableItemRegister(window, id); + if (focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) + if (focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0; @@ -1957,7 +2015,7 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -2000,7 +2058,7 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); return value_changed; @@ -2045,7 +2103,7 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); @@ -2270,6 +2328,10 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type { switch (data_type) { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)v_min, *(const ImS8*)v_max, format, power, flags, out_grab_bb); if (r) *(ImS8*)v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)v_min, *(const ImU8*)v_max, format, power, flags, out_grab_bb); if (r) *(ImU8*)v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)v_min, *(const ImS16*)v_max, format, power, flags, out_grab_bb); if (r) *(ImS16*)v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)v_min, *(const ImU16*)v_max, format, power, flags, out_grab_bb); if (r) *(ImU16*)v = (ImU16)v32; return r; } case ImGuiDataType_S32: IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2); return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags, out_grab_bb); @@ -2323,15 +2385,15 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co // Tabbing or CTRL-clicking on Slider turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); + const bool focus_requested = FocusableItemRegister(window, id); const bool hovered = ItemHoverable(frame_bb, id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + if (focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) + if (focus_requested || g.IO.KeyCtrl || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0; @@ -2394,7 +2456,7 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -2616,6 +2678,7 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) // FIXME: Facilitate using this in variety of other situations. bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) { + IM_UNUSED(id); ImGuiContext& g = *GImGui; // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id. @@ -2629,7 +2692,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format); ImStrTrimBlanks(data_buf); ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); - bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); + bool value_changed = InputTextEx(label, NULL, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); if (g.ScalarAsInputTextId == 0) { // First frame we started displaying the InputText widget, we expect it to take the active id. @@ -2637,7 +2700,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c g.ScalarAsInputTextId = g.ActiveId; } if (value_changed) - return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL); + return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, NULL); return false; } @@ -2648,7 +2711,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p return false; ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; + ImGuiStyle& style = g.Style; IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); if (format == NULL) @@ -2670,10 +2733,12 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p PushID(label); PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, format); PopItemWidth(); // Step buttons + const ImVec2 backup_frame_padding = style.FramePadding; + style.FramePadding.x = style.FramePadding.y; ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; if (flags & ImGuiInputTextFlags_ReadOnly) button_flags |= ImGuiButtonFlags_Disabled; @@ -2690,7 +2755,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p value_changed = true; } SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); + style.FramePadding = backup_frame_padding; PopID(); EndGroup(); @@ -2698,7 +2764,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p else { if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, format); } return value_changed; @@ -2727,7 +2793,7 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, in } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -2817,9 +2883,10 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f } //------------------------------------------------------------------------- -// [SECTION] Widgets: InputText, InputTextMultiline +// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint //------------------------------------------------------------------------- // - InputText() +// - InputTextWithHint() // - InputTextMultiline() // - InputTextEx() [Internal] //------------------------------------------------------------------------- @@ -2827,12 +2894,18 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() - return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); + return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); } bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { - return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); + return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); +} + +bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); } static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) @@ -2895,7 +2968,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t } // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) -namespace ImGuiStb +namespace ImStb { static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } @@ -2998,7 +3071,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im void ImGuiInputTextState::OnKeyPressed(int key) { - stb_textedit_key(this, &StbState, key); + stb_textedit_key(this, &Stb, key); CursorFollow = true; CursorAnimReset(); } @@ -3042,10 +3115,10 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons ImGuiContext& g = *GImGui; ImGuiInputTextState* edit_state = &g.InputTextState; IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); - IM_ASSERT(Buf == edit_state->TempBuffer.Data); + IM_ASSERT(Buf == edit_state->TextA.Data); int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; - edit_state->TempBuffer.reserve(new_buf_size + 1); - Buf = edit_state->TempBuffer.Data; + edit_state->TextA.reserve(new_buf_size + 1); + Buf = edit_state->TextA.Data; BufSize = edit_state->BufCapacityA = new_buf_size; } @@ -3125,8 +3198,9 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. // - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. // - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h -// (FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) -bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) +// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are +// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) +bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -3139,8 +3213,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 ImGuiIO& io = g.IO; const ImGuiStyle& style = g.Style; + const bool RENDER_SELECTION_WHEN_INACTIVE = false; const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; - const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; + const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; @@ -3184,8 +3259,118 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (hovered) g.MouseCursor = ImGuiMouseCursor_TextInput; + // NB: we are only allowed to access 'edit_state' if we are the active widget. + ImGuiInputTextState* state = NULL; + if (g.InputTextState.ID == id) + state = &g.InputTextState; + + const bool focus_requested = FocusableItemRegister(window, id); + const bool focus_requested_by_code = focus_requested && (g.FocusRequestCurrWindow == window && g.FocusRequestCurrCounterAll == window->DC.FocusCounterAll); + const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + + const bool user_clicked = hovered && io.MouseClicked[0]; + const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); + const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetScrollbarID(draw_window, ImGuiAxis_Y); + const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetScrollbarID(draw_window, ImGuiAxis_Y); + + bool clear_active_id = false; + bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + + const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start); + const bool init_state = (init_make_active || user_scroll_active); + if (init_state && g.ActiveId != id) + { + // Access state even if we don't own it yet. + state = &g.InputTextState; + state->CursorAnimReset(); + + // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) + const int buf_len = (int)strlen(buf); + state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. + memcpy(state->InitialTextA.Data, buf, buf_len + 1); + + // Start edition + const char* buf_end = NULL; + state->TextW.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string. + state->TextA.resize(0); + state->TextAIsValid = false; // TextA is not valid yet (we will display buf until then) + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end); + state->CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + + // Preserve cursor position and undo/redo stack if we come back to same widget + // FIXME: For non-readonly widgets we might be able to require that TextAIsValid && TextA == buf ? (untested) and discard undo stack if user buffer has changed. + const bool recycle_state = (state->ID == id); + if (recycle_state) + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + state->CursorClamp(); + } + else + { + state->ID = id; + state->ScrollX = 0.0f; + stb_textedit_initialize_state(&state->Stb, !is_multiline); + if (!is_multiline && focus_requested_by_code) + select_all = true; + } + if (flags & ImGuiInputTextFlags_AlwaysInsertMode) + state->Stb.insert_mode = 1; + if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + select_all = true; + } + + if (g.ActiveId != id && init_make_active) + { + IM_ASSERT(state && state->ID == id); + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + IM_ASSERT(ImGuiNavInput_COUNT < 32); + g.ActiveIdBlockNavInputFlags = (1 << ImGuiNavInput_Cancel); + if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out + g.ActiveIdBlockNavInputFlags |= (1 << ImGuiNavInput_KeyTab_); + if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) + g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); + } + + // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) + if (g.ActiveId == id && state == NULL) + ClearActiveID(); + + // Release focus when we click outside + if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560 + clear_active_id = true; + + // When read-only we always use the live data passed to the function + // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :( + if (is_readonly && state != NULL) + { + const bool will_render_cursor = (g.ActiveId == id) || (user_scroll_active); + const bool will_render_selection = state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || will_render_cursor); + if (will_render_cursor || will_render_selection) + { + const char* buf_end = NULL; + state->TextW.resize(buf_size + 1); + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, buf, NULL, &buf_end); + state->CurLenA = (int)(buf_end - buf); + state->CursorClamp(); + } + } + + // Lock the decision of whether we are going to take the path displaying the cursor or selection + const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); + const bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool value_changed = false; + bool enter_pressed = false; + + // Select the buffer to render. + const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid; + const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); + // Password pushes a temporary font with only a fallback glyph - if (is_password) + if (is_password && !is_displaying_hint) { const ImFontGlyph* glyph = g.Font->FindGlyph('*'); ImFont* password_font = &g.InputTextPasswordFont; @@ -3201,93 +3386,16 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 PushFont(password_font); } - // NB: we are only allowed to access 'edit_state' if we are the active widget. - ImGuiInputTextState& edit_state = g.InputTextState; - - const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing - const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; - - const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); - - bool clear_active_id = false; - - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); - if (focus_requested || user_clicked || user_scrolled || user_nav_input_start) - { - if (g.ActiveId != id) - { - // Start edition - // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) - // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) - const int prev_len_w = edit_state.CurLenW; - const int init_buf_len = (int)strlen(buf); - edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - edit_state.InitialText.resize(init_buf_len + 1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. - edit_state.CursorAnimReset(); - - // Preserve cursor position and undo/redo stack if we come back to same widget - // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). - const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW); - if (recycle_state) - { - // Recycle existing cursor/selection/undo stack but clamp position - // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. - edit_state.CursorClamp(); - } - else - { - edit_state.ID = id; - edit_state.ScrollX = 0.0f; - stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); - if (!is_multiline && focus_requested_by_code) - select_all = true; - } - if (flags & ImGuiInputTextFlags_AlwaysInsertMode) - edit_state.StbState.insert_mode = 1; - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) - select_all = true; - } - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdBlockNavInputFlags = (1 << ImGuiNavInput_Cancel); - if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) - g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); - } - else if (io.MouseClicked[0]) - { - // Release focus when we click outside - clear_active_id = true; - } - - bool value_changed = false; - bool enter_pressed = false; + // Process mouse inputs and character inputs int backup_current_text_length = 0; - if (g.ActiveId == id) { - if (!is_editable && !g.ActiveIdIsJustActivated) - { - // When read-only we always use the live data passed to the function - edit_state.TextW.resize(buf_size+1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); - edit_state.CursorClamp(); - } - - backup_current_text_length = edit_state.CurLenA; - edit_state.BufCapacityA = buf_size; - edit_state.UserFlags = flags; - edit_state.UserCallback = callback; - edit_state.UserCallbackData = callback_user_data; + IM_ASSERT(state != NULL); + backup_current_text_length = state->CurLenA; + state->BufCapacityA = buf_size; + state->UserFlags = flags; + state->UserCallback = callback; + state->UserCallbackData = callback_user_data; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -3295,50 +3403,50 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 g.WantTextInputNextFrame = 1; // Edit in progress - const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX; const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) { - edit_state.SelectAll(); - edit_state.SelectedAllMouseLock = true; + state->SelectAll(); + state->SelectedAllMouseLock = true; } else if (hovered && is_osx && io.MouseDoubleClicked[0]) { // Double-click select a word only, OS X style (by simulating keystrokes) - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); } - else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) + else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { if (hovered) { - stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); } } - else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) + else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) { - stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - edit_state.CursorFollow = true; + stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); + state->CursorFollow = true; } - if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) - edit_state.SelectedAllMouseLock = false; + if (state->SelectedAllMouseLock && !io.MouseDown[0]) + state->SelectedAllMouseLock = false; if (io.InputQueueCharacters.Size > 0) { // Process text input (before we check for Return because using some IME will effectively send a Return?) // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); - if (!ignore_inputs && is_editable && !user_nav_input_start) + if (!ignore_inputs && !is_readonly && !user_nav_input_start) for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering unsigned int c = (unsigned int)io.InputQueueCharacters[n]; if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); + state->OnKeyPressed((int)c); } // Consume characters @@ -3346,10 +3454,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } + // Process other shortcuts/key-presses bool cancel_edit = false; if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) { - // Handle key-presses + IM_ASSERT(state != NULL); const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_osx = io.ConfigMacOSXBehaviors; const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl @@ -3359,27 +3468,29 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; - const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable; - const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable; + const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_readonly; + const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); + const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; - if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) { - if (!edit_state.HasSelection()) + if (!state->HasSelection()) { - if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); - else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); + if (is_wordmove_key_down) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); + else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); } - edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); + state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Enter)) { @@ -3388,18 +3499,18 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { enter_pressed = clear_active_id = true; } - else if (is_editable) + else if (!is_readonly) { unsigned int c = '\n'; // Insert new line if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); + state->OnKeyPressed((int)c); } } - else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) + else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !is_readonly) { unsigned int c = '\t'; // Insert TAB if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); + state->OnKeyPressed((int)c); } else if (IsKeyPressedMap(ImGuiKey_Escape)) { @@ -3407,31 +3518,33 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } else if (is_undo || is_redo) { - edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); - edit_state.ClearSelection(); + state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); + state->ClearSelection(); } else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) { - edit_state.SelectAll(); - edit_state.CursorFollow = true; + state->SelectAll(); + state->CursorFollow = true; } else if (is_cut || is_copy) { // Cut, Copy if (io.SetClipboardTextFn) { - const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; - const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; - edit_state.TempBuffer.resize((ie-ib) * 4 + 1); - ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie); - SetClipboardText(edit_state.TempBuffer.Data); + const int ib = state->HasSelection() ? ImMin(state->Stb.select_start, state->Stb.select_end) : 0; + const int ie = state->HasSelection() ? ImMax(state->Stb.select_start, state->Stb.select_end) : state->CurLenW; + const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1; + char* clipboard_data = (char*)MemAlloc(clipboard_data_len * sizeof(char)); + ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie); + SetClipboardText(clipboard_data); + MemFree(clipboard_data); } if (is_cut) { - if (!edit_state.HasSelection()) - edit_state.SelectAll(); - edit_state.CursorFollow = true; - stb_textedit_cut(&edit_state, &edit_state.StbState); + if (!state->HasSelection()) + state->SelectAll(); + state->CursorFollow = true; + stb_textedit_cut(state, &state->Stb); } } else if (is_paste) @@ -3455,25 +3568,27 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 clipboard_filtered[clipboard_filtered_len] = 0; if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation { - stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); - edit_state.CursorFollow = true; + stb_textedit_paste(state, &state->Stb, clipboard_filtered, clipboard_filtered_len); + state->CursorFollow = true; } MemFree(clipboard_filtered); } } } + // Process callbacks and apply result back to user's buffer. if (g.ActiveId == id) { + IM_ASSERT(state != NULL); const char* apply_new_text = NULL; int apply_new_text_length = 0; if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0) + if (!is_readonly && strcmp(buf, state->InitialTextA.Data) != 0) { - apply_new_text = edit_state.InitialText.Data; - apply_new_text_length = edit_state.InitialText.Size - 1; + apply_new_text = state->InitialTextA.Data; + apply_new_text_length = state->InitialTextA.Size - 1; } } @@ -3486,10 +3601,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. - if (is_editable) + if (!is_readonly) { - edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1); - ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL); + state->TextAIsValid = true; + state->TextA.resize(state->TextW.Size * 4 + 1); + ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL); } // User callback @@ -3527,44 +3643,44 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 callback_data.UserData = callback_user_data; callback_data.EventKey = event_key; - callback_data.Buf = edit_state.TempBuffer.Data; - callback_data.BufTextLen = edit_state.CurLenA; - callback_data.BufSize = edit_state.BufCapacityA; + callback_data.Buf = state->TextA.Data; + callback_data.BufTextLen = state->CurLenA; + callback_data.BufSize = state->BufCapacityA; callback_data.BufDirty = false; // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) - ImWchar* text = edit_state.TextW.Data; - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); + ImWchar* text = state->TextW.Data; + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_end); // Call user code callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA); + IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); - if (callback_data.CursorPos != utf8_cursor_pos) { edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); edit_state.CursorFollow = true; } - if (callback_data.SelectionStart != utf8_selection_start) { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } - if (callback_data.SelectionEnd != utf8_selection_end) { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } + if (callback_data.CursorPos != utf8_cursor_pos) { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start) { state->Stb.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } + if (callback_data.SelectionEnd != utf8_selection_end) { state->Stb.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } if (callback_data.BufDirty) { IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! if (callback_data.BufTextLen > backup_current_text_length && is_resizable) - edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL); - edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() - edit_state.CursorAnimReset(); + state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL); + state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + state->CursorAnimReset(); } } } // Will copy result string if modified - if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0) + if (!is_readonly && strcmp(state->TextA.Data, buf) != 0) { - apply_new_text = edit_state.TempBuffer.Data; - apply_new_text_length = edit_state.CurLenA; + apply_new_text = state->TextA.Data; + apply_new_text_length = state->CurLenA; } } @@ -3594,24 +3710,16 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } // Clear temporary user storage - edit_state.UserFlags = 0; - edit_state.UserCallback = NULL; - edit_state.UserCallbackData = NULL; + state->UserFlags = 0; + state->UserCallback = NULL; + state->UserCallbackData = NULL; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) if (clear_active_id && g.ActiveId == id) ClearActiveID(); - // Render - // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. - const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL; - - // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line - // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. - // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. - const int buf_display_max_length = 2 * 1024 * 1024; - + // Render frame if (!is_multiline) { RenderNavHighlight(frame_bb, id); @@ -3619,33 +3727,54 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size - ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; - ImVec2 text_size(0.f, 0.f); - const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); - if (g.ActiveId == id || is_currently_scrolling) - { - edit_state.CursorAnim += io.DeltaTime; + ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; + ImVec2 text_size(0.0f, 0.0f); + // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line + // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. + // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. + const int buf_display_max_length = 2 * 1024 * 1024; + const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595 + const char* buf_display_end = NULL; // We have specialized paths below for setting the length + if (is_displaying_hint) + { + buf_display = hint; + buf_display_end = hint + strlen(hint); + } + + // Render text. We currently only render selection when the widget is active or while scrolling. + // FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive. + if (render_cursor || render_selection) + { + IM_ASSERT(state != NULL); + if (!is_displaying_hint) + buf_display_end = buf_display + state->CurLenA; + + // Render text (with cursor and selection) // This is going to be messy. We need to: // - Display the text (this alone can be more easily clipped) // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) // - Measure text height (for scrollbar) // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const ImWchar* text_begin = edit_state.TextW.Data; + const ImWchar* text_begin = state->TextW.Data; ImVec2 cursor_offset, select_start_offset; { - // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. - const ImWchar* searches_input_ptr[2]; - searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; - searches_input_ptr[1] = NULL; - int searches_remaining = 1; - int searches_result_line_number[2] = { -1, -999 }; - if (edit_state.StbState.select_start != edit_state.StbState.select_end) + // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions. + const ImWchar* searches_input_ptr[2] = { NULL, NULL }; + int searches_result_line_no[2] = { -1000, -1000 }; + int searches_remaining = 0; + if (render_cursor) { - searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - searches_result_line_number[1] = -1; + searches_input_ptr[0] = text_begin + state->Stb.cursor; + searches_result_line_no[0] = -1; + searches_remaining++; + } + if (render_selection) + { + searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + searches_result_line_no[1] = -1; searches_remaining++; } @@ -3658,20 +3787,22 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (*s == '\n') { line_count++; - if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } - if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; } } line_count++; - if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; - if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; + if (searches_result_line_no[0] == -1) + searches_result_line_no[0] = line_count; + if (searches_result_line_no[1] == -1) + searches_result_line_no[1] = line_count; // Calculate 2d position by finding the beginning of the line and measuring distance cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; - cursor_offset.y = searches_result_line_number[0] * g.FontSize; - if (searches_result_line_number[1] >= 0) + cursor_offset.y = searches_result_line_no[0] * g.FontSize; + if (searches_result_line_no[1] >= 0) { select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; - select_start_offset.y = searches_result_line_number[1] * g.FontSize; + select_start_offset.y = searches_result_line_no[1] * g.FontSize; } // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) @@ -3680,20 +3811,20 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } // Scroll - if (edit_state.CursorFollow) + if (render_cursor && state->CursorFollow) { // Horizontal scroll in chunks of quarter width if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) { const float scroll_increment_x = size.x * 0.25f; - if (cursor_offset.x < edit_state.ScrollX) - edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); - else if (cursor_offset.x - size.x >= edit_state.ScrollX) - edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); + if (cursor_offset.x < state->ScrollX) + state->ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); + else if (cursor_offset.x - size.x >= state->ScrollX) + state->ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); } else { - edit_state.ScrollX = 0.0f; + state->ScrollX = 0.0f; } // Vertical scroll @@ -3704,24 +3835,25 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); else if (cursor_offset.y - size.y >= scroll_y) scroll_y = cursor_offset.y - size.y; - draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag + draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag draw_window->Scroll.y = scroll_y; - render_pos.y = draw_window->DC.CursorPos.y; + draw_pos.y = draw_window->DC.CursorPos.y; } + + state->CursorFollow = false; } - edit_state.CursorFollow = false; - const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); // Draw selection - if (edit_state.StbState.select_start != edit_state.StbState.select_end) + const ImVec2 draw_scroll = ImVec2(state->ScrollX, 0.0f); + if (render_selection) { - const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); + const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + const ImWchar* text_selected_end = text_begin + ImMax(state->Stb.select_start, state->Stb.select_end); + ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. float bg_offy_dn = is_multiline ? 0.0f : 2.0f; - ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); - ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; + ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) { if (rect_pos.y > clip_rect.w + g.FontSize) @@ -3743,36 +3875,48 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (rect.Overlaps(clip_rect)) draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); } - rect_pos.x = render_pos.x - render_scroll.x; + rect_pos.x = draw_pos.x - draw_scroll.x; rect_pos.y += g.FontSize; } } - const int buf_display_len = edit_state.CurLenA; - if (is_multiline || buf_display_len < buf_display_max_length) - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect); + // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash. + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } // Draw blinking cursor - bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; - ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + if (render_cursor) + { + state->CursorAnim += io.DeltaTime; + bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); - // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) - if (is_editable) - g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); + // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) + if (!is_readonly) + g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + } } else { - // Render text only - const char* buf_end = NULL; + // Render text only (no selection, no cursor) if (is_multiline) - text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width - else - buf_end = buf_display + strlen(buf_display); - if (is_multiline || (buf_end - buf_display) < buf_display_max_length) - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); + text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width + else if (!is_displaying_hint && g.ActiveId == id) + buf_display_end = buf_display + state->CurLenA; + else if (!is_displaying_hint) + buf_display_end = buf_display + strlen(buf_display); + + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } } if (is_multiline) @@ -3782,12 +3926,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 EndGroup(); } - if (is_password) + if (is_password && !is_displaying_hint) PopFont(); // Log as text - if (g.LogEnabled && !is_password) - LogRenderedText(&render_pos, buf_display, NULL); + if (g.LogEnabled && !(is_password && !is_displaying_hint)) + LogRenderedText(&draw_pos, buf_display, buf_display_end); if (label_size.x > 0) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); @@ -3844,20 +3988,24 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // If we're not showing any slider there's no point in doing any HSV conversions const ImGuiColorEditFlags flags_untouched = flags; if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; + flags = (flags & (~ImGuiColorEditFlags__DisplayMask)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; // Context menu: display and modify options (before defaults are applied) if (!(flags & ImGuiColorEditFlags_NoOptions)) ColorEditOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__InputsMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); + if (!(flags & ImGuiColorEditFlags__DisplayMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DisplayMask); if (!(flags & ImGuiColorEditFlags__DataTypeMask)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); if (!(flags & ImGuiColorEditFlags__PickerMask)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); + if (!(flags & ImGuiColorEditFlags__InputMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputMask); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask)); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; @@ -3865,34 +4013,36 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Convert to the formats we need float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; - if (flags & ImGuiColorEditFlags_HSV) + if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB)) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV)) ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; bool value_changed = false; bool value_changed_as_float = false; - if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { // RGB/HSV 0..255 Sliders const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); - const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; - const char* fmt_table_int[3][4] = + static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + static const char* fmt_table_int[3][4] = { { "%3d", "%3d", "%3d", "%3d" }, // Short display { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA }; - const char* fmt_table_float[3][4] = + static const char* fmt_table_float[3][4] = { { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA }; - const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; + const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; PushItemWidth(w_item_one); for (int n = 0; n < components; n++) @@ -3916,7 +4066,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag PopItemWidth(); PopItemWidth(); } - else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { // RGB Hexadecimal Input char buf[64]; @@ -3967,11 +4117,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag picker_active_window = g.CurrentWindow; if (label != label_display_end) { - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); Spacing(); } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); PopItemWidth(); @@ -3982,25 +4132,25 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) { SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); } // Convert back - if (picker_active_window == NULL) + if (value_changed && picker_active_window == NULL) { if (!value_changed_as_float) for (int n = 0; n < 4; n++) f[n] = i[n] / 255.0f; - if (flags & ImGuiColorEditFlags_HSV) + if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB)) ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - if (value_changed) - { - col[0] = f[0]; - col[1] = f[1]; - col[2] = f[2]; - if (alpha) - col[3] = f[3]; - } + if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; } PopID(); @@ -4010,16 +4160,21 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) { + bool accepted_drag_drop = false; if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) { memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 - value_changed = true; + value_changed = accepted_drag_drop = true; } if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) { memcpy((float*)col, payload->Data, sizeof(float) * components); - value_changed = true; + value_changed = accepted_drag_drop = true; } + + // Drag-drop payloads are always RGB + if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]); EndDragDropTarget(); } @@ -4098,13 +4253,16 @@ static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 } // Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.) // FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - ImDrawList* draw_list = window->DrawList; + if (window->SkipItems) + return false; + ImDrawList* draw_list = window->DrawList; ImGuiStyle& style = g.Style; ImGuiIO& io = g.IO; @@ -4121,7 +4279,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // Read stored options if (!(flags & ImGuiColorEditFlags__PickerMask)) flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags__InputMask)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__InputMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__InputMask; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected if (!(flags & ImGuiColorEditFlags_NoOptions)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); @@ -4150,8 +4311,12 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. - float H,S,V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); + float H = col[0], S = col[1], V = col[2]; + float R = col[0], G = col[1], B = col[2]; + if (flags & ImGuiColorEditFlags_InputRGB) + ColorConvertRGBtoHSV(R, G, B, H, S, V); + else if (flags & ImGuiColorEditFlags_InputHSV) + ColorConvertHSVtoRGB(H, S, V, R, G, B); bool value_changed = false, value_changed_h = false, value_changed_sv = false; @@ -4240,7 +4405,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if ((flags & ImGuiColorEditFlags_NoSidePreview)) SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); } } @@ -4250,12 +4415,14 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); - ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); + + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); if (ref_col != NULL) { Text("Original"); ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); - if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) + if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))) { memcpy(col, ref_col, components * sizeof(float)); value_changed = true; @@ -4267,32 +4434,43 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // Convert back color to RGB if (value_changed_h || value_changed_sv) - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + col[0] = H; + col[1] = S; + col[2] = V; + } + } // R,G,B and H,S,V slider color editor bool value_changed_fix_hue_wrap = false; if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) - if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB)) + if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) { // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); value_changed = true; } - if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); - if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); + if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); + if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); PopItemWidth(); } // Try to cancel hue wrap (after ColorEdit4 call), if any - if (value_changed_fix_hue_wrap) + if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB)) { float new_H, new_S, new_V; ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); @@ -4305,9 +4483,27 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl } } + if (value_changed) + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + R = col[0]; + G = col[1]; + B = col[2]; + ColorConvertRGBtoHSV(R, G, B, H, S, V); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + H = col[0]; + S = col[1]; + V = col[2]; + ColorConvertHSVtoRGB(H, S, V, R, G, B); + } + } + ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); - ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); + ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, 1.0f)); const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; ImVec2 sv_cursor_pos; @@ -4407,6 +4603,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // A little colored square. Return true when clicked. // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. +// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set. bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) { ImGuiWindow* window = GetCurrentWindow(); @@ -4431,22 +4628,26 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if (flags & ImGuiColorEditFlags_NoAlpha) flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); - ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); + ImVec4 col_rgb = col; + if (flags & ImGuiColorEditFlags_InputHSV) + ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z); + + ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f); float grid_step = ImMin(size.x, size.y) / 2.99f; float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); ImRect bb_inner = bb; float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. bb_inner.Expand(off); - if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) + if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) { float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); - RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); + RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); + window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); } else { // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha; + ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; if (col_source.w < 1.0f) RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); else @@ -4463,18 +4664,18 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) { if (flags & ImGuiColorEditFlags_NoAlpha) - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once); + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once); else - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once); ColorButton(desc_id, col, flags); SameLine(); - TextUnformatted("Color"); + TextEx("Color"); EndDragDropSource(); } // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); if (pressed) MarkItemEdited(id); @@ -4482,18 +4683,22 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl return pressed; } +// Initialize/override default color options void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__InputsMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; + if ((flags & ImGuiColorEditFlags__DisplayMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DisplayMask; if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; if ((flags & ImGuiColorEditFlags__PickerMask) == 0) flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected + if ((flags & ImGuiColorEditFlags__InputMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputMask; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DataTypeMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check only 1 option is selected g.ColorEditOptions = flags; } @@ -4502,29 +4707,39 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); BeginTooltipEx(0, true); - const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { - TextUnformatted(text, text_end); + TextEx(text, text_end); Separator(); } ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); - ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); - if (flags & ImGuiColorEditFlags_NoAlpha) - Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); - else - Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); + if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags__InputMask)) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); + else + Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]); + else + Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]); + } EndTooltip(); } void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) { - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__DisplayMask); bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) return; @@ -4532,9 +4747,9 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) ImGuiColorEditFlags opts = g.ColorEditOptions; if (allow_opt_inputs) { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; - if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayRGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHSV; + if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHex; } if (allow_opt_datatype) { @@ -4750,7 +4965,7 @@ bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand) is_open = true; return is_open; @@ -4877,7 +5092,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l const char log_suffix[] = "##"; LogRenderedText(&text_pos, log_prefix, log_prefix+3); RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); + LogRenderedText(&text_pos, log_suffix, log_suffix+2); } else { @@ -5056,7 +5271,20 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl bb.Min.y -= spacing_U; bb.Max.x += spacing_R; bb.Max.y += spacing_D; - if (!ItemAdd(bb, id)) + + bool item_add; + if (flags & ImGuiSelectableFlags_Disabled) + { + ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; + window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; + item_add = ItemAdd(bb, id); + window->DC.ItemFlags = backup_item_flags; + } + else + { + item_add = ItemAdd(bb, id); + } + if (!item_add) { if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) PushColumnClipRect(); @@ -5289,7 +5517,8 @@ void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_ge RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - if (values_count > 0) + const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; + if (values_count >= values_count_min) { int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); @@ -5446,7 +5675,6 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) // Helpers for internal use ImGuiMenuColumns::ImGuiMenuColumns() { - Count = 0; Spacing = Width = NextWidth = 0.0f; memset(Pos, 0, sizeof(Pos)); memset(NextWidths, 0, sizeof(NextWidths)); @@ -5454,12 +5682,12 @@ ImGuiMenuColumns::ImGuiMenuColumns() void ImGuiMenuColumns::Update(int count, float spacing, bool clear) { - IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); - Count = count; + IM_ASSERT(count == IM_ARRAYSIZE(Pos)); Width = NextWidth = 0.0f; Spacing = spacing; - if (clear) memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < Count; i++) + if (clear) + memset(NextWidths, 0, sizeof(NextWidths)); + for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) { if (i > 0 && NextWidths[i] > 0.0f) Width += Spacing; @@ -5475,7 +5703,7 @@ float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using v NextWidths[0] = ImMax(NextWidths[0], w0); NextWidths[1] = ImMax(NextWidths[1], w1); NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < 3; i++) + for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); return ImMax(Width, NextWidth); } @@ -5836,7 +6064,7 @@ ImGuiTabBar::ImGuiTabBar() CurrFrameVisible = PrevFrameVisible = -1; ContentsHeight = 0.0f; OffsetMax = OffsetNextTab = 0.0f; - ScrollingAnim = ScrollingTarget = 0.0f; + ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f; Flags = ImGuiTabBarFlags_None; ReorderRequestTabId = 0; ReorderRequestDir = 0; @@ -5860,6 +6088,20 @@ static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs) return (b->Index - a->Index); } +static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiTabBarRef& ref) +{ + ImGuiContext& g = *GImGui; + return ref.Ptr ? ref.Ptr : g.TabBars.GetByIndex(ref.IndexInMainPool); +} + +static ImGuiTabBarRef GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + if (g.TabBars.Contains(tab_bar)) + return ImGuiTabBarRef(g.TabBars.GetIndex(tab_bar)); + return ImGuiTabBarRef(tab_bar); +} + bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) { ImGuiContext& g = *GImGui; @@ -5884,7 +6126,10 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG if ((flags & ImGuiTabBarFlags_DockNode) == 0) window->IDStack.push_back(tab_bar->ID); - g.CurrentTabBar.push_back(tab_bar); + // Add to stack + g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); + g.CurrentTabBar = tab_bar; + if (tab_bar->CurrFrameVisible == g.FrameCount) { //IMGUI_DEBUG_LOG("BeginTabBarEx already called this frame\n", g.FrameCount); @@ -5930,8 +6175,12 @@ void ImGui::EndTabBar() if (window->SkipItems) return; - IM_ASSERT(!g.CurrentTabBar.empty()); // Mismatched BeginTabBar/EndTabBar - ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar != NULL && "Mismatched BeginTabBar()/EndTabBar()!"); + return; // FIXME-ERRORHANDLING + } if (tab_bar->WantLayout) TabBarLayout(tab_bar); @@ -5944,7 +6193,9 @@ void ImGui::EndTabBar() if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) PopID(); - g.CurrentTabBar.pop_back(); + + g.CurrentTabBarStack.pop_back(); + g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); } // This is called only once a frame before by the first call to ItemTab() @@ -6107,9 +6358,19 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) TabBarScrollToTab(tab_bar, scroll_track_selected_tab); tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); - const float scrolling_speed = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) ? FLT_MAX : (g.IO.DeltaTime * g.FontSize * 70.0f); if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) - tab_bar->ScrollingAnim = ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, scrolling_speed); + { + // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds. + // Teleport if we are aiming far off the visible line + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize); + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f); + const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize); + tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed); + } + else + { + tab_bar->ScrollingSpeed = 0.0f; + } // Clear name buffers if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) @@ -6187,10 +6448,17 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) int order = tab_bar->GetTabOrder(tab); float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f); float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f); + tab_bar->ScrollingTargetDistToVisibility = 0.0f; if (tab_bar->ScrollingTarget > tab_x1) + { + tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f); tab_bar->ScrollingTarget = tab_x1; - if (tab_bar->ScrollingTarget + tab_bar->BarRect.GetWidth() < tab_x2) + } + else if (tab_bar->ScrollingTarget < tab_x2 - tab_bar->BarRect.GetWidth()) + { + tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - tab_bar->BarRect.GetWidth()) - tab_bar->ScrollingAnim, 0.0f); tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth(); + } } void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) @@ -6311,8 +6579,12 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f if (g.CurrentWindow->SkipItems) return false; - IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!"); - ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar && "Needs to be called between BeginTabBar() and EndTabBar()!"); + return false; // FIXME-ERRORHANDLING + } bool ret = TabItemEx(tab_bar, label, p_open, flags); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) { @@ -6328,9 +6600,13 @@ void ImGui::EndTabItem() if (g.CurrentWindow->SkipItems) return; - IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!"); - ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); - IM_ASSERT(tab_bar->LastTabItemIdx >= 0 && "Needs to be called between BeginTabItem() and EndTabItem()"); + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar != NULL && "Needs to be called between BeginTabBar() and EndTabBar()!"); + return; + } + IM_ASSERT(tab_bar->LastTabItemIdx >= 0); ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) g.CurrentWindow->IDStack.pop_back(); @@ -6401,6 +6677,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) tab_bar->NextSelectedTabId = id; // New tabs gets activated + if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // SetSelected can only be passed on explicit tab bar + tab_bar->NextSelectedTabId = id; // Lock visibility bool tab_contents_visible = (tab_bar->VisibleTabId == id); @@ -6452,9 +6730,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); - hovered |= (g.HoveredId == id); - if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar + if (pressed) tab_bar->NextSelectedTabId = id; + hovered |= (g.HoveredId == id); // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) if (!held) @@ -6484,7 +6762,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, { // Enlarge tab display when hovering bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)); - display_draw_list = GetOverlayDrawList(window); + display_draw_list = GetForegroundDrawList(window); TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); } #endif @@ -6518,7 +6796,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, window->DC.CursorPos = backup_main_cursor_pos; // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) - if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f) + // We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar (which g.HoveredId ignores) + if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f && IsItemHovered()) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip)) SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); @@ -6530,10 +6809,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, void ImGui::SetTabItemClosed(const char* label) { ImGuiContext& g = *GImGui; - bool is_within_manual_tab_bar = (g.CurrentTabBar.Size > 0) && !(g.CurrentTabBar.back()->Flags & ImGuiTabBarFlags_DockNode); + bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode); if (is_within_manual_tab_bar) { - ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); + ImGuiTabBar* tab_bar = g.CurrentTabBar; IM_ASSERT(tab_bar->WantLayout); // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem() ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); TabBarRemoveTab(tab_bar, tab_id); diff --git a/profiler/src/imgui_freetype.h b/profiler/src/imgui_freetype.h index 5fa0dd5c..9df5780e 100644 --- a/profiler/src/imgui_freetype.h +++ b/profiler/src/imgui_freetype.h @@ -4,7 +4,7 @@ #pragma once -#include "../imgui/imgui.h" // IMGUI_API, ImFontAtlas +#include "imgui.h" // IMGUI_API, ImFontAtlas namespace ImGuiFreeType { diff --git a/profiler/src/imgui_impl_glfw.cpp b/profiler/src/imgui_impl_glfw.cpp index d5c0b13b..0ed40cde 100644 --- a/profiler/src/imgui_impl_glfw.cpp +++ b/profiler/src/imgui_impl_glfw.cpp @@ -15,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. @@ -313,7 +314,8 @@ void ImGui_ImplGlfw_NewFrame() glfwGetWindowSize(g_Window, &w, &h); glfwGetFramebufferSize(g_Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); // Setup time step double current_time = glfwGetTime(); diff --git a/profiler/src/imgui_impl_opengl3.cpp b/profiler/src/imgui_impl_opengl3.cpp index bd8c94e6..84b87775 100644 --- a/profiler/src/imgui_impl_opengl3.cpp +++ b/profiler/src/imgui_impl_opengl3.cpp @@ -1,6 +1,7 @@ -// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) +// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) -// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. @@ -11,10 +12,12 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0). +// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader. // 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. // 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450). // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT). +// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN. // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". // 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. @@ -38,17 +41,17 @@ // version version string //---------------------------------------- // 2.0 110 "#version 110" -// 2.1 120 -// 3.0 130 -// 3.1 140 +// 2.1 120 "#version 120" +// 3.0 130 "#version 130" +// 3.1 140 "#version 140" // 3.2 150 "#version 150" -// 3.3 330 -// 4.0 400 +// 3.3 330 "#version 330 core" +// 4.0 400 "#version 400 core" // 4.1 410 "#version 410 core" -// 4.2 420 -// 4.3 430 -// ES 2.0 100 "#version 100" -// ES 3.0 300 "#version 300 es" +// 4.2 420 "#version 410 core" +// 4.3 430 "#version 430 core" +// ES 2.0 100 "#version 100" = WebGL 1.0 +// ES 3.0 300 "#version 300 es" = WebGL 2.0 //---------------------------------------- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) @@ -67,20 +70,24 @@ #include "TargetConditionals.h" #endif -// iOS, Android and Emscripten can use GL ES 3 -// Call ImGui_ImplOpenGL3_Init() with "#version 300 es" -#if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) || (defined(__EMSCRIPTEN__)) -#define USE_GL_ES3 +// Auto-detect GL version +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) +#if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) +#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" +#elif defined(__EMSCRIPTEN__) +#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" +#endif #endif -#ifdef USE_GL_ES3 -// OpenGL ES 3 +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +#elif defined(IMGUI_IMPL_OPENGL_ES3) #include // Use GL ES 3 #else -// Regular OpenGL -// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. -// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. -// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +// About Desktop OpenGL function loaders: +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) #include #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) @@ -107,7 +114,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) io.BackendRendererName = "imgui_impl_opengl3"; // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. -#ifdef USE_GL_ES3 +#if defined(IMGUI_IMPL_OPENGL_ES2) + if (glsl_version == NULL) + glsl_version = "#version 100"; +#elif defined(IMGUI_IMPL_OPENGL_ES3) if (glsl_version == NULL) glsl_version = "#version 300 es"; #else @@ -152,7 +162,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); #endif GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#endif #ifdef GL_POLYGON_MODE GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); #endif @@ -169,7 +181,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); bool clip_origin_lower_left = true; -#ifdef GL_CLIP_ORIGIN +#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__) GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) if (last_clip_origin == GL_UPPER_LEFT) clip_origin_lower_left = false; @@ -206,11 +218,14 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) #ifdef GL_SAMPLER_BINDING glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. #endif + +#ifndef IMGUI_IMPL_OPENGL_ES2 // Recreate the VAO every time // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) GLuint vao_handle = 0; glGenVertexArrays(1, &vao_handle); glBindVertexArray(vao_handle); +#endif glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glEnableVertexAttribArray(g_AttribLocationPosition); glEnableVertexAttribArray(g_AttribLocationUV); @@ -268,7 +283,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) idx_buffer_offset += pcmd->ElemCount * sizeof(ImDrawIdx); } } +#ifndef IMGUI_IMPL_OPENGL_ES2 glDeleteVertexArrays(1, &vao_handle); +#endif // Restore modified GL state glUseProgram(last_program); @@ -277,7 +294,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glBindSampler(0, last_sampler); #endif glActiveTexture(last_active_texture); +#ifndef IMGUI_IMPL_OPENGL_ES2 glBindVertexArray(last_vertex_array); +#endif glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); @@ -307,7 +326,9 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() glBindTexture(GL_TEXTURE_2D, g_FontTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#ifdef GL_UNPACK_ROW_LENGTH glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier @@ -369,10 +390,13 @@ static bool CheckProgram(GLuint handle, const char* desc) bool ImGui_ImplOpenGL3_CreateDeviceObjects() { // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; + GLint last_texture, last_array_buffer; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#endif // Parse GLSL version string int glsl_version = 130; @@ -536,7 +560,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 glBindVertexArray(last_vertex_array); +#endif return true; } diff --git a/profiler/src/imgui_impl_opengl3.h b/profiler/src/imgui_impl_opengl3.h index 0be9dcd8..9fe2a88d 100644 --- a/profiler/src/imgui_impl_opengl3.h +++ b/profiler/src/imgui_impl_opengl3.h @@ -1,6 +1,7 @@ -// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) +// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) -// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. @@ -9,19 +10,23 @@ // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. // https://github.com/ocornut/imgui -// About OpenGL function loaders: -// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. -// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. -// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +// About Desktop OpenGL function loaders: +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. // About GLSL version: -// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. -// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" -// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. +// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. +// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" +// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. #pragma once -// Set default OpenGL loader to be gl3w +// Specific OpenGL versions +//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten +//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android + +// Set default OpenGL3 loader to be gl3w #if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \