diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 48082d056..b223d2261 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -123,6 +123,11 @@ jobs: run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release' if: github.event_name == 'workflow_run' + - name: Build Win32 example_sdl2_sdlrenderer2 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=Win32 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + - name: Build Win32 example_sdl2_vulkan shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release' @@ -168,6 +173,11 @@ jobs: shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' + - name: Build x64 example_sdl2_sdlrenderer2 + shell: cmd + run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=x64 /p:Configuration=Release' + if: github.event_name == 'workflow_run' + - name: Build x64 example_sdl2_vulkan shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 4b2df7747..f0c8f8aa8 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -26,6 +26,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2024-08-19: Storing SDL's Uint32 WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. +// 2024-08-19: ImGui_ImplSDL2_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode(). @@ -125,6 +127,7 @@ static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; struct ImGui_ImplSDL2_Data { SDL_Window* Window; + Uint32 WindowID; SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; @@ -329,6 +332,12 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0); } +static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id) +{ + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. @@ -344,6 +353,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { case SDL_MOUSEMOTION: { + if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == NULL) + return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { @@ -358,6 +369,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_MOUSEWHEEL: { + if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == NULL) + return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); #if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten! float wheel_x = -event->wheel.preciseX; @@ -376,6 +389,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { + if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == NULL) + return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } @@ -391,12 +406,16 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_TEXTINPUT: { + if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == NULL) + return false; io.AddInputCharactersUTF8(event->text.text); return true; } case SDL_KEYDOWN: case SDL_KEYUP: { + if (ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID) == NULL) + return false; ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod); ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode); io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); @@ -414,6 +433,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) #endif case SDL_WINDOWEVENT: { + if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == NULL) + return false; // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. // - However we won't get a correct LEAVE event for a captured window. // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, @@ -485,6 +506,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) bd->Window = window; + bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) @@ -523,7 +545,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void // Set platform dependent data in viewport // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->PlatformHandle = (void*)window; + main_viewport->PlatformHandle = (void*)(intptr_t)bd->WindowID; main_viewport->PlatformHandleRaw = nullptr; SDL_SysWMinfo info; SDL_VERSION(&info.version); diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 0d12e6fac..81110d8b4 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -26,6 +26,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. +// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853) // 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) // 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) // 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) @@ -85,6 +87,7 @@ struct ImGui_ImplSDL3_Data { SDL_Window* Window; + SDL_WindowID WindowID; SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; @@ -144,7 +147,8 @@ static void ImGui_ImplSDL3_SetClipboardText(void*, const char* text) static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); - SDL_Window* window = (SDL_Window*)viewport->PlatformHandle; + SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; + SDL_Window* window = SDL_GetWindowFromID(window_id); if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != NULL) { SDL_StopTextInput(bd->ImeWindow); @@ -305,6 +309,13 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); } + +static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL; +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. @@ -320,6 +331,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { case SDL_EVENT_MOUSE_MOTION: { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == NULL) + return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { @@ -334,6 +347,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_MOUSE_WHEEL: { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == NULL) + return false; //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); float wheel_x = -event->wheel.x; float wheel_y = event->wheel.y; @@ -347,6 +362,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == NULL) + return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } @@ -362,12 +379,16 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_TEXT_INPUT: { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == NULL) + return false; io.AddInputCharactersUTF8(event->text.text); return true; } case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == NULL) + return false; //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); @@ -386,6 +407,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) } case SDL_EVENT_WINDOW_MOUSE_ENTER: { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) + return false; bd->MouseWindowID = event->window.windowID; bd->MousePendingLeaveFrame = 0; return true; @@ -396,18 +419,23 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) // FIXME: Unconfirmed whether this is still needed with SDL3. case SDL_EVENT_WINDOW_MOUSE_LEAVE: { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) + return false; bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; return true; } case SDL_EVENT_WINDOW_FOCUS_GAINED: - io.AddFocusEvent(true); - return true; case SDL_EVENT_WINDOW_FOCUS_LOST: - io.AddFocusEvent(false); + { + if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) + return false; + io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED); return true; + } case SDL_EVENT_WINDOW_CLOSE_REQUESTED: case SDL_EVENT_WINDOW_MOVED: case SDL_EVENT_WINDOW_RESIZED: + { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)SDL_GetWindowFromID(event->window.windowID))) { if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) @@ -419,6 +447,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) return true; } return true; + } case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: { @@ -431,7 +460,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) { - viewport->PlatformHandle = window; + viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window); viewport->PlatformHandleRaw = nullptr; #if defined(_WIN32) && !defined(__WINRT__) viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); @@ -468,6 +497,7 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) bd->Window = window; + bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6de4295f5..534459bfe 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,8 +47,11 @@ Other changes: payload over an already open tree node would incorrectly select it. (#7850) - MultiSelect+TreeNode: default open behavior is OpenOnDoubleClick + OpenOnArrow when used in a multi-select context without any OpenOnXXX flags set. (#7850) +- TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660) - Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not provide a way to do a portable sleep. (#7844) +- Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut] +- Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*. - Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) diff --git a/examples/example_sdl2_sdlrenderer2/main.cpp b/examples/example_sdl2_sdlrenderer2/main.cpp index 31e6abebd..e922873ad 100644 --- a/examples/example_sdl2_sdlrenderer2/main.cpp +++ b/examples/example_sdl2_sdlrenderer2/main.cpp @@ -47,7 +47,7 @@ int main(int, char**) if (renderer == nullptr) { SDL_Log("Error creating SDL_Renderer!"); - return 0; + return -1; } //SDL_RendererInfo info; //SDL_GetRendererInfo(renderer, &info); diff --git a/imgui.cpp b/imgui.cpp index 17e6324fa..516726098 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6896,15 +6896,28 @@ void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window) return; if (window->Hidden) // If was hidden (previous frame) return; - if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow && window->RootWindow == g.HoveredWindow->RootWindow) - return; - if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow && window->RootWindow == g.NavWindow->RootWindow) - return; + if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow) + if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window)) + return; + if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow) + if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window)) + return; window->DrawList = NULL; window->SkipRefresh = true; } } +static void SetWindowActiveForSkipRefresh(ImGuiWindow* window) +{ + window->Active = true; + for (ImGuiWindow* child : window->DC.ChildWindows) + if (!child->Hidden) + { + child->Active = child->SkipRefresh = true; + SetWindowActiveForSkipRefresh(child); + } +} + // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) // should be positioned behind that modal window, unless the window was created inside the modal begin-stack. // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. @@ -7758,7 +7771,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Skip refresh always mark active if (window->SkipRefresh) - window->Active = true; + SetWindowActiveForSkipRefresh(window); // Append SetCurrentViewport(window, window->Viewport); diff --git a/imgui.h b/imgui.h index 300a189d5..8ed5c08e9 100644 --- a/imgui.h +++ b/imgui.h @@ -3502,7 +3502,7 @@ struct ImFontAtlas struct ImFont { // Members: Hot ~20/24 bytes (for CalcTextSize) - ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI). + ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI). float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5fbfcd892..5bad31453 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -322,23 +322,24 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl static ExampleTreeNode* ExampleTree_CreateDemoTree() { static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" }; - char name_buf[32]; + const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name); + char name_buf[NAME_MAX_LEN]; int uid = 0; ExampleTreeNode* node_L0 = ExampleTree_CreateNode("", ++uid, NULL); const int root_items_multiplier = 2; for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++) { - snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); + snprintf(name_buf, IM_ARRAYSIZE(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); const int number_of_childs = (int)strlen(node_L1->Name); for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) { - snprintf(name_buf, 32, "Child %d", idx_L1); + snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Child %d", idx_L1); ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); node_L2->HasData = true; if (idx_L1 == 0) { - snprintf(name_buf, 32, "Sub-child %d", 0); + snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Sub-child %d", 0); ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); node_L3->HasData = true; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6a5a5483d..634fdd3a6 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2821,8 +2821,9 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) { // Check for valid range. This may also help detect *some* dangling pointers, because a common - // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent. - IM_ASSERT(src_range[0] <= src_range[1]); + // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent, + // or to forget to zero-terminate the glyph range array. + IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?"); src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); } dst_tmp.SrcCount++; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 720258ec9..d31f86f17 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1431,6 +1431,9 @@ bool ImGui::TextLink(const char* label) bool pressed = ButtonBehavior(bb, id, &hovered, &held); RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_None); + if (hovered) + SetMouseCursor(ImGuiMouseCursor_Hand); + ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink]; ImVec4 line_colf = text_colf; { diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 68e38ed95..d97605b25 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -480,8 +480,9 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) { // Check for valid range. This may also help detect *some* dangling pointers, because a common - // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent. - IM_ASSERT(src_range[0] <= src_range[1]); + // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent, + // or to forget to zero-terminate the glyph range array. + IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?"); src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); } dst_tmp.SrcCount++;